Distributed Locking Service
ZooKeeper / etcd
Design a lock service that many teams use to make sure only one process at a time works on a job, a file or a leader role, which keeps working when machines crash or the network splits.
Overview
A distributed lock lets processes on different machines agree that only one of them does something at a time: runs tonight's billing job, writes a shared file, or acts as the leader of a service. Inside one process a mutex does this; across machines it is harder because any participant can crash, pause or lose the network at any moment, and nobody can tell a dead client from a slow one.
The design splits into two parts. A small lock service, replicated with a consensus protocol, decides who holds each lock and survives server failures. Sessions with leases free the locks of clients that stop responding. Then comes the honest part: a lease can expire while its holder is paused and still believes it holds the lock, so a lock that protects data must hand out fencing tokens that the protected resource checks. Why that is needed and how resources check tokens is covered in the Fencing topic; here we build the service that issues them.
Interviewers use this problem to test whether you know the difference between a lock that saves duplicate work and one that protects correctness, why a single Redis key or a failover pair is not enough for the second kind, and how systems such as ZooKeeper and etcd implement locks.
Questions to ask first
- What do the locks protect? — Both kinds: saving duplicate work, and data that must not be corrupted
- How many clients and locks? — 50,000 client sessions; about 100,000 locks held at once
- How often are locks taken? — 5,000 grants a second on average, 15,000 at peak
- How fast must a dead client's lock free up? — Within about 10 seconds
- Fair order for waiters? — Yes, first come, first served
- Across regions? — No: one region, three zones
- Latency to take a free lock? — A few milliseconds inside the region (a target we chose)
What you will be able to do
- Say which locks need consensus and fencing, and which do not.
- Build lock grants on a replicated log, with the log index as the fencing token.
- Size sessions, leases and keepalives, and say what happens in a leader failover.
- Queue waiters fairly without waking every client on each release.
- Compare a database row, a Redis key, Redlock and a ZooKeeper or etcd lock.
Key concepts
- Consensus (Raft, ZAB)
- Quorum of 3 out of 5
- Sessions and leases
- Keepalives
- Fencing tokens from the log
- FIFO waiters
- Watch the predecessor
- Redlock trade-offs
- Efficiency vs correctness locks
Functional requirements
- Open a session with a lease; keep it alive with keepalives
- Acquire a named lock, waiting up to a timeout; get a fencing token with each grant
- Release a lock; only the holder can
- Release every lock of a session whose lease runs out
- Grant a lock to waiters in the order they asked
- Show who holds a lock and who is waiting
Non-functional requirements
- Safety first: at most one session holds a lock at a time, as seen by the service
- Tokens only grow, so a resource can reject an older holder
- Keeps granting locks with up to 2 of 5 nodes down
- A dead client's locks free up within about 10 seconds (the lease TTL we chose)
- Taking a free lock costs a few milliseconds inside a region (a target we chose)
Interview questions
- Why not just use SET NX with a TTL in Redis?
- Does Redlock fix that?
- How does the service free the lock of a client that crashed?
- What happens when the lock service leader fails?
- How do you implement fair locking without a thundering herd?
- How would you do leader election with this service?
- The lock service is the bottleneck. How do you scale it?
Sign up free to read the full topic
More System Design topics
Start free on Cariara · Coding problems · System design studio