Distributed locking primitives for Redis. Mutex (exclusive lock) and semaphore (N concurrent leases). Atomic, safe, crash-tolerant.
npm install @prsm/lockExclusive lock. One holder at a time.
import { mutex } from "@prsm/lock"
const lock = mutex({
redis: { host: "127.0.0.1", port: 6379 },
})
const { acquired, id } = await lock.acquire("my-job", { ttl: "30s" })
if (!acquired) return
try {
await doWork()
} finally {
await lock.release("my-job", id)
}redis-{ url?, host?, port?, password? }passed to node-redisprefix- Redis key prefix (default"lock:mutex:")
| Method | Returns | Description |
|---|---|---|
acquire(key, opts?) |
{ acquired, id } |
Attempt to acquire. opts.ttl (default "10s"), opts.id (custom holder ID) |
release(key, id) |
boolean |
Release only if you still own it |
peek(key) |
{ held, holder, ttl } |
Check lock state without acquiring |
close() |
void |
Disconnect Redis |
Up to N concurrent holders.
import { semaphore } from "@prsm/lock"
const sem = semaphore({
max: 20,
ttl: "60s",
redis: { host: "127.0.0.1", port: 6379 },
})
const { acquired, id } = await sem.acquire("worker-slots")
if (!acquired) return
const heartbeat = setInterval(() => sem.renew("worker-slots", id), 15000)
try {
await processTask()
} finally {
clearInterval(heartbeat)
await sem.release("worker-slots", id)
}max- Maximum concurrent holders (required)ttl- Lease lifetime (default"60s"). Expired leases are pruned automaticallyredis- Same as mutexprefix- Redis key prefix (default"lock:sem:")
| Method | Returns | Description |
|---|---|---|
acquire(key, opts?) |
{ acquired, id } |
Acquire a lease slot. opts.id for custom lease ID |
release(key, id) |
true |
Release a lease |
renew(key, id) |
boolean |
Extend lease lifetime. false if expired |
count(key) |
number |
Active lease count (after pruning) |
peek(key) |
{ active, max, available, holders } |
Full semaphore state |
close() |
void |
Disconnect Redis |
Mutex uses SET key value NX PX ttl for atomic acquire and a Lua script for ownership-checked release (only the holder can release).
Semaphore uses a Redis sorted set where each member is a lease ID and the score is a timestamp. Expired entries are pruned via ZREMRANGEBYSCORE before each acquire. Renewal updates the timestamp to extend the lease window.
Both are crash-tolerant via TTLs. If a holder dies without releasing, the lock/lease expires automatically.