Distributed Lock
A Distributed Lock enforces mutual exclusion across nodes that share no memory, using a consistent store, leases, and fencing tokens. It prevents concurrent conflicting updates but should be used sparingly.
A Distributed Lock provides mutual exclusion across processes that run on different machines and share no memory. Within one process a mutex suffices, but when many instances might act on the same resource concurrently, they need an external arbiter. A distributed lock ensures that at most one holder enters a critical section at a time, preventing conflicting updates and duplicate work.
How It Works
Instances request the lock from a shared, consistent store. The store grants the lock to one requester and refuses others until it is released. Locks should be held with a lease (time-to-live) so a crashed holder does not block the resource forever; the lease expires and the lock frees automatically. To stay safe despite expiry and clock issues, robust designs issue a monotonically increasing fencing token with each grant, and the protected resource rejects operations carrying a stale token.
Implementations include locks on ZooKeeper, etcd, and Consul, and Redis-based approaches such as Redlock. Correctness depends heavily on the store's consistency guarantees.
Lock granularity matters: a coarse lock is simple but serializes unrelated work, while fine-grained locks keyed by resource ID allow concurrency where it is safe. Holders should do the minimum work inside the critical section and release promptly. For workloads that can tolerate occasional double execution, an idempotent consumer is often a more scalable choice than a lock.
When to Use It
Use a distributed lock when multiple nodes might modify the same resource and you must serialize them, when a periodic job must run on only one instance, or when you need to guard a non-idempotent operation. It provides cross-node mutual exclusion.
Prefer alternatives when possible: idempotent operations, optimistic concurrency with version checks, or partitioning often avoid the need for a lock and scale better.
It is also reasonable for short, infrequent critical sections where the simplicity of a lock outweighs the engineering cost of a fully lock-free design.
Trade-offs
Locks introduce contention and can become a bottleneck. They depend on the store's availability and consistency; a partition or expired lease can lead to two holders unless fencing tokens are used. Holding locks too long or too broadly hurts throughput. They add a critical dependency. Used sparingly and correctly, they prevent serious data corruption. The safest implementations never rely on timing alone; they combine a short lease with a fencing token and a resource that validates the token, so correctness does not depend on perfectly synchronized clocks.
Related Patterns
Leader Election is often implemented with a leased distributed lock. Consistent Hashing avoids locks by giving each key a single owner. Idempotency and optimistic concurrency are lock-free alternatives. Sagas coordinate longer cross-service workflows without global locks.
Example
Several worker instances consume a queue, and two might pick up the same expensive report job. Before processing, a worker acquires a Redis lock keyed by the report ID with a 60-second lease and a fencing token. Only one worker proceeds; the other skips. The report storage accepts writes only with the current token, so even a delayed worker whose lease expired cannot overwrite newer results.