Wave 4 decomposed: Postgres engine — 11 tasks (schema, open/opts, seam, forwarder, mechanisms, scheduler/outbox, integration, review gate); plan synced
This commit is contained in:
1 parent
ecb8211694
commit
4caea21098
11 files changed
+1287
-3
No files matched your search
@@ -0,0 +1,93 @@
|
||||
---
|
||||
id: pg-engine-locks
|
||||
name: Postgres engine — named locks (`try_lock`, `Lock` handle, duration guards)
|
||||
status: pending
|
||||
depends_on: [pg-engine-seam-tx]
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
tags: [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
|
||||
Reference in new issue
Block a user