Files
alkstore/tasks/sqlite-fix-seam-panic-safety.md
T

3.7 KiB

id, name, status, depends_on, scope, risk, impact, level, tags
id name status depends_on scope risk impact level tags
sqlite-fix-seam-panic-safety Panic-safety armor for the sqlite auto-commit seams (review 004 item 1) pending
moderate medium component implementation
review-004-fixes
sqlite-engine
seam

Description

Defense-in-depth panic-safety for the SQLite auto-commit seams, per docs/reviews/004-pre-release-general-review.md §"Findings recorded, not fixed" item 1. with_writer/with_reader/blocking_read (alkstore-sqlite/src/seam.rs, substrate/schema.rs) release their slot/pool connection only after the closure returns normally. A panic inside the closure skips the release: the single writer slot wedges forever (Writer::acquire waits on a condvar with no timeout), and an abandoned reader connection permanently shrinks the pool (Readers.outstanding never decrements on abandonment). The tx seam already owns this class (ConnLease); the auto-commit seams don't.

No reachable panic path today (the library is panic-free by the standing gates, and these closures take no consumer code), so this is armor, not exposure — but the fix needs abandonment plumbing on both Writer (drop + path-based replenish) and Readers (an outstanding decrement), which is a deliberate small design task, not an inline patch.

Design first: decide the abandonment mechanism of record before touching code, and record it in Notes. The established drop-guard idioms are the precedents: ConnLease (the tx seam's connection lease), the tick's poisoned-disposition replenish arm (review 004 inline fix 1), and cross-engine FanoutRelease (pg's forwarder guard). The natural shape is a guard whose Drop marks abandonment (poison / replenish / decrement) — Drop runs on unwind as well as return — but the design must pin which dispositions each seam's abandonment takes (writer: drop + replenish per the tick's established arm; reader: outstanding decrement, connection dropped not released).

Testability: the closures take no consumer code, so the panic must be injected — a cfg(test) panic seam inside a test closure (or a test-only closure that panics), with the unwind caught, asserting the slot/pool state after: the writer slot is grantable again and a full store operation succeeds; the reader pool's outstanding count is restored and a later reader acquires.

Acceptance Criteria

  • The abandonment design of record is decided and written into Notes (mechanism per seam, which dispositions, which precedents it follows) before implementation lands
  • A panic inside the with_writer closure leaves the writer slot grantable after the unwind — pinned by a test with an injected panic (slot re-acquired, full store operation succeeds after)
  • A panic inside the with_reader/blocking_read closures leaves the reader pool's outstanding count restored and a later reader acquires — pinned by test
  • Normal (non-panic) paths are behaviorally unchanged: the existing seam/slot/pool tests green unchanged
  • cargo test -p alkstore-sqlite, clippy -D warnings, fmt clean; workspace gates green server-less

References

  • docs/reviews/004-pre-release-general-review.md §"Findings recorded, not fixed" item 1 (the finding, the fix shape, the "deliberate small design task" scoping)
  • docs/architecture/engine-sqlite.md (connection architecture: writer slot, reader pool)
  • alkstore-sqlite/src/seam.rs, substrate/schema.rs (the three seams)
  • The tx seam's ConnLease and the tick's poisoned-disposition replenish arm (review 004 inline fix 1) — the established idioms

Notes

To be filled by implementation agent

Summary

To be filled on completion