node-redlock
v2.3.0
Published
A distributed locking algorithm used to manage distributed resources in a system.
Maintainers
Readme
Redlock Implementation in TypeScript
This library provides a robust and distributed locking mechanism using the Redlock algorithm implemented in TypeScript. It allows you to acquire, release, and renew locks on shared resources across multiple Redis instances.
Features
- Acquire a distributed lock with configurable retry logic.
- Release a lock safely to ensure resource consistency.
- Renew locks to extend the time-to-live (TTL) for a lock.
- Support for custom retry strategies.
- Compatibility with multiple Redis clients for high availability.
Installation
- Clone the repository or copy the
Redlock.tsfile into your project. - Install the required dependencies using npm or yarn:
npm install ioredisUsage
Setup
Create Redis clients that implement the RedisClient interface and pass them to the Redlock class. For example, using ioredis:
import Redis from 'ioredis';
import { Redlock } from './Redlock';
const redisClients = [
new Redis({ host: '127.0.0.1', port: 6379 }),
new Redis({ host: '127.0.0.1', port: 6380 }),
new Redis({ host: '127.0.0.1', port: 6381 }),
];
const redlock = new Redlock(redisClients);Acquiring a Lock
You can acquire a lock on a resource with a specific TTL:
(async () => {
const lock = await redlock.acquireLock('my-resource', 5000); // Lock for 5000ms
if (lock) {
console.log('Lock acquired:', lock);
} else {
console.log('Failed to acquire lock');
}
})();Acquiring a Lock with Custom Retry Strategy
To customize the retry strategy, pass a function to acquireLockWithCustomRetry:
(async () => {
const retryStrategy = (attempt: number) => 100 * (attempt + 1); // Exponential backoff
const lock = await redlock.acquireLockWithCustomRetry('my-resource', 5000, retryStrategy);
if (lock) {
console.log('Lock acquired with custom retry:', lock);
} else {
console.log('Failed to acquire lock with custom retry');
}
})();Releasing a Lock
To release a lock after use:
(async () => {
const lock = await redlock.acquireLock('my-resource', 5000);
if (lock) {
await redlock.releaseLock(lock.resource, lock.value);
console.log('Lock released');
}
})();Renewing a Lock
Extend the TTL of an existing lock:
(async () => {
const lock = await redlock.acquireLock('my-resource', 5000);
if (lock) {
const renewed = await redlock.renewLock(lock.resource, lock.value, 5000);
if (renewed) {
console.log('Lock renewed');
} else {
console.log('Failed to renew lock');
}
}
})();API Reference
acquireLock(resource: string, ttl: number): Promise<Lock | null>
Attempts to acquire a lock on the specified resource.
Parameters:
resource: The resource to lock.ttl: The time-to-live for the lock in milliseconds.
Returns: A Lock object if successful, otherwise null.
acquireLockWithCustomRetry(resource: string, ttl: number, retryStrategy: (attempt: number) => number): Promise<Lock | null>
Attempts to acquire a lock using a custom retry strategy.
Parameters:
resource: The resource to lock.ttl: The time-to-live for the lock in milliseconds.retryStrategy: A function that determines the delay before the next retry attempt.
Returns: A Lock object if successful, otherwise null.
releaseLock(resource: string, value: string): Promise<void>
Releases a lock on the specified resource.
Parameters:
resource: The resource to unlock.value: The unique lock value.
Returns: A Promise that resolves when the lock is released.
renewLock(resource: string, value: string, ttl: number): Promise<boolean>
Renews the TTL for an existing lock.
Parameters:
resource: The resource to renew the lock on.value: The unique lock value.ttl: The new TTL in milliseconds.
Returns: true if the lock was successfully renewed, otherwise false.
Using the using Pattern (Recommended)
The using method simplifies lock management by automatically acquiring and releasing the lock. It also supports automatic extension.
await redlock.using('my-resource', 5000, async (lock) => {
// Critical section
console.log('Lock acquired:', lock);
await doWork();
}, { autoExtend: true }); // Optional: Automatically extend lock while workingEvents
Redlock extends EventEmitter and emits the following events:
acquired: When a lock is successfully acquired.released: When a lock is released.renewed: When a lock is renewed.clientError: When a Redis client error occurs.error: When a background error occurs (e.g., auto-extension failure).
redlock.on('acquired', (lock) => {
console.log('Lock acquired:', lock);
});
redlock.on('clientError', (err) => {
console.error('Redis client error:', err);
});Notes
- Secure IDs: Lock values are now generated using
crypto.randomUUID()for better security. - Ensure all Redis clients are connected and synchronized for optimal performance.
- Use a unique
valuefor each lock to prevent accidental unlocking by other processes.
License
This project is licensed under the MIT License. See the LICENSE file for details.
