Imported from bedkillerspacex-boop/codex-skill-library (
typescript-distributed-lock/SKILL.md). Install upstream withnpx skills add bedkillerspacex-boop/codex-skill-library --skill typescript-distributed-lock. Copyright stays with the author.
TypeScript Distributed Lock
Scope And Authorization
- In scope: Adding mutual exclusion for jobs and critical sections in systems you run.
- Out of scope: Locking out other tenants on shared unmanaged infrastructure; using locks as a substitute for correct transaction design when not appropriate.
- Prefer TTLs, fencing, and idempotency; locks are fallible.
- Pair with
typescript-cron-safety,code-quality-standards.
When To Use
- Exactly-one leader tasks across replicas.
- Preventing double-billing / double-send side effects.
- Serializing migrations or compaction jobs.
- Coordinating multi-step workflows with leases.
- Replacing unsafe "assume single replica" cron.
Do Not Use As Primary
| Need | Skill instead |
|---|---|
| Cron scheduling patterns | typescript-cron-safety |
| CQRS/outbox design | typescript-cqrs-notes |
| DB transactions only | data-model / DB skills |
| Redis security hardening | redis-security-misconfig |
| Implementation quality baseline | code-quality-standards |
Domain Focus
| Area | Guidance |
|---|---|
| Topic | Lease locks, TTL, renew, fence, idempotency |
| Tooling | Redis, Redlock caveats, PG pg_advisory_lock, ZooKeeper/etcd optional |
| Verify | Dual workers cannot both commit side effects |
| Pitfalls | Lock without TTL; ignore clock skew; Redlock overconfidence |
Primitives
| Primitive | Notes |
|---|---|
| SET key token NX EX ttl | Simple Redis lock with unique token |
| Compare-and-del | Lua/release only if token matches |
| Fencing token | Monotonic token checked by resource |
| Advisory lock | Session/transaction scoped in PG |
| Idempotency key | Still required for at-least-once |
Workflow
1. Confirm scope and success criteria
- Record resource protected, max critical section time, replica count, failure modes.
- Success: multi-instance test shows single winner; lock expires on crash.
- Prefer DB constraints for money over locks alone.
2. Choose backend
| Backend | When |
|---|---|
| Postgres advisory | Already on PG; transactional work |
| Redis | Fast leases for jobs; accept ops care |
| etcd/ZK | Stronger coordination needs |
3. Implement Redis-style lock (sketch)
export type RedisLike = {
set: (k: string, v: string, mode: "EX", ttl: number, flag: "NX") => Promise<"OK" | null>;
eval: (script: string, keys: string[], args: string[]) => Promise<unknown>;
};
const releaseScript = `
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end`;
export async function acquire(
r: RedisLike,
key: string,
token: string,
ttlSec: number,
): Promise<boolean> {
const res = await r.set(key, token, "EX", ttlSec, "NX");
return res === "OK";
}
export async function release(r: RedisLike, key: string, token: string): Promise<void> {
await r.eval(releaseScript, [key], [token]);
}
Renewal: extend TTL only if token matches; stop work if renew fails.
4. Fencing
export function assertFence(resourceFence: number, lockFence: number): void {
if (lockFence < resourceFence) throw new Error("stale_lock_fence");
}
Resource stores last fence; reject lower tokens after write.
5. Testing
pnpm vitest run tests/distributed-lock
# Start 5 workers; assert sideEffectCounter increments once per period
6. Verify ops
| Check | Pass |
|---|---|
| Crash | Lock frees after TTL |
| Release | Wrong token cannot unlock |
| Clock | TTL margins documented |
| Metrics | acquire_fail, hold_time |
7. Hand off
- Document lock key namespace and TTLs.
- Alert on prolonged hold times.
- Prefer idempotent consumers even with locks.
Good / Bad
| Topic | Good | Bad |
|---|---|---|
| TTL | Always | Immortal locks |
| Release | Token-checked | DEL by key only |
| Correctness | Fence + idempotency | Lock as only payment safety |
| Redlock | Understand limits | Treat as perfect CAP magic |
| Tests | Multi-process | Single-thread unit only |
| Scope | Own Redis/PG | Lock keys on shared SaaS without isolation |
Output Checklist
- Resource and TTL design documented
- Backend chosen
- Acquire/release/renew implemented safely
- Fencing or equivalent for critical writes
- Multi-instance tests pass
- Metrics/alerts
- Idempotency paired
-
code-quality-standardsapplied - Residual risk noted
Rules
- Locks fail; design for timeout and double-delivery.
- Never lock without TTL unless session-scoped DB lock.
- Unique tokens on release.
- Authorized datastores only.
- Cron skill covers scheduling composition.