- Register — successor generation enters
joining→standby. - Readiness — successor runs gates (
preparing); on success returns tostandby. - Drain — current owner
PrepareHandoff→Drain: reject new work, wait forDrainGroup. - Reserve —
Transfer(successorID)sets claim phasereservedwithpending_successor(token unchanged). - Token advance —
Commitatomically increments fencing token and assigns owner to successor. - Active — successor observes ownership / acquires lease and becomes
active. - Stale observed — previous owner's renewals fail with
ErrStaleToken/ lease context canceled. - Confirm / retire — previous generation transitions to
retired.
If the successor fails before Commit, call Abort:
- claim returns to
ownedby the previous generation - fencing token is not advanced
- ownership is not silently discarded
| Failure | Behavior |
|---|---|
| Owner crashes | Lease expires; another generation may acquire (new token) |
| Successor crashes after prepare | Transfer timeout aborts; owner restored |
| Commit fails mid-flight | Reservation remains abortable; no dual owners |
| Network partition of old owner | Renewals fail; successor may acquire after expiry; old release with stale token rejected |
| Multiple candidates | Only one AcquireClaim wins; others get ErrClaimHeld |
| Drain timeout | Drain returns ErrTimeout; handoff can Abort |
| Abort after commit | Rejected (ErrInvalidState) |
| Double prepare different successors | ErrConcurrentTransfer |
See failure-model.md for expanded scenarios.