Wave-3 decomposition: SQLite engine tasks (open/opts, seam+tx, mechanisms, integration, review gate) + core value-constructor pre-work; plan synced for waves-1-2 review + ADR-023 follow-through

This commit is contained in:
glm-5.3-flash committed 2026-10-08 10:57:29 +00:00
1 parent 44637eea5b
commit 1269246faf
11 files changed
+883 -5

No files matched your search

+102
View File
@@ -0,0 +1,102 @@
---
id: sqlite-engine-seam-tx
name: SQLite engine — `spawn_blocking` seam, `begin_tx`, `TxHandle`, writer-slot lease
status: pending
depends_on: [sqlite-engine-open-opts]
scope: moderate
risk: high
impact: component
level: implementation
tags: [wave-3, sqlite-engine, seam]
---
## Description
Implement the transactional seam — the highest-risk piece of the
engine and the shape every other mechanism task rides:
- The `spawn_blocking` bridge helper: every engine call (auto-commit
and tx ops alike) round-trips a blocking thread; rusqlite
connections are not `Send`-across-await. Establish the pattern once
(error mapping substrate `rusqlite::Error` → `Error::Database` with
the source chain preserved — ADR-008 §5's opaque fallback) and reuse
it everywhere.
- `begin_tx`: acquire the writer slot (substrate `Writer::acquire`),
open `BEGIN IMMEDIATE`, return the boxed `TxHandle`. The handle is
the **writer-slot lease** (ADR-007): holding it across `await`
points parks the writer — that is the design, not a defect.
- `TxHandle` impl: every `*_tx` method routes through the same
connection via `spawn_blocking` (thread-affinity is handled by the
slot lease — the connection moves with the handle, ops serialize on
it):
- Writes: `enqueue_tx` (stamps the engine's derived plain-queue
defaults 300/3/5/none — no-handle-open shape, ADR-020 §3a note),
`publish_tx`, `publish_with_key_tx` (key validation per ADR-015
§2), `notify_tx` (substrate's `notify()` SQL function inside the
tx — delivered only at commit), `save_offset_tx` (monotone —
substrate's upsert already is), `outbox_enqueue_tx` (derives the
`__alkstore_outbox:{name}` backing queue name engine-side and
stamps the outbox's 60/5/5 set — ADR-014 §1).
- Reads (ADR-021 §1, read-your-own-writes — they run inside the
caller's tx): `get_job_tx`, `get_offset_tx`, `read_since_tx`,
`read_from_consumer_tx`.
- `commit(self: Box<Self>)`: `COMMIT` + release the writer slot.
- **Drop = rollback** (ADR-021 §4): the handle's `Drop` impl issues
`ROLLBACK` and releases the writer slot — panic-safe (the drop runs
across unwinds; no panic may escape drop). This is the property
N-5's wave-5 probe will exercise against a real engine for the
first time — get the Drop impl right here.
- Name validation at every entry point via core's `validation` helpers
(shared-namespace kinds vs consumer-local; ADR-008 §4 — the
commit-atomic path must not bypass validation).
- Payload encoding: call core's now-fallible `encode_payload` at the
seam; `?` the `Codec` error (ADR-023 §1 — the engines' first
consumption of the typed signature).
All eleven `*_tx` methods are implemented **for real** in this task —
the substrate surface is fully ported (wave 2), so nothing stubs. The
opts-resolution helpers this introduces (delay-over-`run_at`,
relative-`expires`, the derived-default stamp sets) are the shared
engine-side arithmetic the auto-commit mechanism tasks reuse — one
owner per formula (ADR-012 §2), written once here. The mechanism tasks
then add what the tx path deliberately lacks: the auto-commit handles,
the extent/duration guards, the backoff curve, the receiver bridges,
and the scheduler runner.
## Acceptance Criteria
- [ ] `begin_tx` acquires the writer slot + `BEGIN IMMEDIATE`;
concurrent `begin_tx` blocks (slot lease) — tested
- [ ] All eleven `*_tx` methods implemented with entry-point
validation; writes commit-atomic, reads see the tx's own writes;
opts resolution (delay-over-run_at, relative-expires, derived
default stamp sets 300/3/5/none and 60/5/5) pinned by tests
- [ ] `commit` commits + releases the slot; drop (without commit)
rolls back + releases the slot — no-ghosts tested for job rows,
stream events, notifications, and offset saves
- [ ] Drop is panic-safe (a panic unwinding through a scope holding
the handle still rolls back and releases the slot)
- [ ] `with_tx` (core's provided method) works end-to-end on this
engine: Ok ⇒ commit, Err ⇒ rollback — tested
- [ ] `encode_payload`'s `Codec` error propagates from enqueue/publish
paths (typed, not silent)
- [ ] Substrate errors map to `Error::Database` with source chains
- [ ] `cargo test -p alkstore-sqlite`, clippy `-D warnings`, fmt clean
## References
- docs/architecture/decisions/007-transactional-seam.md
- docs/architecture/decisions/021-tx-reads-and-value-shape-fixes.md §1, §4
- docs/architecture/decisions/014-outbox-tx-enqueue.md §1
- docs/architecture/decisions/020-enqueue-opt-semantics-and-bridges.md §1–§4
- docs/architecture/decisions/023-fourth-review-round.md §1
- docs/architecture/engine-sqlite.md (Mapping the contract: begin_tx, handle ops, commit/rollback)
- alkstore/src/tx.rs (the trait to implement)
## Notes
> To be filled by implementation agent
## Summary
> To be filled on completion