pg_lease — Invariant → Test Mapping
Stage-09 regression suite (test/sql/pg_lease.sql). Test IDs are the
comment banners in the test file. Regression tests are single-session;
multi-session races belong to the stage-10 isolation suite, restart/crash
to the stage-11 failure suite — noted below where that is the authoritative
test.
SEMANTICS.md operations
| Spec item | Test |
|---|---|
| §4.1 Acquire fresh key (epoch 1, expiry) | OPS-02 |
§4.1 Acquire denied while HELD (not_acquirable) |
OPS-04 |
§4.1 Blocking mode wait bound exhausted = timeout (57014) |
OPS-11 |
| §4.1 Same-owner idempotent re-acquire (Issue 3) | OPS-03 |
| §4.2 Renew ok / non-holder / wrong epoch / unknown key | OPS-06 |
| §4.3 Release wrong owner / wrong epoch / ok / duplicate | OPS-07, OPS-08 |
§4.3 Release after lapse = lapsed (review finding 2) |
OPS-10 |
| §4.4 Inspect held / unleased / lapsed (lazy model, Issue 1) | OPS-02, OPS-05, OPS-09 |
§5 Input constraints, §7 invalid_input |
OPS-01 |
§7 lapsed vs not_owner distinguishability |
OPS-06, OPS-09, OPS-10 |
§7 timeout vs non-blocking denial distinguishability |
OPS-04, OPS-11 |
| §8 Transaction semantics: commit persists | TXN-01 |
| §8 Rollback undoes acquire / renew / release | TXN-02, TXN-03, TXN-04 |
| §9 Takeover of lapsed lease | OPS-09 |
| §10 Lapsed holder: renew and release fail; must re-acquire | OPS-09, OPS-10 |
INVARIANTS.md
| Invariant | Regression test | Authoritative stage |
|---|---|---|
| I1 Mutual exclusion | OPS-04 (denied acquire while held); OPS-09 (post-lapse only) | Stage 10 (simultaneous-acquire races) |
| I2 Epoch monotonicity | INV-I2 chain 3→4→5; OPS-08 duplicate release leaves epoch; INV-I2/I9 7→8→9; OPS-09 takeover epoch 2 | regression |
| I3 Fencing effectiveness | epoch_mismatch on renew/release with stale epoch (OPS-06, OPS-07); consumer-side rejection is a caller convention, specified S4/S10 | regression (spec-level) |
| I4 Ambiguity window bounded | OPS-09 (lapse observable immediately after expiry; inspect reports unowned) | regression; window bound A1/A2 stated at stage 16 |
| I5 Takeover liveness | OPS-09 (takeover succeeds after TTL without holder action) | regression |
| I6 Atomicity of transitions | TXN-02/03/04 (all-or-nothing under rollback) | regression; race-level in stage 10 |
| I7 No client clock trust | INV-I7 (no operation accepts a timestamp; lapse governed by server clock) | regression (structural) |
| I8 Renewal preserves identity and epoch | OPS-03 (idempotent re-acquire), OPS-06 (renew ok keeps epoch 1) | regression |
| I9 Restart survival | — | Stage 11 (crash/restart suite); epoch durability structurally via WAL-backed table |
| I10 Fail-closed | OPS-01 (no rows after invalid ops); denied ops leave state unchanged (OPS-04, OPS-06, OPS-07) | regression |
| I11 Key independence | INV-I11 | regression; cross-key races in stage 10 |
Notes
- OPS-01 also asserts the invalid-input block leaves zero rows (I10).
- Timestamp masking (
in_futureflags) keeps expected output deterministic; exact expiry values are never asserted, only ordering vs server time. - Stage 10 (isolation): simultaneous acquire, renew-vs-takeover, release-vs-acquire, blocked-acquire wake behavior — I1, I4, I6, I11 races.
- Stage 10 suite:
test/specs/*.specviamake installcheck— compete (I1 single-winner, all start orders), races (renew/release vs acquire/takeover: I1, I3), stale-client (I2, I3 post-takeover rejection), rollback-race (S8, I6: blocked acquirer resolved by COMMIT/ROLLBACK of the competing transaction — ordering proven by<waiting ...>serialization, not sleeps), independent-keys (I11). - Waiter wake (
test/wait-wake.sh,make check-wait): a blocking acquirer must observe a release by another session within its wait bound — S4.1 blocking mode. Not expressible in isolationtester (the waiter sleeps on a timer, not a lock); asserted via two concurrent sessions with a generous bound, so it tests ordering, not timing. - Stage 11 (failure): crash/restart (I9, D1–D3), session termination, resource-exhaustion fail-closed behavior.