Request × Key-State Matrix
A (Acquire)
| Key state | Server action | Response | Timing |
|---|---|---|---|
| Not registered | Register (expiry = now+lease), issue token | A+token+owner+key | Immediately |
| Registered + expired | Renew (now+lease), new token | A+token+owner+key | Immediately |
Registered + valid holder + wait=0 | Do not enqueue | T+owner+key | Immediately |
Registered + valid holder + wait>0 | Enqueue, hold response | A or T+owner+key | Acquired via release/expiry, or T when wait expires |
| Queue or server capacity at its cap | Do not enqueue | B+owner+key | Immediately |
- The release path is FIFO-fair: when the holder does
R, it's handed directly to the waiter at the front of the queue (no re-contention, a new token). - Only the expiry path is non-FIFO.
- Matching the current holder's
ownerreceives no special treatment. It is not authority to return an active token or renew a lease; the ordinary held-key rules apply. - Responses can arrive out of request order, so clients must match by the echoed
owner.
R (Release)
| Key state | Server action | Response |
|---|---|---|
| Registered + token matches, has waiters | Hand off directly to the waiter at the front (new token) | R+token+key |
| Registered + token matches, no waiters | Delete the key | R+token+key |
| Registered + token mismatch | Nothing (lock stays held) | N+token+key |
| Not registered | Nothing | N+token+key |
When Expiry Is Processed
- Single mode processes expiry lazily — a key past its expiry is taken over on the spot by the next acquire (the second row above), and a separate 5-second sweep cleans up expired keys with no waiters. So a release on a key that's expired but not yet swept can get
Rinstead ofN. - Cluster mode waits until the next token-validated deadline in a min-heap, then requires the leader to commit an
Expirecommand through consensus before the key disappears. This keeps clock differences between nodes from splitting state.
The meaning above still holds in cluster mode — except the response only goes out after the state change commits via majority consensus, and a non-leader node responds with M (moved) or E no_leader instead of processing the request.