The wave-2 facade skeleton and routing (tasks/store-construction.md):
the public surface consumers program against, with the ops methods
still to land against it in store-read-paths/gc-protocol/store-put-paths.
- src/store.rs — the public `Store` type (engine-blind, ADR-007/008/
010) over crate-internal tier enums (`KvEngine`{mem}, `LargeEngine`{
local}): the engine-representation design point decided as concrete
+ enum dispatch, generic rejected (pub(crate) engines, no dyn);
the three constructor modes as named constructors — `mem_only`,
`kv_only_mem`, `dual_tier_mem_local` (the ADR-012 §6.3
dispatch-coverage shape) — with `StoreMode` introspection; the
write-side routing seam (`tier_for`), the read-side fall-through
order (`kv()` then `large()`, ADR-012 §6.1), and the `GcHome`
GC-state field; the ADR-012 §3 fleet declaration ruled deferred to
the engine waves (nothing fleet-valid is constructible yet)
- src/dispatch.rs — the pure routing function (`route`, at-or-below
→ kv), `DEFAULT_DISPATCH_THRESHOLD` (128 KiB) exported at the
crate root, boundary-value unit tests (exactly-at-threshold,
degenerate tunings, purity)
- src/gc.rs — `GcHome`: the store-instance GC-state home the
gc-protocol task fills (in-process; fleet state stays on the
engine-state seam per ADR-010 §2/ADR-012 §4)
- tests/conformance — the wave-2 binding made as anticipated: the
factory's `Store` associated type collapsed into the concrete
`alkblobs::Store` (`open` vends it directly), recorded in
factory.rs + README; the wave-1 mock probe retired, replaced by
mem-gated binding demo factories (binding.rs) over both
constructible shapes
Verification: `cargo test` (default), `cargo test --features mem`,
`cargo test --all-features` green (41/53/53 unit + conformance
targets green in all three shapes); `cargo clippy --all-targets
-- -D warnings` green in all three feature shapes; `cargo fmt
--check` green; `cargo doc --no-deps` adds no warnings beyond the
pre-existing private-intra-doc-links class.
180 lines
9.7 KiB
Markdown
180 lines
9.7 KiB
Markdown
---
|
||
id: store-construction
|
||
name: Store facade type — constructor modes and size-threshold dispatch
|
||
status: completed
|
||
depends_on: [engine-mem, engine-local]
|
||
scope: moderate
|
||
risk: medium
|
||
impact: project
|
||
level: implementation
|
||
tags: [wave-2, facade, dispatch]
|
||
---
|
||
|
||
## Description
|
||
|
||
Introduce the `Store` facade type (store-api.md §What this is) — the
|
||
public surface consumers program against — with its constructor modes
|
||
and the size-threshold dispatch. This task builds the *skeleton and
|
||
routing*: the ops methods land in `store-read-paths`, `gc-protocol`,
|
||
and `store-put-paths` against this type.
|
||
|
||
- **The `Store` type** (public, `src/facade.rs` or a new module —
|
||
implementer's call, one module per file): holds the kv-tier engine
|
||
and the large-tier engine (or none, per mode) plus the GC state home
|
||
the `gc-protocol` task fills. **Named design point — engine
|
||
representation**: a concrete `Store` with a crate-internal enum
|
||
dispatching over the closed five-engine set, vs a generic
|
||
`Store<Kv: Backend, Large: Backend>`. The conformance factory's
|
||
binding note (tests/conformance/factory.rs) anticipates the concrete
|
||
type ("most plausibly the associated type collapsing to the concrete
|
||
facade type across all factories"); engines are statically known and
|
||
in-crate (no `dyn` anywhere, wave-1 design point 3). Decide, record,
|
||
and keep the public surface engine-blind (ADR-007/008/010: which
|
||
engines a node runs changes nothing here).
|
||
- **Constructor modes** (ADR-011 §4; ADR-012 §6.3):
|
||
- **dual-tier** (the default): kv + large, size-threshold dispatch.
|
||
Wave-2 constructible shape: `kv = mem` + `large = local` — the
|
||
dispatch-coverage test shape ADR-012 §6.3 names (both tiers
|
||
load-bearing).
|
||
- **kv-only**: no large tier; over-threshold puts reject at put time
|
||
(ADR-012 §6.2 — the rejection behavior itself lands in
|
||
`store-put-paths`; this task carries the mode and its validation).
|
||
- **mem-only**: the mem engine alone, no dispatch threshold in
|
||
effect; never production.
|
||
- Mode machinery is built so the wave-3–5 engine constructors slot in
|
||
without reshaping (sqlite/postgres/pg-lo constructors are later
|
||
waves; only feature-gated constructors exist when their feature is
|
||
on).
|
||
- **Size-threshold dispatch** (ADR-003): routing as a **pure function
|
||
of content length** — same content ⇒ same length ⇒ same tier, no
|
||
content ever migrates. Default threshold **128 KiB**
|
||
(constructor-tunable; the conservative edge of the measured
|
||
~128–256 KiB crossover zone, POC #3 A4). The dispatch function is
|
||
pure and unit-tested (boundary values: exactly-at-threshold routing).
|
||
- **The dispatch seam** the ops tasks program through: tier selection
|
||
for a given length, and the fall-through order (kv first, then large)
|
||
the read paths and delete consume (ADR-012 §6.1's general rule).
|
||
- **Conformance factory binding**: bind `StoreFactory::Store` to the
|
||
real facade type (the skeleton's anticipated collapse to the concrete
|
||
type); if binding reveals a shape correction, make it and record it
|
||
in factory.rs + tests/conformance/README.md per the skeleton's note.
|
||
|
||
## Acceptance Criteria
|
||
|
||
- [ ] Public `Store` type with the three constructor modes; engine
|
||
representation decided and recorded (enum-dispatch vs generic)
|
||
with the public surface engine-blind
|
||
- [ ] Size-threshold dispatch: pure function of length, default 128 KiB,
|
||
constructor-tunable; boundary-value unit tests (at/below/above
|
||
threshold route deterministically)
|
||
- [ ] `kv = mem` + `large = local` dual-tier construction works (the
|
||
ADR-012 §6.3 dispatch-coverage shape); kv-only and mem-only
|
||
modes construct and validate
|
||
- [ ] `StoreFactory::Store` bound to the real facade; any shape
|
||
correction recorded in factory.rs + README
|
||
- [ ] `cargo test` green (default build: local ungated); `cargo test
|
||
--features mem` and `cargo test --all-features` green;
|
||
clippy/fmt gates green in all three feature shapes
|
||
|
||
## References
|
||
|
||
- docs/architecture/store-api.md (§What this is; §Public surface)
|
||
- docs/architecture/backends-and-dispatch.md (§Size-threshold dispatch; §Constructor modes)
|
||
- docs/architecture/decisions/003-backend-contract-and-dispatch.md (§Dispatch policy)
|
||
- docs/architecture/decisions/011-vocabulary-tiers-engines-and-fleet-state.md §4
|
||
- docs/architecture/decisions/012-pre-decomposition-consistency-rulings.md §6.2/§6.3
|
||
- tests/conformance/factory.rs (the binding note)
|
||
|
||
## Notes
|
||
|
||
- **Engine representation decided**: concrete `Store` holding two
|
||
crate-internal engine enums (`KvEngine`: mem [feature-gated];
|
||
`LargeEngine`: local [ungated]), not the generic
|
||
`Store<Kv, Large>` — the generic would leak `pub(crate)` engine
|
||
types through the public surface or force `dyn` (wave-1 design
|
||
point 3); the facade stays engine-blind (ADR-007/008/010) and the
|
||
conformance factory's binding note anticipated exactly this
|
||
collapse. Waves 3–5 slot in by adding an enum variant + a named
|
||
(feature-gated) constructor — no reshaping. Recorded in
|
||
`src/store.rs` module docs.
|
||
- **Constructor modes** land as named constructors, engines named by
|
||
the constructor (public surface can't take `pub(crate)` engine
|
||
handles): `mem_only()` / `kv_only_mem(threshold)` /
|
||
`dual_tier_mem_local(root, threshold)` (the ADR-012 §6.3
|
||
dispatch-coverage shape; fallible because the local root's creation
|
||
is I/O). Modes validate structurally (unrepresentable states
|
||
unrepresentable — dual-tier constructs the large engine itself) and
|
||
every instance carries its mode (`Store::mode() → StoreMode`,
|
||
public). The **fleet declaration (`fleet: true`, ADR-012 §3)
|
||
defers to the engine waves**: it is a constructor *error* for every
|
||
non-fleet-valid configuration, and none is constructible yet — the
|
||
ruling is recorded in store.rs docs rather than carried as a
|
||
dead-machinery flag.
|
||
- **Threshold shape**: `None`-vs-value is the mode's ruling (mem-only
|
||
= "no dispatch threshold in effect", ADR-011 §4), so the field is
|
||
always a value and mem-only's `threshold()` returns `None` by mode
|
||
branch — no unrepresentable `Option` state to `expect` away
|
||
(AGENTS convention 2). The pure function is `dispatch::route(len,
|
||
threshold)` with the pinned boundary reading **at-or-below → kv**;
|
||
`DEFAULT_DISPATCH_THRESHOLD` (128 KiB) is exported at the crate
|
||
root and constructors take the threshold explicitly (tunability
|
||
proven by tests at a non-default threshold).
|
||
- **Dispatch seam handed to the ops tasks**: write-side
|
||
`Store::tier_for(len)` (dual-tier pure routing; kv-only routes kv
|
||
for every length — the over-threshold *rejection* is the put seam's
|
||
behavior keyed off `threshold()`, ADR-012 §6.2; mem-only kv), and
|
||
read-side fall-through order as `kv()`-then-`large()` accessors
|
||
documented as ADR-012 §6.1's general rule. The engine enum's
|
||
`Backend` dispatch methods and the cursor/staging enum wrappers are
|
||
deliberately not built here — they are store-read-paths and
|
||
store-put-paths named design points (the public stream shape, the
|
||
declared-len mismatch case) and adding methods to the enums is
|
||
slotting in.
|
||
- **GC state home**: `pub(crate) struct GcHome` in `src/gc.rs`, held
|
||
as a `Store` field with a crate-internal `gc()` accessor — the
|
||
gc-protocol task fills it (pin registry, liveness sources, protect
|
||
callback, in-process window); fleet state explicitly never lands
|
||
there (ADR-010 §2 / ADR-012 §4 host rule).
|
||
- **Factory binding made and recorded**: the skeleton's `Store`
|
||
associated type is gone — `StoreFactory::open` vends the concrete
|
||
`alkblobs::Store` directly (`tests/conformance/factory.rs` + README
|
||
§The factory contract record the ruling); the wave-1 mock probe
|
||
retired, replaced by mem-gated binding demo factories
|
||
(`tests/conformance/binding.rs`) covering mem-only and the
|
||
dispatch-coverage dual-tier shape with isolation + idempotent
|
||
teardown.
|
||
- **Default-build note**: the kv engine set is empty until wave 3
|
||
(sqlite), so `Store` exists in default builds but is
|
||
unconstructible — constructors and construction tests are
|
||
`#[cfg(feature = "mem")]`; the `#![allow(dead_code)]` scope notes on
|
||
`dispatch.rs`/`store.rs` follow the house pattern and shrink as the
|
||
ops tasks land.
|
||
|
||
## Summary
|
||
|
||
Implemented the `Store` facade skeleton in `src/store.rs`: the public
|
||
engine-blind facade type holding the two crate-internal tier enums
|
||
(`KvEngine` {mem}, `LargeEngine` {local}) per the recorded
|
||
engine-representation ruling (concrete + enum dispatch, generic
|
||
rejected), the three constructor modes as named constructors —
|
||
`mem_only()`, `kv_only_mem(threshold)`, `dual_tier_mem_local(root,
|
||
threshold)` (the ADR-012 §6.3 shape) — with `StoreMode` introspection,
|
||
the write-side routing seam (`tier_for`) composing the pure
|
||
`dispatch::route` (default 128 KiB, constructor-tunable, at-threshold →
|
||
kv), the read-side fall-through order (`kv()` then `large()`, ADR-012
|
||
§6.1), and the `GcHome` GC-state field the gc-protocol task fills
|
||
(added to `src/gc.rs`). `dispatch.rs` grew the pure routing function
|
||
plus `DEFAULT_DISPATCH_THRESHOLD` with boundary-value unit tests
|
||
(exactly-at-threshold, degenerate tunings, purity). The conformance
|
||
factory binding collapsed the `Store` associated type into the concrete
|
||
facade (recorded in `factory.rs` + the suite README); the wave-1 probe
|
||
became mem-gated binding demo factories. Tests: mem-only/kv-only/
|
||
dual-tier construction + validation, threshold tunability at a
|
||
non-default value, dispatch boundary values, factory open/teardown
|
||
lifecycle in both constructible shapes.
|
||
|
||
Verification: `cargo test` (default), `cargo test --features mem`,
|
||
`cargo test --all-features` all green; `cargo clippy --all-targets
|
||
-- -D warnings` green in all three feature shapes; `cargo fmt --check`
|
||
green; `cargo doc --no-deps` clean of new warnings beyond the
|
||
pre-existing private-intra-doc-links class. |