SD System Design In Depth
0%
lesson system-designcoordination

Distributed locks and leases

Module 3.2: Distributed Coordination, Consensus & Leases Updated Sep 17, 2026
📄 Cheat Sheet
21: Distributed Locking | Systems Design Interview Questions With Ex-Google SWE
Jordan has no life 21: Distributed Locking | Systems Design Interview Questions With Ex-Google SWE Watch on YouTube ↗

Jordan covers lock services, leases, the process-pause problem and fencing tokens, the full argument of this lesson, with interview framing.

0:00 / --:--

Concrete scenario

A worker pauses beyond its lease, then resumes after another worker has acquired the same lock.

Mechanics

A lease limits how long the coordinator recognizes ownership, but the old client cannot infer validity from its local clock. For external writes, use a monotonically increasing fencing token that the target rejects when stale, or an equivalent conditional write/version check enforced by the resource.

Failure mode

A lock service can expire ownership while paused code continues. If the target accepts both writers without fencing, mutual exclusion at the coordinator does not protect the data.

Trade-off

Fencing is powerful when the target can validate it, but it is not the only correctness design: transactions, compare-and-swap, idempotency, or single-owner logs may fit. Locks add availability and recovery dependencies.

Worked example: the paused lock holder

sequenceDiagram participant W1 as Worker1 participant L as LockService participant W2 as Worker2 participant S as Storage W1->>L: acquire lock, get token 33 with a 10s lease Note over W1: GC pause of 15 seconds L->>L: lease for token 33 expires W2->>L: acquire lock, get token 34 W2->>S: write with token 34 S-->>W2: ok, highest token seen is now 34 W1->>S: write with token 33 after waking S-->>W1: rejected because 33 is less than 34

Without the token check at storage, both writes succeed and W1 overwrites W2's newer data. Note that W1 did nothing wrong by its own reckoning: it cannot observe its own pause.

Lease maths

With a lease TTL T, a maximum clock drift rate d, and a holder that renews every T/3:

  • The holder should stop working at about T x (1 - d) minus a safety margin, measured with a monotonic clock from when it sent the acquire request, not when it received the reply.
  • For T = 10 s the holder might stop at 8 s. But a 15-second pause makes any client-side check meaningless, which is why the check belongs at the resource.

Ways to get mutual exclusion

MechanismSafe against pauses?Needs resource supportNotes
Lock plus fencing tokenYesYes: stores the maximum token and rejects lower onesZooKeeper zxid or etcd revision make good tokens
Compare-and-set or version columnYesYes, but most databases already have itOften removes the need for a lock
Database row lock (SELECT FOR UPDATE)Yes, within a transactionSame database onlyHolds a connection
Single partition owner (Kafka consumer group)Mostly, but zombies need fencingProducer epochs fence zombiesOwnership via rebalance
Redis SET NX PX without a token checkNoNoFine for efficiency and deduplication of work

How real systems do it

  • Chubby issues sequencers that servers can check. It also delays releasing a crashed holder's lock (the lock-delay, up to a minute) to reduce stale writes from services that ignore sequencers.
  • Kubernetes leader election uses Lease objects with a resourceVersion compare-and-swap, a 15-second lease duration, and a 10-second renew deadline by default.
  • HDFS NameNode HA uses fencing (SSH kill or shared-storage fencing) plus QJM epoch numbers so that an old active node cannot write to the edit log.
Check yourself Question 1 of 3
Why check at the resource recall
From: Mechanics

Why can a lock holder not simply check its lease locally before each write to guarantee mutual exclusion?

Key Takeaways

A paused client can continue after its lease expires.
Targets can reject stale fencing tokens or enforce conditional versions.
Transactions, idempotency, or single-owner logs may be better than locks.
Was this lesson helpful? Spot an issue or have a suggestion? Let us know on GitHub.
Report an issue