Files
alkstore/tasks/pg-engine-locks.md
T

4.3 KiB

id, name, status, depends_on, scope, risk, impact, level, tags
id name status depends_on scope risk impact level tags
pg-engine-locks Postgres engine — named locks (`try_lock`, `Lock` handle, duration guards) pending
pg-engine-seam-tx
narrow low component implementation
wave-4
postgres-engine

Description

Implement the named-lock mechanism's pg arm — the smallest mechanism, and the one with the strongest POC ground (the pg POC's lock probe pinned acquire/release/renew with TTL and expiry re-acquisition directly). Over the schema task's locks table:

  • try_lock(name, owner, ttl) — shared-namespace + non-empty owner validation (InvalidName); the duration guard (ttl <= 0 → opaque Err(Database), detail in the source chain — ADR-023 §2's duration kind; an exclusion mechanism must not silently hand back a lease the caller believes it holds); the closed-store check; then the acquire op on a pool connection: opportunistic-expiry- delete + insert-or-reacquire + read-back (the substrate's lock_acquire semantics, re-derived — the POC's probe shape). Ok(None) = held elsewhere (no-work is a value); Ok(Some(boxed Lock)) on grant.
  • Lock handle (renew, consuming release):
    • renew(ttl) — the same duration guard; the new full TTL window from now (not additive); false = lost it (expired and re-acquired by another owner — the owner-scoped update's 0-rows).
    • release(self: Box<Self>) — consuming; the owner-scoped delete; true = was held (row gone by this owner's hand), false = row-less or foreign (the substrate's row-scoped truth-teller posture — no handle-local "held" flag caching).
  • Guarantee row posture (ADR-008 §7): mutual exclusion bounded by TTL + renewal; expiry lapses silently (no revocation event — expired rows are deleted opportunistically by the next acquire's expiry-delete, no general expiry sweeper — the inherited posture, documented); re-acquirable after expiry (the POC-pinned property — pin it engine-side here; the contract-suite row remains for the suite itself).
  • Same-owner re-acquire: pin the inherited disposition (the substrate's shape: the same owner's re-acquire keeps the original row, TTL not refreshed, reports granted — the SQLite task's pinned posture; engine-sqlite.md's mapping row calls it inherited deliberately, and the pg arm should match it for cross-engine equivalence — flag in Notes if the pg realization differs).
  • Post-lapse release arms: pin both (the stale holder's release deletes its own stale row → true; after a foreign re-acquire → false) — the SQLite task's pinned shape, matched here.
  • Closed-store posture: try_lock/renew/release fail closed with opaque Database (the engine-wide shape).
  • No lock_tx — deliberate absence (core-contract.md's named- locks section); nothing to build, just don't invent one.

Acceptance Criteria

  • try_lock: validation (name + owner), duration guard (ttl <= 0 → opaque Err, detail in the source chain), closed-store check, then acquire — Ok(None) on held-elsewhere
  • Exclusion + loser shape pinned (two owners, one wins); expiry re-acquisition pinned (the POC's property, engine-side)
  • renew: duration guard; full-window reset; false on lost/expired-and-reacquired
  • release: consuming, owner-scoped, both boolean arms pinned (stale-row delete = true; foreign/row-less = false)
  • Same-owner re-acquire disposition pinned (and flagged if it differs from the SQLite arm's)
  • Silent lapse posture documented (no revocation event; no general expiry sweeper)
  • cargo test -p alkstore-postgres (harness server), clippy -D warnings, fmt clean; gates green server-less

References

  • docs/architecture/core-contract.md (named locks section)
  • docs/architecture/decisions/008-contract-v1-pinning.md §7 (guarantee row)
  • docs/architecture/decisions/023-fourth-review-round.md §2 (duration kind)
  • docs/architecture/engine-postgres.md (Mapping the contract: named locks)
  • docs/research/poc-pg-posture-findings.md (the lock probe row)
  • alkstore-sqlite/src/lock.rs (the structural twin — match its pinned dispositions)
  • alkstore/src/lock.rs (the trait)

Notes

To be filled by implementation agent

Summary

To be filled on completion