Overview
src/orchestration/locks/distributed-lock.service.ts acquireLock() may use a non-atomic check-then-set pattern. If acquisition uses separate GET + conditional SET commands, two callers can both read "no lock" and both proceed to set, creating a race condition. Lock acquisition must use Redis SET NX PX in a single atomic command.
Specifications
Features:
- Lock acquisition must use a single atomic
SET key value NX PX ttl command.
- Return
false immediately if the lock is already held (non-blocking) or implement configurable retry with timeout.
Tasks:
- Replace any multi-command acquisition with
redis.set(key, token, 'PX', ttlMs, 'NX').
- Return the unique token if acquired,
null if not.
- Add a
withLock(key, ttl, fn) helper that acquires, runs fn, and always releases.
- Add load tests proving at most one caller holds the lock at any moment.
Impacted Files:
src/orchestration/locks/distributed-lock.service.ts
Acceptance Criteria
- Under 100 concurrent
acquireLock calls, exactly one caller succeeds.
SET NX PX is used as a single Redis command (verified via spy in unit tests).
withLock helper releases the lock even if fn throws.
Overview
src/orchestration/locks/distributed-lock.service.tsacquireLock()may use a non-atomic check-then-set pattern. If acquisition uses separateGET+ conditionalSETcommands, two callers can both read "no lock" and both proceed to set, creating a race condition. Lock acquisition must use RedisSET NX PXin a single atomic command.Specifications
Features:
SET key value NX PX ttlcommand.falseimmediately if the lock is already held (non-blocking) or implement configurable retry with timeout.Tasks:
redis.set(key, token, 'PX', ttlMs, 'NX').nullif not.withLock(key, ttl, fn)helper that acquires, runsfn, and always releases.Impacted Files:
src/orchestration/locks/distributed-lock.service.tsAcceptance Criteria
acquireLockcalls, exactly one caller succeeds.SET NX PXis used as a single Redis command (verified via spy in unit tests).withLockhelper releases the lock even iffnthrows.