ReviseAlgo Logo

Advanced Production Case Studies

Distributed Locking

Coordinating resource access across multiple independent servers to prevent race conditions.

In short

Coordinating resource access across multiple independent servers to prevent race conditions.

Last Updated: June 26, 2026 28 min read

In a single-process application, threads coordinate access to shared resources using simple memory constructs like mutexes or semaphores. However, when an application is distributed across hundreds of independent server instances, local memory locks are useless. If multiple servers attempt to charge a user's credit card or claim an items stock concurrently, double charges and race conditions occur. Distributed Locking provides a cluster-wide coordination boundary, ensuring that only one node can modify a specific resource at any given instant.

1. Learning Objectives

  • Understand the difference between database locks, distributed locks, and local thread locks.
  • Explain the Redlock algorithm mechanics and its resilience assumptions.
  • Implement lock lease safety features: Time-to-Live (TTL) and automatic renewal watchdogs.
  • Detail how to prevent split-brain and clock skew failures in locking datastores.
  • Write a thread-safe Distributed Lock Manager simulator with auto-renewal in Java, Python, and C++.

2. Prerequisites

Before learning about distributed locks, ensure you understand:

  • Race Conditions: How concurrent writes cause data corruption.
  • Redis Basics: Understanding keys, values, and TTL commands.
  • Consensus (ZooKeeper/Raft): General knowledge of leader election.

3. Why This Topic Matters

Without cluster-wide locking, scaling out compute tiers introduces data integrity hazards.

For example, during a flash sale, if Node 1 and Node 2 simultaneously verify stock for the last ticket, both read "1 ticket available". Both proceed to deduct stock, resulting in double-selling. Distributed locking restricts access: Node 1 acquires the lock lock:ticket_102 first, forcing Node 2 to wait until Node 1 commits its write, ensuring consistency.

4. Real-world Analogy

Think of a shared conference room in a co-working space:

  • No coordination (Race condition): Anyone can walk in at any time. Two teams walk in at 2 PM, arguing over who gets to use the whiteboard.
  • Distributed Lock (Booking Key): There is a centralized digital calendar. To use the room, a team must log in and book the slot. The booking has a duration limit (TTL) of 1 hour. If the team needs more time, they must extend the booking before it expires. If the team leaves early, they delete the booking (unlock). If the team's laptop crashes and they leave without cleaning up, the system frees the room automatically after 1 hour (lease expiry), preventing permanent deadlock.

5. Core Concepts

  • Mutual Exclusion (Mutex): Only one node can hold the lock at any given time.
  • Lease Time-to-Live (TTL): A safety expiration time attached to a lock. If the worker crashes, the lock expires naturally, preventing deadlocks.
  • Watchdog Renewal: A background thread that periodically extends the lock lease time while the main worker thread is still actively running.
  • Fencing Token: An auto-incrementing ID generated by the lock manager. Databases check the token; if a late worker tries to write with a stale token, the database rejects the write, resolving network partition delays.

6. Visualization

Distributed Lock Lease and Watchdog Sequence

7. How It Works: Lock Lifecycle

  1. Acquisition: Worker calls Redis: SET lock_key worker_UUID NX PX 10000. NX creates the key only if it does not exist; PX 10000 sets a 10-second TTL.
  2. Lease Tracking: The worker launches a background watchdog thread. Every 3 seconds, the watchdog resets the TTL back to 10 seconds.
  3. Critical Section execution: The worker processes the shared resource (e.g. updating bank balance).
  4. Release validation: When complete, the worker calls a Lua script to release the lock. The Lua script verifies that the value of the key matches worker_UUID before deleting it, preventing a slow worker from deleting a lock owned by another node.

8. Internal Architecture

Datastore Type Lock Mechanism Pros Cons
Redis (Redlock) SET NX PX (leases) Extremely fast, easy to set up. Not strictly consistent (susceptible to clock drifts).
ZooKeeper / Raft Ephemeral Nodes Strictly consistent (guarantees safety). Slower write throughput; complex setup.
SQL Databases SELECT FOR UPDATE Standard ACID safety. Can lock tables; slows database performance.

9. Request Lifecycle

  • 1. Ingress Request: Node A receives a payment request with ticket ID 405.
  • 2. Lock Check: Node A queries the Redis cluster, executing SET lock:ticket:405 UUID_A NX PX 5000. Redis returns true. Lock is active.
  • 3. Processing: Node A launches the watchdog thread and proceeds to execute payment API.
  • 4. Concurrent Collision: Simultaneously, Node B receives the same request and executes SET lock:ticket:405 UUID_B NX PX 5000. Redis returns false. Node B immediately rejects the request with a "409 Conflict" code.
  • 5. Release: Node A completes payment, terminates the watchdog, and runs Lua code: if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end to safely delete the key.

10. Deep Dive: The Redlock Algorithm

A single Redis master node is a single point of failure. If it crashes, locks are lost. The Redlock Algorithm addresses this by coordinating locks across 5 independent Redis master nodes:

  1. The client captures the current timestamp.
  2. The client attempts to acquire the lock in all 5 instances sequentially, using the same key and UUID. The timeout for each single instance call is kept small (e.g. 5-50ms) to prevent blocking if a node is down.
  3. The client calculates how much time it took to acquire all locks. If it successfully acquired the lock in at least 3 nodes (majority), and the total elapsed time is less than the lock validity time, the lock is acquired.
  4. The lock validity time is recalculated: initial_validity_time - elapsed_time.
  5. If the client fails to acquire the lock in the majority of nodes, it immediately executes an unlock script in all 5 instances to clean up partially set keys.

11. Production Example

  • Uber: Uses Redis-based distributed locking (using Redisson client library in Java) to coordinate matching algorithms, preventing two matching engines from assigning the same driver to different riders concurrently.
  • Kubernetes: Uses ZooKeeper/etcd distributed locking (Leases) to coordinate Leader Election among control plane nodes, ensuring only one manager instance is active as the scheduler master.

12. Advantages

  • Strong Resource Protection: Strictly guarantees mutual exclusion, preventing race conditions.
  • Fault-Tolerant Deadlock Prevention: Lease TTLs ensure locks release naturally even if servers crash.
  • Decoupled Compute: Allows application nodes to scale out horizontally without sharing database locks.

13. Limitations

  • Susceptible to Network Pauses: If a JVM experiences a Stop-the-World garbage collection pause *after* acquiring the lock, the lock lease might expire in Redis before the worker finishes processing, allowing another node to acquire it concurrently.
  • Clock Skew Vulnerability: Redlock relies on physical system times. If NTP clocks drift between nodes, validity calculations can fail.

14. Trade-offs

Weigh lock safety against system throughput:

  • Redis (AP Model): Prioritizes performance. High throughput, low latency locks, but carries a small risk of duplicate locks during network partitions.
  • ZooKeeper (CP Model): Prioritizes consistency. Ephemeral node sessions guarantee strict correctness, but write throughput is lower due to consensus roundtrips.

15. Performance Considerations

  • Acquisition Wait Time: Configure short lock attempt timeouts. If a key is locked, waiting nodes should poll with exponential backoff rather than spamming Redis.
  • Watchdog CPU Cost: Running hundreds of background thread checks can consume CPU resources. Limit watchdog pools.

16. Failure Scenarios: Slow Worker Invalidation

The Slow Worker Failure:
Node A acquires a 5-second lock to write to the database. Node A experiences a 6-second GC pause.
- During the pause, the lock expires in Redis.
- Node B acquires the lock and begins writing to the database.
- Node A wakes up and writes to the database, overwriting Node B's changes.
Mitigation: Implement Fencing Tokens. The lock manager returns an auto-incrementing ID (e.g. token=123) with the lock. The database checks the token; if a write arrives with token 123 after token 124 has already committed, the database rejects it.

17. Best Practices

  • Always Use Lua Scripts for Deletions: Never release a lock using a simple delete query. Always verify the owner token in a Lua script to prevent deleting other workers' locks.
  • Size TTL Appropriately: Keep lease TTLs larger than the average execution time of the critical section.

18. Common Mistakes

  • Missing Watchdogs: Assuming a static TTL is enough. If a database query slows down, a static lock can expire, allowing concurrent processing.
  • Locking Too Aggressively: Locking entire tables or large catalogs instead of granular row keys (e.g., locking the entire inventory instead of the specific item_id).

19. Implementation: Distributed Lock Manager with Watchdog

The following code implements a Distributed Lock Manager simulator featuring auto-renewal watchdogs and safe token verification releases:

20. Interview Questions

Q1 (Easy): Why is setting a lease TTL mandatory on distributed locks?

Answer: Without a lease TTL, if the worker instance crashes or loses network connectivity mid-transaction after acquiring the lock, the lock key remains in the datastore indefinitely. No other node can ever acquire it, creating a permanent deadlock. A lease TTL ensures that the lock expires naturally after a set time, restoring availability.

Q2 (Medium): How do Fencing Tokens prevent slow worker write corruption?

Answer: A fencing token is an auto-incrementing number returned with the lock. When Node A gets the lock, it receives token 100. If Node A experiences a long GC pause and its lock expires, Node B gets the lock with token 101. When Node B writes to the database, it writes token 101. When Node A wakes up and attempts to write with token 100, the database compares the token; since 100 is less than the current committed token 101, the database rejects Node A's write, protecting data integrity.

Q3 (Hard): Detail Martin Kleppmann's critique of the Redlock algorithm. Why is it considered unsafe for strict correctness?

Answer: Martin Kleppmann argues that Redlock is unsafe because it relies on physical clock times:
1. Clock Skew: If one Redis node's clock drifts or jumps ahead (e.g. due to NTP adjustments), it might expire a lock key prematurely, allowing another client to acquire the lock concurrently.
2. Process Pauses: Redlock cannot protect against Stop-the-World GC pauses occurring after the lock is validated but before writes execute. To guarantee absolute correctness, you must use a consensus-backed coordinator (etcd/ZooKeeper) combined with database-level fencing tokens.

21. Practice Exercises

Exercise 1 (Easy): Write a Lua Unlock Script

Write the exact Redis Lua script used to safely delete a lock key only if its value matches the client's unique UUID. Explain why this script runs atomically.

Exercise 2 (Medium): ZooKeeper Recipes

Draw a diagram showing how ZooKeeper ephemeral sequential nodes coordinate a queue lock. Detail how the "watcher" pattern prevents thundering herd polls on the parent lock node.

Exercise 3 (Hard): Design a Fencing Database Controller

Design a PostgreSQL table schema and a SQL update function that validates incoming fencing tokens. If the incoming token is less than the table's current token, raise a SQL state exception to roll back the transaction.

22. Challenge Problem

The Multi-Leader Split-Brain Lock Challenge:

You have a Redis cluster with 3 master nodes. Due to a network partition, Client A is isolated with Node 1, while Client B is isolated with Node 2 and Node 3.

Constraints:

  • Both clients attempt to lock the exact same resource ID.
  • Client B acquires the lock on Node 2 and Node 3 (attaining majority).
  • Node 1's system clock jumps forward by 10 seconds due to an NTP sync drift.

Describe the sequence of lock acquisitions and TTL expirations, and explain whether Client A can acquire the lock on Node 1, breaking mutual exclusion constraints.

23. Summary

Distributed locking coordinates exclusive access to shared resources across independent nodes. While Redis SETNX is fast, it requires watchdog threads to renew leases and Lua scripts for safe releases. For strict correctness, consensus systems like ZooKeeper combined with database fencing tokens protect against clock skew and GC pause vulnerabilities.

24. Cheat Sheet

Requirement Redis (Redlock) ZooKeeper / etcd Database SQL Lock
Consistency Model AP (Eventual) CP (Strong Consensus) ACID (Strict relational)
Performance (Speed) Very High (<2ms) Medium (5-15ms) Low (disk constraints)
Lock Release on Crash TTL lease expiration Ephemeral node deletion Session timeout disconnect
Implementation Cost Low (Simple SET NX) High (Requires complex client) Low (SQL query wrapper)

25. Quiz

Q1: Which parameter prevents a lock key from staying in Redis forever if a worker node crashes?

  • A. Lua script deletion check.
  • B. Time-To-Live (TTL) lease expiration.
  • C. Ephemeral node watch.
  • D. Fencing token ID.

Answer: B. Lease TTLs ensure locks release naturally after timeouts, preventing deadlocks.

2. What is the role of a watchdog thread on distributed locks?

  • A. To detect double-matching attempts.
  • B. To periodically extend the lease TTL while the transaction is still running.
  • C. To check database index health.
  • D. To encrypt the owner token.

Answer: B. Watchdogs renew the lease dynamically, preventing premature expirations during slow processing.

3. In a ZooKeeper lock recipe, what happens to the lock when the client crashes?

  • A. The lock remains locked forever.
  • B. The ephemeral node is deleted automatically by the ZK cluster.
  • C. ZK throws a split-brain warning.
  • D. ZK prompts the admin for manual approval.

Answer: B. Ephemeral nodes are bound to client session lifecycles; they disappear when the client disconnects.

4. Why must a lock delete operation use a Lua script matching owner tokens?

  • A. To speed up network transmission.
  • B. To prevent a slow node from accidentally deleting a lock that has expired and been acquired by another node.
  • C. To validate database schema bounds.
  • D. To bypass rate limits.

Answer: B. Verifying ownership before deletion prevents releasing locks held by other workers.

5. What is a "Fencing Token"?

  • A. A secure SSL certificate key.
  • B. An auto-incrementing ID returned with the lock to prevent out-of-order writes in database nodes.
  • C. A hashing salt value.
  • D. A rate limiter bucket counter.

Answer: B. Fencing tokens are sequential IDs that databases check to reject stale writes from slow workers.

6. In the Redlock algorithm, what is the minimum number of nodes that must approve the lock in a 5-node cluster?

  • A. 2 nodes.
  • B. 3 nodes (majority).
  • C. 4 nodes.
  • D. 5 nodes.

Answer: B. Redlock requires a majority (at least 3 out of 5) to guarantee partition tolerance.

7. What is a major vulnerability of the Redlock algorithm?

  • A. High write costs.
  • B. Reliance on synchronized system clocks (clock drift risks).
  • C. Lack of support for Redis keys.
  • D. Incompatibility with Java.

Answer: B. Redlock relies on physical system clock parameters, making it vulnerable to NTP drifts.

8. How do ZooKeeper watchers prevent a "Thundering Herd" poll bottleneck?

  • A. By blocking all client threads.
  • B. By letting clients watch only the next lowest sequence node in the queue instead of the parent key.
  • C. By running queries on S3.
  • D. By caching results in RAM.

Answer: B. Watching only the immediate predecessor node ensures that when a lock releases, only one client wakes up.

9. If Node A experiences a Stop-the-World GC pause after checking a lock, what error can occur?

  • A. The GC sweep will delete the Redis cache key.
  • B. The lock lease might expire in Redis before Node A resumes execution.
  • C. The database will crash.
  • D. The NTP clock will skew.

Answer: B. A process pause can delay execution past the lease TTL, causing concurrent write conflicts.

10. Why are SQL database lock commands (SELECT FOR UPDATE) avoided at high throughput?

  • A. They are incompatible with PostgreSQL.
  • B. They lock database connection threads and degrade write performance.
  • C. They do not support primary keys.
  • D. They require Redis.

Answer: B. Database-level locking consumes thread pools and degrades database performance.

26. Further Reading

27. Next Lesson Preview

Distributed locking coordinates access, but we must also manage how data converges across regions. In the next lesson, we explore Data Consistency Strategies, reviewing read-your-own-writes mechanisms, monotonic reads, eventual consistency models, and the mathematical quorum validation criteria.

Key takeaways

  • Avoid single points of failure in lock states by using distributed algorithms like Redlock.
  • Always use lock leases (TTL) combined with automatic renewal watchdogs to prevent deadlocks on worker crashes.