SQLite engine open: SqliteOpts, connection architecture, spawn_blocking seam posture (task sqlite-engine-open-opts)
- SqliteOpts (poll_interval: Option<Duration>, None = 1 ms shipping default ADR-023 §4; max_readers, DEFAULT_MAX_READERS = 8; opts exemption from non_exhaustive per ADR-017 §3) - open(path, opts) -> Box<dyn Store>: writer slot, reader pool, SharedUpdateWatcher with fallible spawn mapped W-2-style into Error::Database; plain-path posture (ADR-023 §3) pinned by test - Substrate delta D-30: open_conn_bootstrapped — every connection (writer and pooled readers) carries the full bootstrap surface (pragmas, notify, alkstore functions, schema) per engine-sqlite.md; lineage opened readers pragmas-only. Registered in PROVENANCE.md - spawn_blocking seam helper + error mappings (string/rusqlite -> Database, source chains preserved); helper cfg(test)-gated until sqlite-engine-seam-tx wires the trait impls - Store trait stubbed with Database errors (no panics); close()/Drop join the watcher, clear subscribers (death-guard), close pool+writer - 15 new tests (open boot, watcher fanout/death-close, cadence accounting, plain-path literal filename, :memory:, pool bounding, drop teardown, seam smoke incl. panic mapping); gates green
This commit is contained in:
1 parent
5474141f2b
commit
7d400906f5
11 files changed
+937
-13
No files matched your search
@@ -1,7 +1,7 @@
|
||||
---
|
||||
id: sqlite-engine-open-opts
|
||||
name: SQLite engine — `open` constructor, `SqliteOpts`, connection architecture
|
||||
status: pending
|
||||
status: completed
|
||||
depends_on: [core-engine-value-constructors]
|
||||
scope: moderate
|
||||
risk: medium
|
||||
@@ -53,21 +53,21 @@ verify bootstrap ran, verify the watcher is up, verify opts flow).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `SqliteOpts` exists with `poll_interval: Option<Duration>`
|
||||
- [x] `SqliteOpts` exists with `poll_interval: Option<Duration>`
|
||||
(None = 1 ms default) and reader-pool sizing; documented
|
||||
- [ ] `open(path, opts)` boots the full connection architecture:
|
||||
- [x] `open(path, opts)` boots the full connection architecture:
|
||||
writer slot, reader pool, watcher (fallible spawn mapped to
|
||||
`Database`), substrate bootstrap on every connection
|
||||
- [ ] Plain-path posture holds: no URI flag re-introduced anywhere in
|
||||
- [x] Plain-path posture holds: no URI flag re-introduced anywhere in
|
||||
the open path (pinning test: a `?name=value`-suffixed path
|
||||
creates a file with that literal name — ADR-023 §3's backlog row)
|
||||
- [ ] `poll_interval` flows to the watcher config (config-surface
|
||||
- [x] `poll_interval` flows to the watcher config (config-surface
|
||||
accounting test — assert the watcher's configured cadence, not
|
||||
wall-clock timing; ADR-023 §4's backlog row)
|
||||
- [ ] Watcher death handling wired: subscribers' receivers close
|
||||
- [x] Watcher death handling wired: subscribers' receivers close
|
||||
(mechanism lands with `listen`, but the death-guard plumbing
|
||||
exists here)
|
||||
- [ ] `cargo test -p alkstore-sqlite`, clippy `-D warnings`, fmt clean
|
||||
- [x] `cargo test -p alkstore-sqlite`, clippy `-D warnings`, fmt clean
|
||||
|
||||
## References
|
||||
|
||||
@@ -79,8 +79,82 @@ verify bootstrap ran, verify the watcher is up, verify opts flow).
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
- **Concrete type + boxed constructor** (the task's implementer's
|
||||
choice): `open()` returns `Box<dyn Store>`; the concrete
|
||||
`SqliteStore` is also re-exported (pub) so tests and the later
|
||||
mechanism tasks can reach the machinery — consumers hold the trait.
|
||||
- **Substrate delta D-30** (registered in PROVENANCE.md): the engine
|
||||
spec's "each connection runs the substrate's bootstrap" obligation
|
||||
required reader opens to carry the full surface — the lineage
|
||||
opened readers pragmas+notify only (functions + schema lived on the
|
||||
writer alone). Added `open_conn_bootstrapped` (pragmas, notify,
|
||||
`attach_alkstore_functions`, `bootstrap_schema`) and switched
|
||||
`Readers::acquire`'s fresh-open path to it. Fresh connections only;
|
||||
the pool never re-bootstraps reused connections (readers stay
|
||||
read-mostly — writes ride the writer slot; the idempotent bootstrap
|
||||
would be harmless but pointless per-acquire).
|
||||
- **Zero `poll_interval` disposition**: `SqliteOpts::poll_interval =
|
||||
Some(ZERO)` fails at `open` with `Error::Database` whose source
|
||||
chain carries the substrate's "watcher poll interval must be
|
||||
positive" (the W-2 mapping posture — engine-layer mapping of the
|
||||
substrate's string into the taxonomy; no new variant, per ADR-008
|
||||
§5). Pinned by test at the source-chain level.
|
||||
- **`max_readers: 0` clamps to 1** (the substrate's existing `max()`
|
||||
discipline, inherited — not re-decided here).
|
||||
- **Defaults**: `DEFAULT_MAX_READERS = 8` (the lineage bindings'
|
||||
default, carried) — re-exported from the crate root like core's
|
||||
opts constants.
|
||||
- **Trait stubs**: all twelve `Store` methods return
|
||||
`Err(Database("… wiring lands with the … task"))` — nothing panics
|
||||
(family standard); the mechanism tasks replace them. `with_tx` is
|
||||
core's provided method — it surfaces the `begin_tx` stub naturally.
|
||||
- **`spawn_blocking` seam posture**: the private `blocking` helper
|
||||
exists in `seam.rs` (tokio promoted to a real dep, `rt`+`sync`) but
|
||||
is `#[cfg(test)]`-gated until `sqlite-engine-seam-tx` wires the
|
||||
trait impls — clippy `-D warnings` rejects dead code, and an
|
||||
ungated unused helper would trip the gate every intermediate commit.
|
||||
Its shape is pinned by a smoke test (panics inside bridged closures
|
||||
map to `Database` — no panics cross the seam) and the seam task
|
||||
drops the gate. `database_error(msg)` (string→`Database`, io::Error
|
||||
source) and `sqlite_error(e)` (`rusqlite::Error`→`Database`, source
|
||||
chain preserved) are the two mappings the mechanism tasks reuse.
|
||||
- **`SqliteStore` accessors deliberately absent**: the test module
|
||||
lives under `store::` and reaches the private fields directly;
|
||||
`close()` is pub (explicit close, idempotent, `Drop` delegates).
|
||||
`Debug` is hand-implemented (`finish_non_exhaustive`) — no derive.
|
||||
- **Watcher death plumbing verified at the substrate seam**: the open
|
||||
tests subscribe through the store's `SharedUpdateWatcher` and
|
||||
assert receivers disconnect on close (and on drop's teardown); the
|
||||
file-replacement death path is already pinned substrate-side
|
||||
(`shared_update_watcher_signals_subscribers_on_watcher_death`).
|
||||
- **`store/open_tests.rs`**: `open` returns `Box<dyn Store>`, so the
|
||||
tests use `open_store` (crate-private, returns `SqliteStore`) — the
|
||||
boxed public constructor itself is exercised by the reopen/close
|
||||
tests.
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
Stood up `alkstore-sqlite`'s public surface: `SqliteOpts`
|
||||
(`poll_interval: Option<Duration>` with None = the substrate's 1 ms
|
||||
shipping default via `WatcherConfig::with_poll_interval`; `max_readers`
|
||||
with `DEFAULT_MAX_READERS = 8`; not `#[non_exhaustive]` per ADR-017
|
||||
§3's opts exemption; `Default`+`Clone`), the `open(path, opts) ->
|
||||
Result<Box<dyn Store>>` constructor booting engine-sqlite.md's full
|
||||
connection architecture (writer slot, reader pool,
|
||||
`SharedUpdateWatcher` with fallible spawn mapped W-2-style into
|
||||
`Error::Database` — open fails if the watcher cannot start), and the
|
||||
`spawn_blocking` seam's helper + error mappings. Required substrate
|
||||
delta D-30 (`open_conn_bootstrapped` + reader-pool open path carries
|
||||
the full per-connection bootstrap — pragmas/notify/functions/schema —
|
||||
per engine-sqlite.md's "each connection runs the substrate's
|
||||
bootstrap"; the lineage opened readers pragmas-only), registered in
|
||||
PROVENANCE.md. Store wiring: `close()`/`Drop` join the watcher,
|
||||
clear subscribers (death-guard), close pool + writer slot. Trait
|
||||
methods are `Database`-error stubs pending the mechanism tasks. 14
|
||||
new engine tests (full-boot, watcher fanout, death-close, cadence
|
||||
accounting ADR-023 §4, plain-path ADR-023 §3, `:memory:`, pool
|
||||
bounding + 0-clamp, read visibility, close/reopen, drop teardown,
|
||||
seam smoke incl. panic mapping, unopenable-path failure) + 1 substrate
|
||||
test. Verified: `cargo test` (workspace: 25 core + 3 suite + 101
|
||||
sqlite), `cargo clippy --all-targets -- -D warnings`, `cargo fmt
|
||||
--check` all clean.
|
||||
Reference in new issue
Block a user