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:
glm-5.3-flash committed 2026-10-03 03:41:49 +00:00
1 parent 395d58cf3f
commit 281c37876e
16 files changed
+689 -88

No files matched your search

+48 -15
View File
@@ -1,6 +1,6 @@
---
status: draft
last_updated: 2026-10-02
last_updated: 2026-10-03
---
# Backends and dispatch
@@ -39,10 +39,16 @@ is ever reachable through that door.
is also the persistence boundary — a kv row or an fs filename has no
schema-migration story, so the byte layout must be self-describing
(algorithm-tagged keys).
- **Methods:** `has` / `get` / `put` / `delete` / `list` / `name`.
Streaming put/get shapes ride the store layer's seams (store-api.md);
- **Methods:** `has` / `get` / `put` / `delete` / `list` / `name` /
`size` (the length probe added by ADR-008). Streaming put/get shapes
ride the store layer's seams (store-api.md);
backends provide byte- or handle-level primitives underneath
(`get` on the fs tier yields a file handle, not a loaded buffer).
`size(key)` returns the entry's length or `None` if absent —
metadata only, never a content fetch: kv engines probe the indexed
`size` column both row shapes carry (POC #5 B5); the `local` fs
engine stats the file. Store-level `stat` and pool accounting ride
this method.
- **`list()` complete by contract.** A *malformed* list is as deadly as
an incomplete one: list correctness is only observable through GC
(POC #1 finding 2 — the redb key-vs-value trap produced a sweep that
@@ -122,18 +128,43 @@ sweep-outcome CI gate, run against dockerized postgres under
near-linearly to ~37k puts/s at 24 conns — the only engine whose
throughput *increases* under concurrency.
### fs backend (feature `fs`, default-on)
### fs backend (feature `fs`, default-on); engines: `local` (default) + `pg-lo` (candidate)
Large blobs. Flat sharded layout: `{hex-prefix}/{hex-prefix}/{hash}`
sharding survives from iroh's conclusion (limits directory size on
huge pools); stage-then-commit-rename for the two-pass unknown-length
path; pread-based range reads (POC #3 finding A2 — local range serving
is sound, e.g. for packfiles).
Large blobs. Like the kv tier (ADR-007), the fs tier is **one contract,
multiple engines** — the medium is a per-node constructor choice
(ADR-008; REQ-2 in requirements.md makes the engine concept load-bearing
for fleets):
- **`local` engine** (the default; the ADR-003 backend as originally
specced): flat sharded layout:
`{hex-prefix}/{hex-prefix}/{hash}` sharding survives from iroh's
conclusion (limits directory size on huge pools); stage-then-commit-
rename for the two-pass unknown-length path; pread-based range
reads (POC #3 finding A2 — local range serving is sound, e.g. for
packfiles).
- **`pg-lo` engine** (candidate, *not shipped*; ADR-008): postgres
Large Objects as the fs tier's storage — the same engine-behind-
one-trait move as ADR-007, one tier over. Admission is gated on
POC #7's measured evidence and its own engine ADR (see
`docs/research/poc-pglo-spec.md`).
- **Fleet locality contract (ADR-008):** over one shared pool, the fs
tier is either shared media (every node's `local` root on the same
fleet-shared media — soundness caveats and the required media
guarantees in ADR-008) or re-routed (one storage node serves pool
large-blob content via the ops surface; client nodes are
kv-tier-only over pool content). Mixed per-node-local fs tiers over
one pool is the partitioning failure this contract exists to
prevent — a documented deployment invariant the constructor cannot
fully prove but must be declared against (ADR-008 names the
enforceable seam and the detection symptom).
### mem backend (feature `mem`, default-off)
`BTreeMap`-shaped ephemeral backend for tests and in-process
ephemerality. A testing/utility tier, never a production story.
Implements the full trait contract including `size` (trivially) — it
exists partly as the reference impl every other backend's conformance
tests can mirror.
## Size-threshold dispatch (ADR-003)
@@ -156,12 +187,13 @@ ephemerality. A testing/utility tier, never a production story.
The trait is open to future implementations (network stores, S3-like
tiers), but nothing in the current consumer set requires one, and the
contract is deliberately hostile to half-implementations (complete
`list()`, GC-participating `delete`). Any future backend — or third kv
engine — is a new ADR carrying its own sweep-safety story (ADR-007 is
the precedent for an engine addition: measured evidence first, impl
contract naming the durability/ops posture deltas). This crate's
roadmap is not blocked on one (see open-questions.md — alkfs intake
may name needs externally; OQ-08).
`list()`, GC-participating `delete`). Any future backend — or a third
kv engine or second fs engine — is a new ADR carrying its own
sweep-safety story (ADR-007 is the precedent for a kv engine
addition; ADR-008 named pg-lo as the candidate second fs engine with
POC #7 as its admission evidence — the door is exercised twice). This
crate's roadmap is not blocked on one (see open-questions.md — alkfs
intake may name needs externally; OQ-08).
## Design Decisions
@@ -171,6 +203,7 @@ may name needs externally; OQ-08).
| [004](decisions/004-two-backends-no-third.md) | Two backends, no third | scope boundary against manifest-layer absorption |
| [005](decisions/005-pooled-cas-and-gc-mechanism.md) | Namespace-blindness | backends see hashes only |
| [007](decisions/007-two-kv-engines-sqlite-and-postgres.md) | Two kv engines | sqlite (default) + postgres behind one trait; one relational engine + fs per node |
| [008](decisions/008-trait-size-probe-fleet-gc-and-fs-engines.md) | `size` probe + fleet GC + fs engines | trait length probe; DB-backed pins/advisory-locked sweeper for fleets; fs tier engine-selectable (`local` default, pg-lo candidate) |
## Open Questions