docs(architecture): ADR-008 — trait size probe, fleet GC, fs-tier engines; requirements anchor
- requirements.md (new): the three use cases pinned as REQ-1..4 with the node/pool/fleet/engine vocabulary defined once — ends per-session re-derivation of consumer facts. REQ-2 (replicator fleet over one shared pg pool, incl. large-blob serving) is recorded as a planning fact predating all POCs, on the operator's authority. - ADR-008: Backend trait gains size(key) length probe (before any backend ships data — ADR-002 one-way-door discipline); fleet GC mechanism (DB-backed pin rows committed atomically with entries, TTL+renewal semantics, liveness = embedder-owned table, protect callback is single-node-only, advisory-locked single sweeper, staged re-arbitrated delete window on both engines); fs tier becomes engine-selectable (local default; pg-lo named candidate) with the fleet locality contract (shared media or re-routing; mixed tiers are a documented deployment invariant, not a constructor-provable one). - poc-pglo-spec.md (new): POC #7 spec — pg Large Objects as the fs tier's pg-lo engine; instruments, decision gate, registered in phase-0.md OQ-BL-06. - ADR-003/005 status amendments point to ADR-008's extensions; specs ripple (backends/store-api/gc/ops/overview/README). - open-questions.md: deferral-policy header gains the decisions-vs- sequenced-work distinction; pg-lo's why-not-parked audit trail recorded. - research fixes: postgres POC renumbered #4->#5 to the canonical register (phase-0 OQ-BL-06), redb cross-refs fixed, thinking- artifact sentence in B1 replaced with the honest reading. Verification: docs-only change; reference-integrity sweep across the tree (ADR/REQ/POC refs resolve); architecture-reviewer pass on the delta — original 3 criticals addressed, its follow-up (fleet liveness form, pin TTL, staged-delete semantics, enforceability, shared-media caveats) fixed in this commit.
This commit is contained in:
1 parent
395d58cf3f
commit
281c37876e
16 files changed
+689
-88
No files matched your search
@@ -1,6 +1,6 @@
|
||||
---
|
||||
status: draft
|
||||
last_updated: 2026-10-01
|
||||
last_updated: 2026-10-03
|
||||
---
|
||||
|
||||
# Store API
|
||||
@@ -11,7 +11,9 @@ The store core's public surface: the typed facade above the backends
|
||||
that consumers (alkgit, alkfs, the ops module) program against. It owns
|
||||
typed keys and hashing (ADR-002), size-threshold dispatch (ADR-003),
|
||||
the put-pinning seam, and the liveness/sweep seam (ADR-005) — and
|
||||
nothing else: no paths, no manifests, no wire (ADR-004, ADR-001).
|
||||
nothing else: no paths, no manifests, no wire (ADR-004, ADR-001). The
|
||||
facade is **engine-blind** (ADR-007/008): which kv/fs engine a node
|
||||
runs changes nothing here except fleet-GC mechanism wiring (ADR-008).
|
||||
|
||||
## Public surface
|
||||
|
||||
@@ -57,7 +59,9 @@ nothing else: no paths, no manifests, no wire (ADR-004, ADR-001).
|
||||
primitive and the packfile-serving surface rides this). `EntryMeta`
|
||||
carries `len: u64` and the key's algorithm (the "type" from the
|
||||
store's perspective is the hash algorithm — there is no other type at
|
||||
this layer).
|
||||
this layer). `stat` rides the `Backend` trait's `size` method
|
||||
(ADR-008): an indexed-column probe on kv engines, a file stat on the
|
||||
fs `local` engine — never a value/content load.
|
||||
- **`read_range(key, range) → (slice, slice_digest)`** — slice plus an
|
||||
out-of-band slice digest: SHA-256 over the slice bytes, fixed by
|
||||
convention so callers and ops handlers agree without negotiation
|
||||
@@ -85,7 +89,12 @@ nothing else: no paths, no manifests, no wire (ADR-004, ADR-001).
|
||||
(finding 7) is why the verb/name is pinned here).
|
||||
- **`sweep() → SweepReport`** — explicit mark-and-sweep; embedders own
|
||||
the cadence (ADR-005, no ambient timers). A sweep with no registered
|
||||
liveness sources aborts without deleting — the safe default.
|
||||
liveness sources aborts without deleting — the safe default. Under
|
||||
a fleet (multiple nodes over one pool — requirements.md REQ-2),
|
||||
sweep/put/delete semantics are mechanism-extended by ADR-008: pins
|
||||
are DB-backed rows committed atomically with entries, one sweeper
|
||||
holds the advisory lock, and delete arbitration is SQL-level — the
|
||||
facade's invariant text is unchanged.
|
||||
|
||||
## Error model
|
||||
|
||||
@@ -96,7 +105,9 @@ tests (AGENTS convention 2). Distinguished failure families:
|
||||
- `Verification` — put/get hash-check failure (content ≠ key)
|
||||
- `Io(String)` — backend media failure (stringly because
|
||||
`std::io::Error` is not stable across versions)
|
||||
- `GcAborted` — a protection source failed; nothing deleted (ADR-005)
|
||||
- `GcAborted` — nothing deleted; sweep not run. Two cause classes
|
||||
(ADR-005: a protection source failed; ADR-008: another node holds
|
||||
the fleet sweeper lock) — the typed error distinguishes them.
|
||||
- `KeyInvalid` — malformed key bytes at the boundary (hashing doc)
|
||||
|
||||
Virgin-store semantics: read paths on a fresh store see absent/empty,
|
||||
@@ -117,18 +128,27 @@ lesson generalizes to any kv engine).
|
||||
## Invariants (the test gate)
|
||||
|
||||
1. Whole-blob put/get round-trips byte-identical under the canonical
|
||||
derivation, both tiers, both put paths (interop with real git
|
||||
remains the source of truth, per POC #1's lesson about hardcoded
|
||||
vectors).
|
||||
derivation, both tiers, both put paths, **and every shipped engine
|
||||
of both tiers** (kv: sqlite + postgres; fs: `local`; ADR-007/008)
|
||||
(interop with real git remains the source of truth, per POC #1's
|
||||
lesson about hardcoded vectors).
|
||||
2. Dedup: putting identical content twice (same or different path) is
|
||||
one pool entry.
|
||||
3. Stage hygiene: any failure mid-put leaves zero stage files; any
|
||||
success leaves exactly one committed entry.
|
||||
4. Sweep safety: with correct liveness registered, sweep counts are
|
||||
exact; with aborting sources, sweep deletes nothing.
|
||||
exact; with aborting sources, sweep deletes nothing. **Engine-gated
|
||||
form (ADR-008):** single-node postures assert this under the
|
||||
in-process protocol; fleet postures (shared-pool engines) assert it
|
||||
under DB-backed pins + the advisory-locked sweep, with the
|
||||
exact-count sweep-outcome shape run against dockerized postgres
|
||||
under `--all-features`.
|
||||
5. `list()` correctness is observable only through GC — list-related
|
||||
tests assert through sweep outcomes (POC #1 finding 2's lesson
|
||||
codified).
|
||||
6. `size`/`stat` on any engine returns the entry length without
|
||||
fetching content (asserted per-backend: kv probe, fs stat —
|
||||
ADR-008).
|
||||
|
||||
## Design Decisions
|
||||
|
||||
@@ -138,6 +158,7 @@ lesson generalizes to any kv engine).
|
||||
| [003](decisions/003-backend-contract-and-dispatch.md) | Dispatch | routing is a pure function of length; pre-threshold buffering |
|
||||
| [005](decisions/005-pooled-cas-and-gc-mechanism.md) | Pools & GC | pins, liveness seams, delete windows, sweep semantics |
|
||||
| [006](decisions/006-verification-posture-and-transfer-encoding.md) | Verification | whole-blob checks; slice digests out-of-band |
|
||||
| [008](decisions/008-trait-size-probe-fleet-gc-and-fs-engines.md) | Trait `size` + fleet GC | stat/accounting ride the trait's `size` probe; fleet pins/sweeps are engine-backed |
|
||||
|
||||
## Open Questions
|
||||
|
||||
|
||||
Reference in new issue
Block a user