Optimistic Locking
Optimistic locking with WATCH detects concurrent modifications before MULTI/EXEC commits — read version, queue updates, abort if another client changed the key.
Introduction
Optimistic locking with WATCH detects concurrent modifications before MULTI/EXEC commits — read version, queue updates, abort if another client changed the key. Inventory systems with low contention prefer retry over blocking locks.
EXEC returning empty multi-bulk means conflict — re-read and retry with backoff. High contention on same key makes optimistic locking thrash; switch to pessimistic SET NX or Lua.
Spring @Version on JPA is the ORM analog; WATCH is the Redis native pattern.
Understanding the topic
Key concepts
- WATCH key — track changes until EXEC.
- Read current state after WATCH.
- MULTI queue writes based on read.
- EXEC fails (nil) if watched key touched.
- Retry loop with exponential backoff.
- UNWATCH or EXEC/DISCARD clears watch.
sequenceDiagramClient->>Redis: WATCH keyClient->>Redis: MULTIClient->>Redis: commandsClient->>Redis: EXEC
Step-by-step explanation
- Client WATCHes keys involved in decision.
- Reads values (GET/HGET) outside MULTI.
- MULTI queues dependent writes.
- EXEC runs only if watched keys unchanged.
- On nil, client retries from WATCH.
Syntax reference
Common commands
- Minimize time between WATCH and EXEC.
- Watch all keys your logic depends on.
- Cap retries to avoid infinite loops.
WATCH stock:sku42qty = GET stock:sku42# if qty >= 1 locally:MULTIDECR stock:sku42EXEC# retry if EXEC returned (nil)
Informative example
Decrement stock only if sufficient — retry on conflict:
# Loop in application until EXEC succeeds or max retriesWATCH inventory:widgetGET inventory:widgetMULTIDECRBY inventory:widget 1EXEC
Production code wraps in while loop with max 3–5 retries. High-contention SKU → use Lua single script instead.
Real-world use
Real-world use cases
- Low-contention inventory decrement.
- Balance update with version check.
- Config update with read-verify-write.
- Counter adjust after read threshold.
- Cart merge conflict resolution.
Best practices
- Short watch window — few commands in MULTI.
- Max retry with jitter backoff.
- Switch to Lua on hot keys.
- Watch minimal key set.
- Log retry rate — high rate signals design issue.
- Don't WATCH across user think time.
Common mistakes
- Infinite retry on perpetual conflict.
- Watching wrong keys — stale commits.
- Using on flash-sale hot SKU — use Lua or queue.
- Assuming EXEC nil is error not conflict signal.
Advanced interview questions
Interview Prep
Practice concise answers, then expand each card for the explanation.
1BeginnerQuestionOptimistic vs pessimistic lock?+
Answer
2BeginnerQuestionEXEC returns nil meaning?+
Answer
3IntermediateQuestionWhen prefer WATCH over SET NX lock?+
Answer
4IntermediateQuestionFlash sale inventory pattern?+
Answer
5AdvancedQuestionImplement optimistic lock in Spring + Redis?+
Answer
Summary
WATCH + MULTI/EXEC = optimistic concurrency.