A process can hold a distributed lease, pause long enough for that lease to expire, then resume with local state that still says it owns the resource. Another process may already have acquired a newer lease during the pause. At that point, mutual exclusion in the lock service is not enough: two processes can each act as if they have authority, even though only one lease is current.
This is a boundary problem between coordination and the resource being protected. A lease service can decide which holder is current according to its own state. It cannot retroactively erase instructions already held by an old process, nor can it stop that process from sending a request after a long pause.
A fencing token carries the coordination order across that boundary. Each successful acquisition receives a token greater than every token issued before it. The protected resource records the greatest token it has accepted and rejects operations carrying an older one. Expiration then becomes observable where stale work can cause damage.
Lease expiry does not revoke a process
A lease differs from an indefinitely held mutex because ownership has a time bound. The holder normally renews before expiry. If renewal stops, another contender can eventually acquire the lease without waiting for explicit cooperation from the former holder.
That property is useful only if expiry is treated as a statement about the coordination service, not as a mechanism that forcibly stops application code.
Consider holder A with lease token 41. A becomes unable to run for longer than the lease interval. The coordinator expires A’s lease and grants holder B a new lease with token 42. B begins work. Later A resumes.
A local check performed before the pause cannot establish current ownership after the pause. A network request prepared earlier can also arrive after B has acquired the resource. Clock checks inside A do not close this gap: the protected resource still needs a basis for distinguishing an operation from the old ownership epoch from one belonging to the current epoch.
The core issue is stale authority. Time-based expiry permits progress in the coordinator, but it does not by itself make old authority unusable at downstream boundaries.
A monotonic token turns epochs into data
A fencing token is an ordered generation number attached to a lease acquisition. The ordering matters more than the exact representation.
Suppose acquisitions produce:
A acquires: token 41
A pauses
lease expires
B acquires: token 42
B writes with 42
A resumes
A writes with 41If the storage boundary remembers that it has accepted token 42, it can reject A’s later request carrying 41. The stale holder is not required to notice its own staleness first.
The protected operation can be modeled as accepting a pair (token, mutation) under a condition such as:
accept only if token >= highest_accepted_tokenThe exact comparison depends on the operation. If multiple writes from one lease are valid, equality may be accepted after the first write from that holder. If each operation has its own sequence, a stricter ordering may be appropriate. The important invariant is that an operation from an older lease generation cannot overwrite effects already admitted from a newer generation.
This changes the role of the lease identifier. A random identifier can distinguish two holders, but it does not tell the resource which holder is newer. A monotonic token provides an order that the resource can enforce.
The resource has to participate
Fencing cannot be implemented solely inside the lock client. If application code obtains token 52 and then discards it before touching the protected resource, the resource has no information with which to reject token 51.
The enforcement point must therefore be on, or immediately in front of, the state transition that needs protection. For a database-backed resource, the token can participate in a conditional update. For a service boundary, it can travel as request metadata and be compared with persisted generation state. For an object or file interface, equivalent enforcement requires that the storage path expose some conditional mechanism capable of preserving the token order.
This requirement limits where fencing can be applied. A resource that accepts unconditional writes and stores no ordering metadata cannot distinguish stale lease holders. Wrapping such a resource with a separate validator can work only if the validation and protected mutation cannot be separated by another race that defeats the check.
The token also needs a trustworthy source of monotonicity. A coordinator that can issue a lower or reused generation while older requests remain possible breaks the ordering argument. The implementation does not require wall-clock timestamps; in fact, timestamps introduce clock assumptions that a simple logical counter can avoid. It requires an ordering domain whose issued generations do not move backward for the lifetime relevant to stale requests.
Fencing and optimistic concurrency solve different races
A version column used for optimistic concurrency can resemble a fencing token because both appear in conditional writes. Their authority comes from different places.
An optimistic version usually represents the state of a particular record. A client reads version 7, computes a change, and attempts an update conditioned on the record still being at version 7. If another writer changes the record first, the condition fails.
A fencing token represents an ownership epoch granted by a coordinator. The protected resource does not need the stale holder to have read an old record version. It rejects the holder because a newer ownership generation has already reached the boundary.
The mechanisms can coexist. A mutation may need to prove both that its lease generation is current enough and that the application state still matches the version on which the mutation was computed. Combining the two does not make them interchangeable: one orders authority, while the other detects a state change relative to an observation.
Token order is not operation order
A fencing token orders lease acquisitions. It does not automatically order every event produced inside one lease.
Holder B with token 42 might send two asynchronous writes that arrive in the opposite order from which B created them. Both carry the same fencing token, so a resource that checks only the lease generation can accept both. If their internal order matters, another mechanism is needed, such as a per-holder sequence number, a compare-and-set condition, or an application invariant encoded in storage.
Similarly, accepting a newer token does not prove that all work from older generations has completed. It proves only that older generations can be rejected at enforcement points that honor the token. External side effects that cannot carry or validate the generation remain outside that protection.
This distinction keeps the guarantee narrow and useful. Fencing is not a general transaction protocol. It is a way to make stale ownership detectable at a boundary.
The boundary defines the guarantee
Distributed locking is often described as if acquisition alone creates exclusive execution. With leases, that description omits the interval between coordinator state and resource effects. A former holder can survive expiry as a running process, a delayed packet, a queued request, or buffered work.
A fencing token gives the protected resource enough history to reject that old authority after a newer holder has appeared. The coordinator establishes an ordered sequence of ownership epochs; the resource enforces that sequence when mutations arrive.
The resulting guarantee depends on both halves. Without lease expiry, a failed holder can block progress. Without fencing at the resource, expiry can permit a new holder while stale work from the old one remains admissible. The useful abstraction is therefore not merely a distributed lock. It is an ordered ownership protocol whose generation reaches the state it is intended to protect.