Initial Commit
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Redis-backed distributed lock provider using atomic `SET NX` operations.
|
||||
*
|
||||
* Implements the `LockProvider` interface with owner-based release semantics:
|
||||
* a lock is only released if the requesting instance owns it, preventing
|
||||
* accidental release of another process's lock after TTL expiry.
|
||||
*
|
||||
* @module redis-lock-provider
|
||||
*/
|
||||
|
||||
import type { RedisClientAdapter } from "../store/adapter.types.js";
|
||||
import type { LockProvider } from "../types/idempotency.types.js";
|
||||
|
||||
/** Configuration for `RedisLockProvider`. */
|
||||
export interface RedisLockProviderConfig {
|
||||
/** Redis client adapter. */
|
||||
readonly client: RedisClientAdapter;
|
||||
/** Key prefix for lock keys. Default: `'lock:'`. */
|
||||
readonly prefix?: string;
|
||||
/** Unique identifier for this lock owner (e.g. worker ID). */
|
||||
readonly ownerId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redis-backed distributed lock using `SET NX` with TTL.
|
||||
*
|
||||
* Owner-verified release ensures that only the process that acquired the
|
||||
* lock can release it — safe for use in distributed multi-process deployments.
|
||||
*/
|
||||
export class RedisLockProvider implements LockProvider {
|
||||
protected readonly client: RedisClientAdapter;
|
||||
protected readonly prefix: string;
|
||||
protected readonly ownerId: string;
|
||||
|
||||
constructor(config: RedisLockProviderConfig) {
|
||||
this.client = config.client;
|
||||
this.prefix = config.prefix ?? "lock:";
|
||||
this.ownerId =
|
||||
config.ownerId ??
|
||||
`owner-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to acquire the lock for `resource` with the given TTL.
|
||||
* Returns `true` if the lock was acquired, `false` if already held.
|
||||
*/
|
||||
async acquire(resource: string, ttlMs: number): Promise<boolean> {
|
||||
const key = `${this.prefix}${resource}`;
|
||||
return this.client.setNx(key, this.ownerId, ttlMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the lock for `resource` if this instance owns it.
|
||||
* No-ops if the lock is not held or was taken by another owner.
|
||||
*/
|
||||
async release(resource: string): Promise<void> {
|
||||
const key = `${this.prefix}${resource}`;
|
||||
const value = await this.client.get(key);
|
||||
if (value === this.ownerId) {
|
||||
await this.client.del(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user