Distributed Lock decorator for NestJS cron jobs using Redis - prevents duplicate execution across replicas
npm install dpa-distributed-lock-cron

Distributed Lock decorator for NestJS cron jobs using Redis. Prevents duplicate cron job execution in multi-replica environments (Docker Swarm, Kubernetes, PM2 cluster).
- š Atomic Locking - Uses Redis SET NX PX for race-condition-free locking
- šÆ Simple Decorator - Just add @DistributedLock() to your cron method
- š Auto Release - Lock is automatically released after method execution
- š”ļø Fail-Open - Allows execution if Redis is unavailable (configurable)
- š¦ Zero Config - Works out of the box with environment variables
``bash`
npm install dpa-distributed-lock-cron ioredis
`typescript
// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { DistributedLockModule, distributedLockConfig } from 'dpa-distributed-lock-cron';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [distributedLockConfig],
}),
DistributedLockModule,
],
})
export class AppModule {}
`
`bash`
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD= # optional
REDIS_DB=0 # optional, default: 0
REDIS_KEY_PREFIX= # optional, e.g., "myapp:"
`typescript
import { Injectable } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { DistributedLock } from 'dpa-distributed-lock-cron';
@Injectable()
export class MyService {
@Cron(CronExpression.EVERY_DAY_AT_6AM)
@DistributedLock('cron:daily-sync', 300000) // key, TTL 5 minutes
async dailySync(): Promise
// ā
Only ONE replica will execute this
console.log('Running daily sync...');
}
}
`
Decorator to wrap your cron method with distributed locking.
| Parameter | Type | Description |
|-----------|------|-------------|
| keyName | string | Unique lock key (e.g., 'cron:my-job') |ttlMs
| | number | Lock TTL in milliseconds (safety timeout) |
Injectable service for manual lock management.
`typescript
import { DistributedLockService } from 'dpa-distributed-lock-cron';
@Injectable()
export class MyService {
constructor(private readonly lockService: DistributedLockService) {}
async manualLockExample() {
const acquired = await this.lockService.acquireLock('my-lock', 60000);
if (acquired) {
try {
// do work
} finally {
await this.lockService.releaseLock('my-lock');
}
}
}
}
`
| Method | Returns | Description |
|--------|---------|-------------|
| acquireLock(key, ttlMs) | Promise | Acquire lock, returns true if acquired |releaseLock(key)
| | Promise | Release lock (only if owned) |isConnected()
| | boolean | Check Redis connection status |
``
āāāāāāāāāāāāāāā āāāāāāāāāāāāāāā āāāāāāāāāāāāāāā
ā Replica 1 ā ā Replica 2 ā ā Replica 3 ā
āāāāāāāā¬āāāāāāā āāāāāāāā¬āāāāāāā āāāāāāāā¬āāāāāāā
ā ā ā
ā SET lock NX PX ā SET lock NX PX ā SET lock NX PX
ā ā ā
ā¼ ā¼ ā¼
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā REDIS ā
ā lock:cron:daily-sync = "replica-1-uuid" ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ā ā
ā¼ ā¼ ā¼
ā
Execute ā Skip ā Skip
1. Before method execution, decorator tries to acquire lock using Redis SET key NX PX ttlOK
2. If lock acquired (): method executes, then lock is releasednull`): method is skipped silently
3. If lock not acquired (
4. Lock uses Lua script for safe release (only the owner can release)
If Redis is unavailable, the decorator allows execution (fail-open) to prevent blocking all replicas. This ensures your cron jobs continue to run even during Redis outages.
- Node.js >= 18.0.0
- NestJS >= 10.0.0
- Redis >= 6.0
Contributions are welcome! Please feel free to submit a Pull Request.
MIT Ā© DPA Team