A distributed lock is not a bigger mutexA distributed lock is not a bigger mutex. It is a different animal wearing the same costume, and the costume is what gets people hurt.Distributed locks look deceptively similar to local mutexes. You acquire, you perform a critical operation, you release. That resemblance is dangerous.A local mutex lives inside one failure domain. A distributed lock spans processes and networks and clocks and storage, along with failure detectors that can disagree with each other. The lock service may correctly decide that ownership changed while the previous holder is still running or paused or partitioned, or while it is quietly carrying delayed requests.The central question is whether an obsolete client can still produce effects after its authority has ended. Martin Kleppmann frames the same divide as locks used for efficiency versus locks used for correctness. In the correctness case duplicate execution becomes corruption rather than wasted work.A lock is not a critical sectionConsider a lease-backed lock around a reconciliation job.Lease lease = lockService.acquire("order:" + orderId, Duration.ofSeconds(15));try { reconcile(orderId);} finally { lockService.release(lease);}The code is locally reasonable and globally unsafe. Suppose the process pauses for 30 seconds after acquisition. Maybe garbage collection stalls it, maybe the CPU is starved, maybe it is paging, maybe something sends it a stop signal. The lease expires, another worker acquires the lock, and the original process later resumes. Nothing in the resumed process proves that authority still exists.A delayed network request creates the same failure even when the process never pauses at all. Checking the lease immediately before a write does not close the race, because execution can pause in the gap between the check and the write. Kleppmann uses this stale holder problem to show why even a perfect lock service does not automatically make protected resource access safe.Coordination systems can provide strong ownership semantics without controlling the resource that actually matters. The ZooKeeper lock recipe uses ephemeral sequential znodes and gives the lock to the client with the lowest sequence number. etcd ties lock ownership to a lease and releases the lock when that lease expires. Those mechanisms establish authority inside the coordinator. They do nothing to stop an old process from writing to an external resource after ownership has changed.Leases trade deadlock for stale ownershipLeases exist because permanent locks have a severe liveness problem. A crashed owner can block progress forever. Time-bounded authority lets another participant proceed after expiration, but now time becomes part of the safety argument.Redis Redlock computes a remaining validity window from the elapsed acquisition time and assumes local clocks progress at roughly the same rate with bounded drift relative to the TTL. Current Redis documentation also warns that TTL expiration does not use a monotonic clock, and that a shift in the wall clock can compromise consistency.Lease expiration is not necessarily a single instant that everyone observes at once. Consul documents TTL as a lower bound for invalidation. Expiration will not occur before the TTL, but it may occur later. Consul warns about clock skew too. More fundamentally, it notes that failure detection can produce false positives and release a lock while the owner is still alive. Disabling those checks improves safety at the expense of liveness. Faster failure detection improves progress but raises the risk of overlapping authority.Lease renewal reduces probability, not category. Extending a TTL helps a long-running operation, but it cannot make networks timely, or process pauses bounded. Redis documentation now states that lock retention must not be assumed merely because the owning process is still alive, and it recommends fencing tokens for work that runs long.Fencing moves safety to the resourceA fencing token is a monotonically increasing value issued whenever ownership changes. Every protected operation carries that token. The resource remembers the highest token it has accepted and rejects any request carrying an older value. This converts a timing problem into an ordering problem. A stale process may resume, and an old RPC may arrive late, but neither one can overwrite state once a newer owner has advanced the fence.The essential logic belongs at the write boundary, not only in the lock client.long token = lockService.acquire("account:" + accountId).fencingToken();int updated = jdbc.update(""" UPDATE accounts SET state = ?, fence_token = ? WHERE id = ? AND fence_token