docs(tasks): decompose wave 2 — mem + local engines, facade, in-process GC, first suite columns
Nine tasks against the wave-1 contract surface as reviewed (no amendments from the gate): - engine-mem / engine-local (parallel, dependency-free): the two engines; local owns the stage-hygiene exact-count tests and the Drop-cancellation story the wave-1 review recorded - store-construction: the Store type, constructor modes, size-threshold dispatch; binds the conformance factory's Store associated type - store-read-paths ∥ gc-protocol: read ops with the ADR-012 §6.1 fall-through rule; the ADR-005 in-process protocol (pins, liveness, delete windows, abort-as-data, GcRefuse with the full protection set) - store-put-paths: the two put paths behind one signature, ADR-003's three numbered rules, pin-before-publish, Batch, kv-only rejection - suite-column-mem → suite-column-local: the reference column, then the dual-tier dispatch-coverage shape mirroring it - review-wave-2: the gate (facade/dispatch/GC vs ADR-003/005/012) Verification: dependency graph validated (all ids resolve, no cycles, topological order clean); docs-only change. Refs: docs/plans/implementation.md §Wave 2
This commit is contained in:
1 parent
c1d9494c11
commit
6bce301c55
10 files changed
+834
-2
No files matched your search
@@ -0,0 +1,120 @@
|
||||
---
|
||||
id: gc-protocol
|
||||
name: In-process GC protocol — pins, liveness, delete windows, sweep, direct-delete refusal
|
||||
status: pending
|
||||
depends_on: [store-construction]
|
||||
scope: broad
|
||||
risk: high
|
||||
impact: phase
|
||||
level: implementation
|
||||
tags: [wave-2, gc, facade]
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Implement the in-process GC protocol (ADR-005; abort-as-data per
|
||||
ADR-012 §1; non-SQL window staging per ADR-012 §5) on the `Store` type
|
||||
— the architecture's most intricate state machine, in its single-node
|
||||
form. The fleet extension (DB-backed pins, sweep lease, SQL
|
||||
arbitration) is wave 6; this task is the protocol the fleet form
|
||||
extends without changing its invariants.
|
||||
|
||||
- **Pin registry** (ADR-005 §Marks 2): per-key refcounts under the
|
||||
arbitration lock the sweep consults. `Pin` guards minted by the put
|
||||
path (`store-put-paths` wires the call sites; the registry and
|
||||
minting land here); a dedup re-put refcounts the existing entry — a
|
||||
dedup put's guard protects exactly as a fresh put's does. `Pin` drop
|
||||
decrements. **Named design point**: the guard's back-reference shape
|
||||
(how the public `Pin`/`Batch` types reach the registry — the wave-1
|
||||
types carry only the key today) and the lock granularity
|
||||
(`parking_lot` short-held per AGENTS convention 3, poisoning via
|
||||
`unwrap_or_else(|e| e.into_inner())`).
|
||||
- **`Batch` machinery**: a batch's pins drop together on batch commit
|
||||
or batch drop, in one ordered step under the arbitration lock; no
|
||||
early single-pin release. The **conversion ordering rule** (ADR-005,
|
||||
pinned in facade.rs docs): a pin's drop and its replacement's
|
||||
registration into a liveness source are one ordered step under the
|
||||
same lock — the registry needs the API shape that makes the ordered
|
||||
step expressible.
|
||||
- **Registered liveness sources** (`register_liveness_source`, the
|
||||
live-shared seam — ADR-005 §Decision): **named design point** — the
|
||||
source trait/callback shape. Live-shared means consulted at mark
|
||||
time and at delete-time arbitration, never snapshotted at
|
||||
registration (POC #1 finding 7's install-implies-copy failure is the
|
||||
pinning reason).
|
||||
- **Protect callback**: consulted pre-sweep; may add externally-known
|
||||
hashes or abort the run — abort is report data
|
||||
(`GcAbortCause::ProtectFailed`), never an error (ADR-012 §1).
|
||||
- **`sweep() → Result<SweepReport, Error>`** — the delete-window
|
||||
protocol (ADR-005 §Sweep):
|
||||
1. **mark**: enumerate the whole pool (both tiers' complete `list()`
|
||||
unioned); compute the live set from all three sources (registered
|
||||
sources, pins, protect callback); stage the candidate-dead set.
|
||||
2. **delete window**: per candidate, **arbitrate under the pin/
|
||||
liveness lock at delete time** (not once upfront) — pinned or
|
||||
re-observed-live cancels the candidate; otherwise proceed.
|
||||
3. **commit**: remaining candidates deleted in batches (~100/batch,
|
||||
iroh's proven shape); window closed.
|
||||
- **`Ok(SweepReport)` always** — sweeps are never errors; every abort
|
||||
is data in `aborted: Option<GcAbortCause>` (exactly three causes;
|
||||
`NoLivenessSources` is the safe default when zero sources are
|
||||
registered — nothing deleted). Exact counts in the report fields.
|
||||
- **Non-SQL window staging** (ADR-012 §5): `local`/`mem` stage
|
||||
candidates in-process and execute the window protocol **inline in
|
||||
the same sweep** — there is no durable window table on non-SQL
|
||||
engines, and the "executor is the next sweep" clause (ADR-012
|
||||
§6.7) does not apply to them.
|
||||
- The test-asserted property (ADR-005): a visible pool entry is never
|
||||
deleted while liveness protects it, and an in-flight put is never
|
||||
deleted by a sweep started before it committed — concurrency tests
|
||||
(sweep racing a pinned put) assert it.
|
||||
- **Direct `delete(key)`** (wired through the fall-through dispatch —
|
||||
arbitration-then-delete where the entry lives): refuses **any
|
||||
protected key** — pinned, registered-source-live, or
|
||||
protect-callback-named (the full protection set, ADR-012 §6.4) —
|
||||
with the typed `Error::GcRefuse`, nothing deleted; unprotected keys
|
||||
delete; absent keys carry `Err(Missing)`. The refusal is decided at
|
||||
the facade (the altitude discipline: `GcRefuse` never fires below
|
||||
the facade).
|
||||
- **`has`/`get` during a window observe no torn state** — existence
|
||||
flips atomically per key (immutable entries); document the posture
|
||||
the engines' CAS semantics already give.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Pin registry with per-key refcounts under the arbitration lock;
|
||||
dedup re-puts refcount; `Pin`/`Batch` drops release through it
|
||||
(batch drops in one ordered step)
|
||||
- [ ] `register_liveness_source` live-shared (consulted at mark and at
|
||||
delete-time arbitration, never snapshotted); source shape decided
|
||||
and recorded
|
||||
- [ ] Protect callback with abort-as-data (`ProtectFailed`);
|
||||
`NoLivenessSources` safe default; `sweep()` returns
|
||||
`Ok(SweepReport)` on every abort — never an abort error
|
||||
- [ ] Delete-window protocol: mark → arbitrate-at-delete-time →
|
||||
batched commit (~100/batch), with exact-count `SweepReport`
|
||||
fields (`candidates_staged`/`deleted`/`cancelled_by_arbitration`)
|
||||
- [ ] Direct `delete` refuses the full protection set with `GcRefuse`
|
||||
and deletes unprotected keys through the fall-through dispatch
|
||||
- [ ] Concurrency tests: sweep vs in-flight pinned put (the
|
||||
pin-before-publish property from the sweep side), exact-count
|
||||
outcomes, abort shapes
|
||||
- [ ] `cargo test` green; `cargo test --features mem` and `cargo test
|
||||
--all-features` green; clippy/fmt gates green
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/decisions/005-pooled-cas-and-gc-mechanism.md (the protocol + invariants)
|
||||
- docs/architecture/gc-and-namespaces.md (§Mark-and-sweep GC; §Delete windows)
|
||||
- docs/architecture/decisions/012-pre-decomposition-consistency-rulings.md §1 (abort-as-data), §5 (non-SQL staging), §6.4 (full protection set)
|
||||
- docs/architecture/store-api.md (§Lifecycle — sweep shape; §Invariants 4/5)
|
||||
- src/facade.rs (the pinned Pin/Batch/SweepReport lifecycle contracts this wires)
|
||||
- docs/research/poc-trait-dispatch-findings.md (findings 3/7/8)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
Reference in new issue
Block a user