- store-fleet-construction: the fleet: true declaration slotting into the fleet-capable constructors (the recorded non-event), the composite fleet-validity predicate enforced at the constructor (kv=postgres AND pg-lo-same-instance OR local-on-declared-shared- media OR kv-only re-routing), the TTL/owner-id fleet state, the same-instance probe design point - engine-pg-lo-joint: the pg-lo joint contribution — staged-put mechanics (512 KiB knee, CAS, transactional LO creation) into the store-held PgJointTx, the wave-5 deferred API-shape design point - store-fleet-put: the joint entry+pin tx driver (amendment A1's drop discipline lands here — its recorded owner), the pin guard's TTL/3 renewal form, the dedup upsert-renewal, pin-first/ publish-second for large=local, the consolidation one-tx put - store-fleet-sweep: the SQL sweep lease (ADR-013 §2 semantics), SQL-level delete arbitration on the direct + window-execution paths, the expired-pin reaping clause (the seam as the named home for fleet-clause growth), the fleet liveness registration verb, the two-domain arbitration composition - suite-column-fleet: the fleet column — two-instance factories over one schema, the exact-count fleet sweep rows (the wave's gate), lease contention, cross-instance arbitration, TTL/renewal rows, the fleet-validity predicate rows - review-wave-6: the fleet gate per the plan, plus a standing code-smell/hygiene block — cargo doc --no-deps asserted clean (the twice-bitten PGLO-1 rustdoc private-link class), the allow(dead_code) scope notes audited against their remainder, stale wave-6 forward references, and task-Summary-vs-diff correctness Harness server cited in every server-touching task: dockerized pglo-poc (postgres:16-alpine) on :15432, db blobs, postgres/poc (verified live 2026-10-12); env-driven ALKBLOBS_PG_* settings, clean server-less skips, acceptance requires green against the server. Dependency graph validated: two parallel roots (construction, pg-lo joint), put merging them, sweep parallel to put, the fleet column merging, the review gate last; no cycles.
10 KiB
10 KiB
id, name, status, depends_on, scope, risk, impact, level, tags
| id | name | status | depends_on | scope | risk | impact | level | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| store-fleet-sweep | the fleet GC protocol — SQL sweep lease, SQL-level arbitration, pin reaping, the fleet liveness verb | pending |
|
broad | high | phase | implementation |
|
Description
Grow the sweep and the delete paths into their fleet forms under
fleet: true (ADR-008 §2; ADR-010 §2; ADR-012 §1/§5/§6.6/§6.7;
ADR-013 §2) — the architecture's most intricate state machine extended
across instances, its invariants unchanged (a visible pool entry is
never deleted while liveness protects it; an in-flight put is never
deleted by a sweep started before it committed). The non-fleet forms
(inline + durable, single-node) are byte-unchanged.
- The sweep lease (ADR-013 §2's named exclusive semantics; the
shipped provider is the wave-4 advisory-lock clause): under fleet
mode the sweep acquires
sweep_lock_acquire()on the fleet host before sweeping and holds it for the whole sweep; contention is theGcAbortCause::SweeperLockreport abort (Ok(SweepReport), nothing deleted — ADR-012 §1); holdership ends at release, owner death, or session loss (the clause's session-scoped semantics, wave 4). The in-process single-flight flag composes (both held — the instance- local guard stays cheap and uniform). Named design point — the lease's hold across the sweep body: the lease's held checkout must not deadlock the sweep's own engine work (the wave-4 lease holds one pooled connection for its duration; the sweep's other clauses ride further checkouts — verify the pool sizing composes and record the posture). - SQL-level delete arbitration (ADR-008 §2): under fleet mode the
delete-time decision is the seam's
arbitrate(key)— theSELECT … FOR UPDATEfinger-lock folding un-expired pin rows + registered liveness tables into one answer — on both the directdelete()path (protected →Error::GcRefuse, the facade owns the refusal) and the durable window's execution-time re-arbitration. Named design point — the two arbitration domains' composition: the in-processGcHomemachinery (markers, in-process pins, callbacks, in-process sources) and the SQLarbitrateboth run — the in-process domain guards this instance's in-flight state (the marker mechanism closes the local sweep-vs-put race without engine I/O under the lock), the SQL domain is the cross-instance authority; decide the composition order (in-process first — cheap, local; SQL second — fleet-authoritative) and record it. The engine-sidepin_checkclassification stays available to the store core where a pins-only answer suffices (its consumer beyondarbitratemay be wave 7's ops renewal probe — do not force it here). - The expired-pin reaping maintenance step (ADR-008 §2's GC-of-GC
cleanup: "an expired pin is abandoned … and is reaped by the
sweeper's maintenance step"): the fleet sweep gains the maintenance
step deleting expired pin rows. Named design point — the clause
home:
EngineFleetStatehas no reap clause; the seam is the named home for fleet-clause growth (ADR-010 §2: "If fleet clauses ever grow, they grow here") — add the clause body on the postgres host (e.g.pin_reap(now) → count, one statement, contract-tested per the applicability row) rather than any raw-SQL side-channel (rejected by ADR-010 §2). The step runs under the lease, before or alongside the overdue execution (record the ordering). - The fleet liveness registration verb (ADR-012 §6.6): "the same
verb with a fleet-shaped argument" — the facade's
register_liveness_sourcegains its fleet form, registering a named table reference in the shared engine (the embedder's own table per ADR-008 §2; the store core reads it, never owns it — the schema-ownership rule). The engine-side registration landed wave 4 (PostgresBackend::register_liveness_source— quoted-identifier boundary, idempotent re-registration, malformed refs error). Named design points — the verb's shape: how the fleet form rides the public verb (a fleet-shaped argument vs a sibling method — decide consistent with §6.6's "same verb" wording and the existing signature's infallibility: the engine-side registration is fallible, so the fleet form's error surface needs deciding), and whether in-processLivenessSourceregistrations remain legal under fleet mode (ADR-008: in-process callback views are not a fleet liveness form — an instance-local registration's protection is instance-local only; decide allow-and-document vs reject and record it). Deregistration composes (the correction flow, ADR-012 §6.4). - The protect callback's fleet posture (ADR-008 §2): single-node- only — under fleet mode the callback remains consultable by this instance's sweeps (its protection is instance-local; the embedder's fleet-wide protection materializes into its liveness table or pin rows before invoking sweep). Record the posture in the verb docs; no new machinery.
- The fleet sweep body (per sweep, under the lease): the protect
consult (instance-local, abort
ProtectFailedprecedes everything) → the maintenance step (expired-pin reaping) → execute overdue (window_takeon the postgres host — the one-window-host rule holds: every fleet-valid configuration's window hosts on the kv engine; each overdue candidate re-arbitrated at execution time via the SQL arbitration, deleted where it lives — the fall-through delete across both tiers) → mark (the pool enumerated; the live set from the in-process sources — the SQL liveness tables are consulted at arbitration time, not enumerated into the mark set) → stage atvisible_after = mark_end + window→ release the lease. Report counts exact relative to the schedule (the durable form's reading carries: staged-this-sweep vs executed-this-sweep are different sets). - The sweep-vs-put race under fleet, closed by the row: a fleet
put's pin row commits atomically with its entry (the joint tx,
store-fleet-put), so a sweep's window execution can never observe a visible-unpinned entry; a put landing during the window delay has its row committed before its entry is visible and the execution-time SQL arbitration cancels the candidate. Unit-test the race across the delay in its fleet form (the wave-3 durable-form race test's fleet edition). - Unit tests (env-driven, skipping cleanly server-less; the
harness server
pglo-poc, :15432, dbblobs,postgres/poc— verified live 2026-10-12): the lease's fleet form (contention between two instances of one store shape → the second abortsSweeperLockwith nothing deleted; release/loss reaping); SQL arbitration on the direct-delete path (a pin row from a foreign owner id refuses the delete — the cross-instance protection proof;GcRefuse); the maintenance step reaps exactly the expired set (un-expired rows survive); the fleet liveness verb registers a test-owned embedder table and its rows protect (the schema- ownership rule — the engine never creates the table); the two-instance exact-count sweep shape (instance A pins, instance B sweeps — the pinned entry survives; unpinned twins delete); the race across the delay; the non-fleet forms unchanged (all existing suites green).
Acceptance Criteria
- Under
fleet: true, the sweep acquires the SQL lease (the ADR-013 §2 semantics: atomic test-and-set, one sweeper per pool across instances, contention =SweeperLockreport abort with nothing deleted); the in-process flag composes; underfleet: falsethe sweep forms are byte-unchanged - Delete-time arbitration is SQL-level under fleet mode on both
the direct path (
GcRefuse) and the window execution (execution-time re-arbitration); a foreign owner's un-expired pin row protects (cross-instance proof); the two-domain composition decided and recorded - The expired-pin reaping maintenance step landed as a new
EngineFleetStateclause on the postgres host (the seam is the named home; no raw-SQL side-channel), contract-tested, run under the lease - The fleet liveness registration verb landed (the named table reference form; the verb-shape and in-process-source design points decided and recorded; the schema-ownership rule holds)
- The protect callback's fleet posture recorded (instance-local; materialization is the embedder's fleet-wide duty)
- The fleet race property unit-tested across the window delay (a put landing during the delay survives via the row + the execution-time SQL re-arbitration)
cargo test(default) green;cargo test --features postgresgreen server-less (skipping) and against the harness server at acceptance;cargo clippy --all-targets -- -D warningsandcargo fmt --checkgreen (default +--features postgres);cargo doc --no-depsclean
References
- src/store.rs (the sweep's two forms;
arbitrated_delete; the fleet flag fromstore-fleet-construction) - src/gc.rs (
GcHome— the in-process domain;EngineFleetState— the clauses; the seam-as-named-home note) - src/postgres.rs (the lease,
arbitrate,pin_check— the wave-4 clause bodies this drives; the liveness registration) - docs/architecture/decisions/008-trait-size-probe-fleet-gc-and-large-engines.md §2 (the fleet mechanism: SQL arbitration, one sweeper per pool, the maintenance step, the protect callback's single-node-only posture)
- docs/architecture/decisions/013-family-composition-reactive-boundary-and-feature-graph.md §2 (the named exclusive sweep lease — the semantics this implements)
- docs/architecture/decisions/012-pre-decomposition-consistency-rulings.md §1 (abort-as-data), §5 (the one-window-host rule), §6.4 (the full protection set), §6.6 (the fleet liveness verb's API home), §6.7 (the executor is the next sweep)
- docs/architecture/decisions/010-backend-io-seams-and-fleet-state-home.md §2 (the seam clauses; the raw-SQL rejection)
- tasks/store-sweep-durable-window.md (the durable form's shape — the single-node structural twin)
- tasks/store-fleet-put.md (the joint puts whose rows this sweep arbitrates against)
Notes
To be filled by implementation agent
Summary
To be filled on completion