Overview
A fencing token is a number that increases every time a lock or leadership is granted. The protected resource uses it to refuse work from stale holders. Martin Kleppmann popularized the pattern in his analysis of distributed locks; ZooKeeper zxids and etcd revisions can serve as tokens.
How it works
Client 1: acquires lock, token=33, then pauses (GC)
Lease expires
Client 2: acquires lock, token=34, writes to storage (max=34)
Client 1: wakes, writes with token=33
Storage: 33 < 34, reject
The storage layer keeps a record of the highest token seen, atomically with the write:
if request.token < stored.maxToken: reject
else: stored.maxToken = request.token; apply write
Leader epochs in Raft and Kafka controller epochs use the same idea: messages from lower terms are ignored.
Pitfalls
- The check must be atomic with the write, for example a conditional update, or a race remains.
- Token generation must survive failover. A lock backed by a single non-replicated Redis can reissue lower numbers.
- Resources without versioning, such as third-party APIs, cannot enforce fencing; use idempotency keys or accept the risk.
- Persist the max token durably, otherwise a restart forgets it.
- Do not rely on lease timeouts and clock checks on the client as a substitute.
Key takeaways
- Leases limit staleness; fencing tokens make it harmless.
- The resource, not the client, enforces exclusion.
- Use a strictly monotonic source backed by consensus.
- Log rejected stale tokens; a rising count signals pauses or clock trouble in lock holders worth investigating.