SQLite commit-error-arm coverage (task sqlite-commit-error-arm): the wave-3 review gate's deferred test landed — failed_commit_replenishes_the_writer_slot drives a failing COMMIT through the engine's commit path and pins the review's code-read fix end-to-end: the error surfaces as the opaque Database carrying the SQLITE_FULL-shaped source chain, the failed connection is dropped and the writer slot replenished via the handle's reopen closure (the next begin_tx proceeds within a bounded timeout, no parking — the store-wide-livelock posture), no partial-commit residue (the dropped connection's uncommitted writes read back None), the post-failure commit is a real clean commit (the fault disarms on consumption), and auto-commit notify works afterward. Injection is a cfg(test) commit-fault seam in seam.rs — a per-store Arc<AtomicBool> arm (born disarmed, armed via arm_commit_fault, take() disarms on first consumption so exactly one commit faults) whose fabricated rusqlite SqliteFailure feeds the production commit error arm rather than replicating it; the PRAGMA max_page_count route was probed live against both WAL and DELETE journal modes first and rejected: SQLite checks the page-count limit at page-allocation time, so the squeeze always fails the growth statement (SQLITE_FULL/DiskFull on the first INSERT) and leaves no transaction active for COMMIT to fail — the arm is unreachable through PRAGMA-space (also probed: the pragma is per-connection, so pre-begin arming on the writer conn would have ridden into the tx conn; the route failed on error placement, not delivery). Mechanism choice and probes documented in the seam doc comment and the task Notes. Cross-test safety is per-store scoping; parallel stores never see the arm. Replay-proofed live: with the error arm's writer_reopen replenish temporarily removed the test fails (begin_tx parks past the 5 s timeout — the stranding the review identified), reverted it passes. Plumbing follows the pg-fix-forwarder-reconnect cfg(test) precedent: fields on SqliteStore/SqliteTxHandle and a begin param are cfg-gated, production builds compile the plain path. The waves-1-2 review's optional watcher reconnect-success add rides here (taken — recorded in Notes): reconnect_success_resumes_wake_delivery drives run_poll_loop through its existing open_conn_fn seam (same instrument as the W-1 failure test), with the db file present throughout because the vanished-file route cannot reach the success body (file reappearance trips the dead-man's identity switch first): initial open + first two reconnects fail by injection, the third reconnect succeeds, and a subsequent commit wakes on_change — the success arm's data_version re-baseline and restored delivery pinned. Watcher shape untouched. Verified: cargo test -p alkstore-sqlite green server-less (191 lib + 25 suite), workspace cargo test 399/0, clippy -D warnings, fmt clean
This commit is contained in:
1 parent
36023914b2
commit
0c7744977c
6 files changed
+320
-8
No files matched your search
@@ -1,7 +1,7 @@
|
||||
---
|
||||
id: sqlite-commit-error-arm
|
||||
name: SQLite engine — commit-error-arm coverage (wave-3 review's deferred test)
|
||||
status: pending
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: medium
|
||||
@@ -61,8 +61,85 @@ suggestion, not an order).
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
> Decisions of record the implementation made that the description
|
||||
> didn't pin:
|
||||
|
||||
- **Injection mechanism: the `cfg(test)` commit-fault seam, not the
|
||||
PRAGMA route — decided by live probe.** Both PRAGMA candidates were
|
||||
tested against real SQLite before choosing: in *both* WAL and DELETE
|
||||
journal modes, `PRAGMA max_page_count` squeezed to the current page
|
||||
count fails the first growth statement (`SQLITE_FULL` /
|
||||
`DiskFull` at page-allocation time), leaving "no transaction is
|
||||
active" for the subsequent `COMMIT` — the commit arm is unreachable
|
||||
through PRAGMA-space. (Also probed: `max_page_count` is
|
||||
per-connection, so arming on the writer conn pre-`begin_tx` would
|
||||
have ridden into the tx connection — the route failed on placement
|
||||
of the error, not on delivery.) The seam substitutes a
|
||||
`SQLITE_FULL`-shaped `rusqlite::Error` for **one** commit and feeds
|
||||
it into the *production* commit error arm — the test pins the real
|
||||
drop + `reopen` replenish code, not a replica.
|
||||
- **Scoping design** (the seam's cross-test-safety shape): the fault
|
||||
flag is a per-`SqliteStore` `Arc<AtomicBool>` (born disarmed, armed
|
||||
via the `#[cfg(test)]` `arm_commit_fault`, disarmed on first
|
||||
consumption — exactly one commit faults and every later commit is
|
||||
real, which the test also pins). Parallel test stores can never
|
||||
interfere. Plumbing follows the `pg-fix-forwarder-reconnect`
|
||||
precedent exactly: `#[cfg(test)]` field on `SqliteStore` and on
|
||||
`SqliteTxHandle`, `#[cfg(test)]` param on `SqliteTxHandle::begin`,
|
||||
cfg'd statement branches in `store.rs::begin_tx` and
|
||||
`src/tx.rs::commit` — production builds compile the plain path.
|
||||
- **Replay-proofed live**: with the commit error arm's
|
||||
`writer_reopen` replenish temporarily removed, the new test fails
|
||||
(the next `begin_tx` parks past its 5 s timeout — the stranding the
|
||||
review identified by code-read); reverted, it passes.
|
||||
- **The watcher reconnect-success add rides here — taken.** The
|
||||
vanished-file route the waves-1–2 review sketched could not reach
|
||||
the success body (on file reappearance the identity check reads a
|
||||
fresh `(dev, ino)` and the watcher dies through the dead-man's
|
||||
switch before the reconnect would matter). Instead the test drives
|
||||
`run_poll_loop` directly through the `open_conn_fn` seam the loop
|
||||
already takes — the same instrument as the W-1 failure test — with
|
||||
the db file present throughout (identity stable): initial open and
|
||||
first two reconnects injected as failures, third reconnect
|
||||
succeeds, and a commit from a writer connection then wakes
|
||||
`on_change` (proving the success arm re-baselines `data_version`
|
||||
and restores delivery). Watcher shape untouched.
|
||||
- `cargo build`; `cargo test -p alkstore-sqlite` green server-less
|
||||
(191 lib + 25 suite, both new tests included); workspace `cargo
|
||||
test` 399 passed / 0 failed (core 25 + harness 3; postgres 121 +
|
||||
25 suite + 9 schema; sqlite 191 + 25 suite); `cargo clippy
|
||||
--all-targets -- -D warnings`; `cargo fmt --check` — all green.
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
> What landed, verified how:
|
||||
|
||||
- **`alkstore-sqlite/src/seam.rs`**: the `#[cfg(test)]`
|
||||
`commit_fault` module — per-store `Flag` (`Arc<AtomicBool>`),
|
||||
`disarmed`/`arm`/`take` (disarm-on-consume), and the
|
||||
`SQLITE_FULL`-shaped fabricated `rusqlite::Error` — with the
|
||||
mechanism-choice rationale (the PRAGMA probes) in its doc comment.
|
||||
- **`alkstore-sqlite/src/tx.rs`**: `#[cfg(test)]` fault field on
|
||||
`SqliteTxHandle` + `begin` param; `commit`'s blocking body gains a
|
||||
cfg'd branch that feeds the fault into the existing production
|
||||
match — the error arm (drop + `reopen` replenish, `sqlite_error`
|
||||
mapping) runs unmodified.
|
||||
- **`alkstore-sqlite/src/store.rs`**: `#[cfg(test)]`
|
||||
`commit_fault` field on `SqliteStore`, the `arm_commit_fault`
|
||||
surface, and the cfg-branched `begin_tx` call.
|
||||
- **`alkstore-sqlite/src/store/tx_tests.rs`**:
|
||||
`failed_commit_replenishes_the_writer_slot` — failed `COMMIT` errs
|
||||
opaque `Database` with the SQLITE_FULL source chain; the next
|
||||
`begin_tx` proceeds (slot replenished, no stranding); no
|
||||
partial-commit residue; the post-failure commit is real and clean
|
||||
(fault disarmed); auto-commit `notify` works afterward.
|
||||
- **`alkstore-sqlite/src/substrate/watcher.rs`**:
|
||||
`reconnect_success_resumes_wake_delivery` — the waves-1–2 review's
|
||||
optional reconnect-success add, driven through the loop's
|
||||
`open_conn_fn` seam (injected failed opens, then a successful
|
||||
reconnect; wake delivery resumes and is pinned). Recorded as
|
||||
*taken* per the task's optional clause.
|
||||
- **Gates**: `cargo build`, `cargo test` (server-less: core 25 + core
|
||||
suite 25 + sqlite 191 + postgres 121 + 9 schema — 399 passed / 0
|
||||
failed, postgres harness skipped server-less), `cargo clippy
|
||||
--all-targets -- -D warnings`, `cargo fmt --check` — all green.
|
||||
Reference in new issue
Block a user