Where I Use Locks
I use Redis locks for small critical sections: claiming a batch, updating shared progress, preventing duplicate chunk finalization, or protecting a resource that cannot safely be modified concurrently.
Rules I Follow
- Lock keys are scoped narrowly, such as
batch:{batchId}:finalize. - Every lock has an expiry so a dead worker cannot block the system forever.
- Every lock has an owner token, and only the owner can release it.
- The locked section does as little work as possible.
- Durable state still lives in the database; Redis coordinates access but is not the source of truth.
The lock is not the design.
The real design is the state machine underneath it. The lock only protects transitions that would be unsafe under concurrent execution.
Failure Scenarios
- If a worker dies, expiry releases the lock and another worker can recover.
- If a lock expires too early, idempotency and database state checks prevent duplicate completion.
- If Redis is unavailable, the worker should fail safely instead of processing an unsafe critical section.
Trade-off
Locks reduce concurrency bugs, but they add operational complexity. I prefer database constraints or idempotent updates first, then add locks only around the small parts that truly need mutual exclusion.