The ADR-008 pg-lo admission POC ran in a standalone crate
(/workspace/alkblobs-pglo-poc): PgLoBackend over the ADR-003/008 trait
contract (including size), 10/10 exact-count sweep-outcome contract
tests, clippy/fmt clean; dockerized postgres:16-alpine on :15432,
POC #5 driver stack (tokio-postgres + deadpool) via SQL lo_* functions,
no new dependency.
Gate verdict: passed, with named deltas.
- Performance: durable put 60-65 MB/s at >=1 MiB, within 1.5x of — and
below 1 MiB beating — durable local fs on this fsync-slow disk;
cached gets 70-180 MB/s single-stream, ~0.7 GB/s aggregate over 16
readers (20-50x behind page-cache fs — the honest named delta)
- Contract: companion table is the list()/size()/CAS authority (never
the catalogs); stage-then-commit; GC-participating lo_unlink delete
- Handles: the tx-scoped descriptor is real but pool-compatible via
descriptorless lo_get(oid, off, len) windows — window gets keep
handle-acquire p99 at 1-6 ms under readers <= pool; held descriptor
is the fallback posture
- Vacuum: pg_largeobject pages churn-reused, never returned; tracked
by autovacuum; rel-size monitoring named as an ops requirement
- Crash/orphan: LO creation is transactional — kill/terminate
mid-write-tx leaves zero orphan pages; the only orphan class is a
committed LO bypassing the companion table (planted, reaped by the
~7 ms/oid sweep; committed content survives byte-exact)
- Harness lessons: lo_lseek is int4 — the 64 variants are the
>2 GiB discipline; shared-table parallel tests are unsound (per-test
CREATE DATABASE isolation)
Docs: new poc-pglo-findings.md; poc-pglo-spec.md status passed;
register OQ-BL-06 #7 marked passed; ADR-008 pg-lo bullet updated
(duplicate bullet removed) + backends-and-dispatch/open-questions
cross-references.
Verification: cargo test --release (10 passed), clippy -D warnings,
fmt --check in /workspace/alkblobs-pglo-poc.
- 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.
Dissolve OQ-10's circular "standing offer" framing and record the
engine decision the POC evidence already supported:
- ADR-007 (new): kv tier ships sqlite (default) + postgres (feature
`postgres`, default-off), constructor-selected per node; records the
deployment economics (classical kv+fs+relational downstream stack
collapses to one relational engine + fs), the pg impl contract
(POC #5 B5 posture deltas), the CI sweep-safety gate under
--all-features, redb ruled out (POC #6), and posture defaults as
deferred cost in the ADR-006 pattern
- ADR-003: amended status (engine pin widened by ADR-007); kv entry
notes the widened engine set
- ADR-004: reconciliation note — the "two backends" count is tiers,
not engines
- backends-and-dispatch.md: tier≠engine distinction, pg engine
section, co-tenancy note (same-file consumer writes share sqlite's
single-writer ceiling), substitution-door precedent
- open-questions.md: OQ-10 resolved-by-ADR-007, with the
why-this-is-not-Schrödinger's-code record (a deployment running
alkblobs cannot pre-exist the pg engine — OQ-09 precedent); parked
index narrowed to OQ-07/OQ-08
- overview.md: layer map + dependency posture reflect the `postgres`
feature; README.md: ADR-007 in tables + corollary applications
- phase-0.md / pg+redb findings: promotion + supersession notes
Trigger framing survives only as deployment guidance (which engine a
node chooses — topology, not throughput), not as a gate on the crate's
own work.
Verified: cargo test, clippy --all-targets -D warnings, fmt --check.
Docs-only change (Phase 1 architecture); no implementation yet.
The backends spec read as if the Backend trait were a general storage
abstraction and 'two backends, exactly' banned other storage in a
deployment — so a downstream wanting sqlite manifests appeared forced
into implementing a third backend, the exact weld ADR-004 forbids.
- backends-and-dispatch.md: state plainly that the trait is a CAS-tier
contract (immutable digest-keyed entries, GC-shaped list/delete) and
never a host for manifest/index storage; consumer storage is
beside-the-pool. 'Two, exactly' counts trait impls shipped, not
storage per deployment. kv: tier name vs pinned sqlite engine made
explicit.
- ADR-004: add the explicit corollary under the consumer-seams point.