Locks Plugin
Distributed locking solution for coordinating access to shared resources across multiple application instances.
Overview
The Locks Plugin provides Redis-based distributed locking with automatic renewal, ownership validation, and configurable retry strategies. Useful for preventing race conditions in distributed systems.
| Challenge | Without Locks | With Locks Plugin |
|---|---|---|
| Double Payment | Multiple instances process same payment | Single instance processes, others skip |
| Race Conditions | Concurrent updates corrupt data | Sequential, atomic updates |
| Job Duplication | Multiple workers execute same job | One worker per job |
| Inventory Oversell | 10 items available, 15 sold | Inventory tracking |
Key Features
- Redis-Based Locking — Atomic lock acquisition using Lua scripts
- Automatic Renewal — Configurable TTL extension for long-running operations
- Ownership Validation — Unique tokens prevent accidental release by other processes
- Retry Strategies — Exponential backoff with configurable multiplier and delay caps
- Graceful Shutdown — Automatic lock release on application termination
- Decorator API —
@WithLockdecorator for declarative locking
Installation
bash
npm install @nestjs-redisx/core @nestjs-redisx/locks ioredisbash
npm install @nestjs-redisx/core @nestjs-redisx/locks redisBasic Configuration
typescript
import { Module } from '@nestjs/common';
import { RedisModule } from '@nestjs-redisx/core';
import { LocksPlugin } from '@nestjs-redisx/locks';
@Module({
imports: [
RedisModule.forRoot({
clients: {
host: 'localhost',
port: 6379,
},
plugins: [
new LocksPlugin({
defaultTtl: 30000,
keyPrefix: '_lock:',
autoRenew: {
enabled: true,
intervalFraction: 0.5,
},
}),
],
}),
],
})
export class AppModule {}Usage with Decorator
typescript
import { Injectable } from '@nestjs/common';
import { WithLock } from '@nestjs-redisx/locks';
import { Payment, PaymentAlreadyProcessedError, OrderRepository, PaymentGateway } from './types';
@Injectable()
export class PaymentService {
constructor(
private readonly orderRepository: OrderRepository,
private readonly paymentGateway: PaymentGateway,
) {}
@WithLock({
key: 'payment:{0}',
ttl: 10000,
})
async processPayment(orderId: string): Promise<Payment> {
// Only one instance can process this order at a time
const order = await this.orderRepository.findById(orderId);
if (order.status === 'paid') {
throw new PaymentAlreadyProcessedError(orderId);
}
const result = await this.paymentGateway.charge(order);
await this.orderRepository.update(orderId, { status: 'paid' });
return result;
}
}Usage with Service API
typescript
import { Injectable, Inject } from '@nestjs/common';
import { LOCK_SERVICE, ILockService } from '@nestjs-redisx/locks';
import { InventoryStore } from './types';
@Injectable()
export class InventoryService {
constructor(
@Inject(LOCK_SERVICE) private readonly lockService: ILockService,
private readonly inventory: InventoryStore,
) {}
async reserveItem(itemId: string, quantity: number): Promise<boolean> {
return this.lockService.withLock(
`inventory:${itemId}`,
async () => {
const stock = await this.inventory.getStock(itemId);
if (stock < quantity) {
return false;
}
await this.inventory.decrement(itemId, quantity);
return true;
},
{ ttl: 5000 },
);
}
}Lock Flow
Common Use Cases
| Scenario | Implementation |
|---|---|
| Payment Processing | Lock per order ID to prevent double charges |
| Inventory Management | Lock per SKU during stock updates |
| Job Processing | Lock per job ID for at-most-once execution |
| Leader Election | Single lock for scheduler or cron jobs |
| Sequential Operations | Lock per resource for ordered operations |
Documentation
| Topic | Description |
|---|---|
| Core Concepts | Understanding distributed locking |
| Configuration | Configuration reference |
| @WithLock Decorator | Declarative locking |
| Service API | Programmatic lock operations |
| Auto-Renewal | TTL extension for long operations |
| Retry Strategies | Handling lock contention |
| Patterns | Common locking patterns |
| Monitoring | Metrics and observability |
| Testing | Testing locked services |
| Recipes | Implementation examples |
| Troubleshooting | Debugging common issues |