feat(pg-lo): the joint contribution — staged-put mechanics into the store-held tx (wave 6)
The wave-5 deferred design point, landed: under the consolidation posture (kv = postgres + large = pg-lo, same pg instance), a large entry's content reaches the store-held joint transaction the pin row commits in (ADR-012 §4's one-tx ruling). - The contribution's API home (the named design point) — decided: the joint staging-cursor variant. `PgLoBackend::put_joint_staged(&mut PgJointTx)` hands `PgLoJointStaging<'_>`, the staged put with inverted ownership: it borrows the held checkout, owns no checkout, opens no tx, never COMMITs, never ROLLBACKs (the driver owns termination, amendment A1) — no Drop, by design. The deciding facts: the large tier's put contract is the streaming cursor, the fleet put driver routes to the joint form as the non-fleet path routes to the staged form, and the unknown-length derivation pass reads back through the cursor's `ReadCursor` seam — none servable by a whole-value method. - The mechanics are the landed bodies re-homed: the 512 KiB `lowrite` knee (LOWRITE_CHUNK aggregation of the store core's 64 KiB windows), read-your-own-writes `lo_get` windows, the publish-time CAS (the key resolves at publish — the staged put's named design point), the companion-row insert, transactional LO creation (C6: the LO lives and dies with the store-held tx). The re-put form publishes an empty contribution — the staged LO unlinked inside the store-held tx, zero residue; a concurrent CAS miss surfaces the companion key's unique violation (the landed puts' posture). - Enabling affordance: `PgJointTx::client()` — the crate-internal statement surface every joint contribution (and the store-fleet-put driver's A1 termination design) rides through; deliberately not an abort verb. - Crate-internal seam: the public trait surface untouched, nothing of `EngineFleetState` on pg-lo (ADR-010 §2's correction), the engine-level held-tx staging cursor and the non-fleet put path byte-unchanged. Gated `feature = "postgres"`; both pg-lo pure builds compile. - Contract tests (`src/pg_lo.rs` mod joint, both engines on one dedicated database): one-tx publish (kv row + LO + companion row + pin row, byte-exact round-trips after `pin_commit`); forced failure mid-contribution → driver ROLLBACK → zero residue (no LO pages, no companion row, no pin row, no kv row); the CAS re-put form (empty contribution, upsert renewal); the 512 KiB knee (multi-chunk). Verification: `cargo test` (default) green; `--features "pg-lo,postgres"` green server-less (skipping) and against the harness server (pglo-poc, :15432 — the four joint tests + full pg_lo + conformance suites live); `--all-features` green; clippy `-D warnings` green (default / postgres / pg-lo+postgres / all-features / the two pg-lo pure builds); `cargo fmt --check` green; `cargo doc --no-deps` clean. The store-core driver that calls the contribution on the fleet put path is store-fleet-put.
This commit is contained in:
1 parent
b6b353242b
commit
76be7b0273
4 files changed
+730
-10
No files matched your search
@@ -1,7 +1,7 @@
|
||||
---
|
||||
id: engine-pg-lo-joint
|
||||
name: the pg-lo joint contribution — staged-put mechanics into the store-held tx
|
||||
status: pending
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: moderate
|
||||
risk: high
|
||||
@@ -138,8 +138,102 @@ the postgres host's clause — landed wave 4, unchanged here).
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
> The contribution's API shape (the named design point) — **decided: the
|
||||
> joint staging-cursor variant**, not a whole-value method taking
|
||||
> `&mut PgJointTx`. Landed as `PgLoBackend::put_joint_staged(&self, tx:
|
||||
> &mut PgJointTx) -> PgLoJointStaging<'_>` in `src/pg_lo.rs` (gated
|
||||
> `feature = "postgres"` — `pg-lo` does not imply `postgres`, ADR-013
|
||||
> §4; both pure builds compile). The deciding facts: (1) the large
|
||||
> tier's put contract is the streaming staging cursor
|
||||
> (`StagingCursor`/`ReadCursor`), and store-fleet-put routes to the
|
||||
> joint form "exactly as the non-fleet path routes to the staged form"
|
||||
> — stage-while-hashing, the key resolving at stream end — so a
|
||||
> whole-value method would force the driver to materialize every large
|
||||
> entry, regressing the memory posture; (2) the unknown-length flow's
|
||||
> derivation pass re-reads the staged artifact through the cursor's
|
||||
> read seam, the engine-level `PgLoStaging` shape — a whole-value
|
||||
> method cannot serve any of it; (3) the cursor is exactly "the staged
|
||||
> put with inverted ownership" — the task description's own framing —
|
||||
> the landed bodies re-homed, not re-written. Consequence recorded:
|
||||
> the CAS existence check lands at **publish** (the key resolves
|
||||
> there), the landed `PgLoStaging::publish` shape — a re-put publishes
|
||||
> an **empty contribution** (the staged LO unlinked *inside the
|
||||
> store-held tx*, so the committed tx carries nothing of the losing
|
||||
> put; zero residue; the driver's `pin_commit` then upsert-renews the
|
||||
> pin row), while a concurrent CAS miss surfaces the companion key's
|
||||
> unique violation (the landed puts' posture: check = fast path, PK =
|
||||
> hard guarantee). "Never `lo_create`" holds literally only in the
|
||||
> engine-level whole-value put — the cursor form's handout must create
|
||||
> the LO before the key exists — but the LO lives and dies with the
|
||||
> store-held tx (C6, structural), so a losing put leaves zero residue
|
||||
> either way (contract-tested: the re-put form's catalog stays at
|
||||
> exactly the first writer's pages).
|
||||
>
|
||||
> **Ownership rule honored, literally:** `PgLoJointStaging` borrows the
|
||||
> held checkout (`&'a mut PgJointTx`); it owns no checkout, opens no
|
||||
> tx, never COMMITs, never ROLLBACKs — including on `Drop` (no Drop
|
||||
> impl: the driver owns termination on every path, amendment A1). Its
|
||||
> failure paths (statement failures, the injected media failure after
|
||||
> the tail flush, the concurrent-PK race) return with the tx exactly
|
||||
> as the contribution found it — open, uncommitted, un-rolled-back —
|
||||
> composing with the driver's catch-propagate-terminate (A1) with no
|
||||
> partial LO state that outlives the termination's rollback
|
||||
> (transactional LO creation). The engine-level held-tx staging cursor
|
||||
> and the non-fleet put path are byte-unchanged (wave-5 hygiene tests
|
||||
> green untouched); nothing of `EngineFleetState` is implemented on
|
||||
> pg-lo; the public trait surface untouched.
|
||||
>
|
||||
> **Enabling affordance (minimal, for the contribution and the next
|
||||
> driver task):** `PgJointTx::client()` — a crate-internal accessor
|
||||
> returning `&Object` (the field stays private), documented as the
|
||||
> statement surface every joint contribution rides. The contract
|
||||
> tests' driver-side ROLLBACK (the forced-failure form) and the future
|
||||
> store-fleet-put driver's A1 termination design are the consumers;
|
||||
> the accessor is deliberately *not* an abort verb (A1: an explicit
|
||||
> abort verb would need a new ADR — the store core's driver issues its
|
||||
> termination through the exposed checkout; that design decision
|
||||
> remains the driver task's).
|
||||
>
|
||||
> **Tests** (`src/pg_lo.rs` `mod joint`, feature `postgres`-gated, the
|
||||
> env-driven convention, per-test dedicated databases from the start —
|
||||
> amendment A2's lesson honored; the probes ride the pg-lo engine's
|
||||
> pool over the kv host's tables: one instance, one schema): the
|
||||
> one-tx publish (kv host's `joint_put` row + LO content + companion
|
||||
> row through the joint cursor + `pin_commit`'s pin row, byte-exact
|
||||
> round-trips of both entries after commit, exactly one LO with
|
||||
> exactly its page count); the forced-failure zero-residue form
|
||||
> (injection after the tail flush, driver ROLLBACK, all four artifact
|
||||
> families absent); the CAS re-put form (empty contribution, first
|
||||
> writer wins, pin row upsert-renewed, zero losing-LO residue); the
|
||||
> 512 KiB knee (64 KiB appends aggregating into multi-`LOWRITE_CHUNK`
|
||||
> statements, boundary-crossing read-back, multi-chunk round-trip).
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
The pg-lo engine's joint contribution landed: under the consolidation
|
||||
posture (kv = postgres + large = pg-lo, same pg instance), a large
|
||||
entry's content reaches the store-held joint transaction —
|
||||
`put_joint_staged` hands the `PgLoJointStaging` staging cursor against
|
||||
the postgres engine's held `PgJointTx` checkout; the cursor rides the
|
||||
held tx for every statement (chunked 512 KiB `lowrite` aggregation,
|
||||
read-your-own-writes `lo_get` windows for the derivation pass, the
|
||||
publish-time CAS, the companion-row insert or the re-put's in-tx
|
||||
`lo_unlink` empty-contribution form, transactional LO creation), and
|
||||
the store's `pin_commit` — landed wave 4, unchanged — publishes entry +
|
||||
pin row and commits in one step. The ownership rule is honored
|
||||
structurally: the engine contributes mechanics into the tx it does not
|
||||
own, never committing or rolling it back on any path (A1's discipline;
|
||||
the `PgJointTx::client()` accessor is the checkout surface). The public
|
||||
trait surface is untouched; nothing of `EngineFleetState` exists on
|
||||
pg-lo; the engine-level held-tx staging cursor and the non-fleet put
|
||||
path are byte-unchanged. Verified: `cargo test` (default) green;
|
||||
`--features "pg-lo,postgres"` green server-less (skipping cleanly) and
|
||||
against the harness server (pglo-poc, :15432 — live, all four joint
|
||||
contract tests plus the full pg_lo and conformance suites green);
|
||||
`--all-features` green; clippy `-D warnings` green (default, postgres,
|
||||
pg-lo+postgres, all-features, and the pg-lo-pure builds
|
||||
`--no-default-features --features "pg-lo,mem"` and
|
||||
`--features "postgres,pg-lo"`); `cargo fmt --check` green; `cargo doc
|
||||
--no-deps` clean. The store-core driver that calls this contribution on
|
||||
the fleet put path is `store-fleet-put` (this task's scope boundary —
|
||||
recorded in the module doc and here).
|
||||
Reference in new issue
Block a user