Expand description
ADR-099 migration step 3 (sub-slice B2) — the atomic runner: the
synchronous commit-pass mechanism that applies a caller-supplied sequence
of prepared write plans (crate::atomic_plan) as ONE
SqlAccess::atomic_unit, under a per-op SAVEPOINT, committing every
plan or rolling back the whole unit.
This module is the mechanism only. It has no production caller in
B2 — no verb dispatch, no CLI --atomic surface, no daemon wiring.
Tests in this file are the only consumer; wiring a real per-verb
prepare step (ops → AtomicOpPlan) and the exec --ops-file --atomic CLI surface is ADR-099 migration steps 1 (cont’d) and 4 — the
B3 wiring point referenced throughout this file.
§The atomic-unit suspend-free invariant, restated at this seam
run_atomic_unit is the one place in this crate that builds an
AtomicUnitOp closure and hands it to SqlAccess::atomic_unit. That
trait method carries a hard contract (its own doc comment,
crates/khive-storage/src/sql.rs, and crates/khive-db/src/sql_bridge.rs
block_on_sync): the closure’s future must resolve on its first
poll — synchronous DML against the provided &mut dyn SqlWriter only,
never a real .await on embedding, ANN warming, or any other suspending
work. This module honors that invariant structurally, not by convention:
every statement the commit-pass closure below drives comes from
AtomicOpPlan::plan_statements (private — the runner’s own internal
flattening step), which can only ever produce
PlanStatements — plain parameterized SQL, the same shape ADR-099 D1’s
prepare pass produces for the v1 DML-only admissible verb set (ADR-099
D3). There is no code path in this module that can hand atomic_unit an
embedding call or any other suspending future — see the paired
suspend-trap tests at the bottom of this file for the two things this
promise is checked against: the real commit pass resolving on first poll
(the happy-path proof), and a hand-built closure that deliberately
suspends failing loudly through the exact same seam (the misuse-is-caught
proof).
§Two-phase shape (ADR-099 D1)
Only the commit pass (phase 2) lives here: given an already-prepared
Vec<AtomicOpPlan> (phase 1, the async prepare pass, is out of scope for
B2 — a test-only caller constructs plans directly), run_atomic_unit
opens one atomic_unit, applies each plan’s statements under a named
SAVEPOINT, and returns either every op’s collected
PostCommitEffects (phase 3, the async post-commit pass — this is the
B3 wiring point: nothing in B2 executes these effects, a test
consumer only drains the returned list) or the first op’s failure and its
index.
Structs§
- Atomic
Runner Error - A failure of the
atomic_unitseam itself — the storage layer refused or could not complete the call at all (read-only backend, no async runtime for the writer-task lookup, or anAtomicUnitOpthat violated the suspend-free invariant and got caught byblock_on_sync). Never returned for an ordinary op-level guard or SQL failure inside the unit — those surface asAtomicRunOutcome::RolledBack, not this error.
Enums§
- Atomic
OpFailure - Why a single op’s plan failed inside the commit pass (ADR-099 acceptance criteria: “the failing op index is recorded”).
- Atomic
OpPlan - One admissible op’s prepared write plan (ADR-099 D3’s v1 admissible verb
groups), ready for the commit pass. This is the
Vec<AtomicOpPlan>shaperun_atomic_unitconsumes — the runner is agnostic to which verb produced a given plan; it only needs each plan’s ordered statements (plan_statements) and any deferred post-commit effect (post_commit_effect). - Atomic
RunOutcome - The whole-unit outcome of a completed
run_atomic_unitcall — the commit pass ran to a clean, distinguishable verdict (never returned for a seam-level failure; seeAtomicRunnerErrorfor that case).
Functions§
- run_
atomic_ unit - Run
plansas ONE atomic unit (ADR-099 D1 commit pass): open a singleSqlAccess::atomic_unit, apply every plan’s statements under a namedSAVEPOINT(adr099_atomic_op_<n>), and commit all or roll back all.