How do you implement a distributed lock with Redis?
Build a safe distributed lock in Redis using SET NX PX, unique tokens, and Lua compare-and-delete release, plus watchdogs and the Redlock algorithm.
Expected Interview Answer
You implement a distributed lock in Redis by atomically setting a key only if it does not exist, with a unique random token as the value and an expiry: SET lock:name token NX PX 30000. The holder releases it with a Lua script that deletes the key only if the token still matches, ensuring you never delete someone else's lock.
The NX flag guarantees only one client acquires the lock, and PX sets an automatic expiry so a crashed holder does not deadlock the resource forever. The unique token is essential: releasing must be a compare-and-delete done atomically in a Lua script, otherwise a client whose lock already expired could delete a lock now held by another client. For long tasks the holder can periodically extend (watchdog) the TTL. For stronger guarantees across independent Redis nodes, the Redlock algorithm acquires the lock on a majority of nodes, though it has known trade-offs debated for correctness under clock skew and pauses.
- Prevents two processes from mutating a shared resource at once
- Auto-expiry avoids permanent deadlock if a holder crashes
- Unique token makes release safe under expiry races
- Simple single-instance version is fast and easy to reason about
- Redlock extends the idea across independent nodes
AI Mentor Explanation
A distributed lock is the single bat that must be held to be on strike. Only one batter can hold it (NX), and if they walk off injured the umpire reclaims it after a timeout (PX) so play never stalls. The bat is engraved with the batter's name (the token), so the pavilion never hands it to the wrong player or takes it back from someone who legitimately picked up a new one.
Step-by-Step Explanation
Step 1
Generate a unique token
Create a random value (UUID) per acquisition so the lock can be identified as yours during release.
Step 2
Acquire atomically
Run SET lock:name token NX PX 30000 — NX ensures only one winner and PX sets an auto-expiry.
Step 3
Do the protected work
Only the client that won the SET proceeds to mutate the shared resource.
Step 4
Extend if needed
For long tasks, a watchdog periodically extends the TTL so the lock does not expire mid-work.
Step 5
Release safely
Use a Lua compare-and-delete so you remove the key only if the stored token still matches yours.
What Interviewer Expects
- Use of SET NX PX for atomic acquisition with expiry
- A unique token to identify ownership
- Atomic compare-and-delete on release via Lua
- Understanding why a bare DEL on release is unsafe
- Awareness of Redlock and its correctness debate
Common Mistakes
- Using SETNX then EXPIRE as two commands instead of one atomic SET
- Releasing with a plain DEL, risking deletion of another client's lock
- Omitting a TTL, so a crashed holder deadlocks the resource
- Setting a TTL too short for the protected work to complete
- Assuming a single-instance lock is safe during a Redis failover
Best Answer (HR Friendly)
“A distributed lock lets only one server at a time work on a shared resource. In Redis you set a special key that only succeeds if no one else holds it, give it an automatic expiry so a crash cannot freeze everything, and tag it with a unique code so only the true owner can release it.”
Code Example
const crypto = require('crypto');
async function withLock(redis, name, ttlMs, fn) {
const key = `lock:${name}`;
const token = crypto.randomUUID();
// Atomic acquire: only one client wins, auto-expires to avoid deadlock
const acquired = await redis.set(key, token, 'NX', 'PX', ttlMs);
if (!acquired) throw new Error('Could not acquire lock');
try {
return await fn();
} finally {
// Compare-and-delete: release only if we still own the lock
const lua = `
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end`;
await redis.eval(lua, 1, key, token);
}
}Follow-up Questions
- Why must release be a Lua compare-and-delete instead of a plain DEL?
- What is the Redlock algorithm and what are its criticisms?
- How does a watchdog extend a lock for long-running work?
- What goes wrong if the TTL expires before the work finishes?
- How does Redis failover affect single-instance lock safety?
MCQ Practice
1. Which command atomically acquires a Redis lock with an expiry?
SET with NX and PX acquires the lock and sets its expiry in a single atomic operation, avoiding the two-step race.
2. Why release a lock with a Lua compare-and-delete instead of DEL?
If your lock already expired and another client acquired it, a bare DEL would wrongly release their lock; the token check prevents this.
3. Why include an expiry (PX) on the lock key?
The auto-expiry guarantees the lock is eventually released even if the holder crashes without cleaning up.
Flash Cards
Command to acquire a Redis lock? — SET lock:name token NX PX <ttl> — NX ensures one winner, PX sets an auto-expiry to prevent deadlock.
Why a unique token per lock? — So release can verify ownership and never delete a lock acquired by a different client.
How to release safely? — A Lua script that deletes the key only if its stored value equals your token (atomic compare-and-delete).
What is a watchdog? — A background process that periodically extends the lock's TTL so long-running work does not lose the lock.
What is Redlock? — An algorithm acquiring the lock on a majority of independent Redis nodes for stronger guarantees, though its correctness is debated.
Continue Learning
Related Interview Questions
How would you implement distributed rate limiting with Redis, and which algorithm would you choose?
hard
What is a cache stampede (thundering herd) and how do you prevent it in Redis?
hard
What are Redis Functions, how do they differ from EVAL scripts, and what are the rules for writing them safely?
hard
What is the difference between SET with NX and a plain SET in Redis?
easy