Row-first gate: consumer-inventory.md gains an engine-tier section — the mem-engine need recorded operator-authority, dated 2026-10-10 (wasm32 sandboxing across the alk protocol family + downstream ffi/napi/python-adapter economics; rusqlite/tokio-postgres structurally out on wasm, so mem is the only triad member the sandbox can carry). ADR-024 (decisions/024-mem-engine.md) pins the decision set: (1) separate `alkstore-mem` crate — discharges and amends ADR-001 §4 (superseded in part: full contract-v1 engine, not an "implementation convenience"; annotation on ADR-001 §4 + Status, class-1 window still open); (2) honest-ephemeral posture — no durability, single-process, ephemeral by design, never fleet-valid (ADR-010 §3's shared-pool predicate fails structurally), riding ADR-016's carriers unchanged (identity + docs + matrix); (3) full contract v1 tier with the asymmetry classification — `PayloadTooLarge` never produced (SQLite arm), no reconnection concept (reconnect-wake/watcher rows absent from mem's per-engine column), receiver close at engine drop, wake coalescing N/A; (4) `MemStore::new()` engine-native constructor — the one documented exception to the open-prose, core-contract.md Store concept amended; shared-instance (two opens → one engine) rejected with the ADR-016 §4-style re-entry gate (consumer-inventory row), register stays closed; (5) wasm32 — compile-clean for wasm32-unknown-unknown plus the suite's mem column green on host under a current-thread runtime flavor are wave-6 acceptance items; on-target suite execution scoped out with a named collapse condition (wasm-bindgen-test adapter crate) — a scope decision, not a pending-client hedge; (6) mem is not a core dev-dependency — core's doc examples stay mock-based, ADR-001 §4's doctest posture declines. engine-mem.md (draft): the in-process mapping — guarded per-mechanism state (no pool, no schema bootstrap, no forwarder, no spawn_blocking seam), the tx overlay (staged *_tx effects merged atomically at commit; read-your-own-writes easy, drop-=-rollback trivial; wake-at-commit structural — the seam pg needed pg-fix-tx-wake for, mem pins from birth via the existing tx-commit-atomicity rows), ADR-012 §2 third owner of the curve/opts/@every arithmetic (three-way equivalence, ADR-022's StoreFactory reused verbatim — born pinned). deployment.md: host-semantics row (single-process, ephemeral, never fleet-valid, wasm32-clean), mem connection-budget subsection (none — nothing to budget), durability-knobs row (the honest row is the absence), toolchain row (wasm32 subset + current-thread-clean, acceptance-cited). README/overview: engine-mem.md row, ADR-024 row, family table entry (alkstore-mem, no driver deps), current-state updated to the implementation phase with engine specs' stable statuses reflected. implementation.md: wave table gains wave 6 (mem engine, depends waves 1 + 5) and renumbers release readiness to wave 7 (fuzzing decision — the one remaining decided-at-implementation deferral — rides wave 7); wave-6 section records the born-pinned posture, acceptance items, the one suite-harness pre-task (past_stamp_sleep blocking sleep → async sleep, review 003's properties.rs:1683 note), and the window riders (review 003 Finding 1: the pg transient-fault seam, MEDIUM, rides wave 6; Findings 2–3 follow their park into wave 7's pre-release); wave-6 review gate defined; Decided points updated (mem discharged, fuzzing renumbered); stale wave-6 references swept across review docs and the landed tasks' notes (release-readiness items renumbered or annotated). Docs-only change; verified by cross-reference audit (all ADR/OQ/anchor references resolve, residual wave-6 mentions are correct under the new numbering or carry dated annotations).
119 lines
5.6 KiB
Markdown
119 lines
5.6 KiB
Markdown
# ADR-001: Reactive-core crate + per-engine crates
|
|
|
|
## Status
|
|
|
|
Accepted *(capability-flags content annotated 2026-10-06 by
|
|
[ADR-016](016-deployment-honesty.md); §4 superseded in part
|
|
2026-10-10 by [ADR-024](024-mem-engine.md) — the mem engine exists as
|
|
the separate `alkstore-mem` crate, a full contract-v1 implementation,
|
|
not an implementation convenience)*
|
|
|
|
## Context
|
|
|
|
The store must present one reactive interface (notify, streams, queues,
|
|
locks, scheduler, outbox) over two backing engines, SQLite and Postgres.
|
|
The engines differ sharply in how much of that surface is natively
|
|
theirs:
|
|
|
|
- The **SQLite** engine rides honker's existing, battle-tested machinery
|
|
(published `honker-core` — a port-and-adapt of a sync library to the
|
|
family's async-facing-trait posture). *(Ownership since resolved:
|
|
the substrate is forked into owned code — [ADR-011](011-sqlite-substrate-fork.md).)*
|
|
- The **Postgres** engine is the build-heavy side: LISTEN/NOTIFY wiring
|
|
and the pg-boss-family queue schema are built by this crate.
|
|
|
|
A single crate with feature-gated engines would force both engines'
|
|
dependency graphs into every downstream binary that enables both
|
|
features. That is not hypothetical: rusqlite 0.40's `libsqlite3-sys`
|
|
link collision with sqlx's sqlite driver is a real constraint
|
|
(see [ADR-003](003-sqlite-driver.md)); single-driver binaries are the
|
|
only clean escape.
|
|
|
|
Scope evidence lives in `docs/research/consumer-inventory.md`; the
|
|
operator decision was recorded 2026-10-04 against
|
|
OQ-ST-02 (`docs/research/phase-0.md`).
|
|
|
|
## Decision
|
|
|
|
The project ships as a **family of crates**:
|
|
|
|
1. **`alkstore` (core)** — the crate carrying the unified trait
|
|
surface, types, error model, capability flags, and the contract
|
|
documentation. No driver dependencies. Compile-lean by construction:
|
|
the base crate has no engine machinery to keep out.
|
|
*(Annotated 2026-10-06: capability flags are not part of this
|
|
contents list as decided — [ADR-016](016-deployment-honesty.md)
|
|
resolves there is no runtime capability surface, by default ever;
|
|
the engine boundary lives at compile time and in the deployment
|
|
matrix.)*
|
|
2. **`alkstore-sqlite`** — the SQLite engine implementing the core
|
|
surface. Single driver: rusqlite + the forked honker-core
|
|
lineage, carried in-tree as the engine crate's substrate module
|
|
subtree ([ADR-003]; ownership per
|
|
[ADR-011](011-sqlite-substrate-fork.md); folded into the engine
|
|
crate per [ADR-013](013-fold-substrate-into-sqlite.md)).
|
|
3. **`alkstore-postgres`** — the Postgres engine implementing the core
|
|
surface. Single driver: tokio-postgres + deadpool-postgres
|
|
([ADR-004]).
|
|
4. **A mem-shaped engine** (in-tree or separate test crate) may exist as
|
|
a third implementation for tests and doctests, if the test story
|
|
turns out to want it. Treated as an implementation convenience, not
|
|
a contract artifact — decided at implementation time, not now.
|
|
*(Superseded in part 2026-10-10 by
|
|
[ADR-024](024-mem-engine.md): the mem engine exists as the
|
|
separate `alkstore-mem` crate — full contract v1, no durability,
|
|
single-process, ephemeral by design, never fleet-valid, wasm32
|
|
compile-clean by acceptance. Not a test convenience, and not a
|
|
core dev-dependency: core's doc examples stay mock-based.)*
|
|
|
|
Downstream consumers depend on `alkstore` plus exactly one engine
|
|
crate. A consumer binary links at most one driver.
|
|
|
|
## Consequences
|
|
|
|
**Positive**
|
|
|
|
- The engines' asymmetry of work is structural: the SQLite engine has a
|
|
small, mostly-wiring job; the Postgres engine carries the build-heavy
|
|
LISTEN/queue work. Neither is burdened by the other's dependencies.
|
|
- Any future engine (alkfs's in-tree needs, an ops-surface engine) is
|
|
additive — a new crate implementing the core traits — rather than a
|
|
feature-graph edit to one crate.
|
|
- Core-lean is structural, not a feature-discipline to be enforced by
|
|
review.
|
|
- The libsqlite3-sys link-collision class of problems is eliminated by
|
|
construction (single-driver binaries).
|
|
- Per-engine compilation/test gates are independent.
|
|
|
|
**Negative**
|
|
|
|
- A version-coordination duty: the core's trait surface is a contract
|
|
the engine crates must track. Version bumps in core must be adopted
|
|
by engines in lockstep when the contract changes (minor/major
|
|
discipline; no trait-default drift). *(Discharged 2026-10-06: the
|
|
duty's concrete shape is the versioning discipline —
|
|
[ADR-017](017-contract-versioning.md), OQ-10's resolution: the
|
|
core crate's semver is the contract version, change classes +
|
|
lockstep duties pinned, the pairing tracked by manifest pin +
|
|
version-stamped contract suite + docs; released engines on a
|
|
breaking change keep their pinned majors — nothing forced.)*
|
|
- Slightly more crate plumbing; naming/publishing overhead.
|
|
- Consumers who want *both* engines in one binary (rare, unsupported-by
|
|
design) cannot get a both-features build today.
|
|
|
|
## References
|
|
|
|
- OQ-ST-02 (`docs/research/phase-0.md`) — the decision record with
|
|
options considered.
|
|
- `docs/research/consumer-inventory.md` — the inventory whose lean
|
|
(single crate) was superseded, with the correction recorded there.
|
|
- [ADR-003](003-sqlite-driver.md) — the SQLite driver choice whose
|
|
link-collision constraint motivates this split.
|
|
- [ADR-004](004-postgres-driver.md) — the Postgres driver choice.
|
|
- OQ-10 (`docs/architecture/open-questions.md`) — trait-versioning
|
|
duties created here; resolved by
|
|
[ADR-017](017-contract-versioning.md); OQ-11 — the fork-follow-through
|
|
residue (per [ADR-013](013-fold-substrate-into-sqlite.md) §4).
|
|
[ADR-003]: 003-sqlite-driver.md
|
|
[ADR-004]: 004-postgres-driver.md
|