76 lines
3.2 KiB
Markdown
76 lines
3.2 KiB
Markdown
---
|
||
id: sqlite-fix-watcher-death-subscribe
|
||
name: Dead-watcher subscribe hang + initial-stat self-kill (review 004 items 2+3)
|
||
status: pending
|
||
depends_on: []
|
||
scope: moderate
|
||
risk: medium
|
||
impact: component
|
||
level: implementation
|
||
tags: [review-004-fixes, sqlite-engine, watcher]
|
||
---
|
||
|
||
## Description
|
||
|
||
Two bundled watcher-state defects in the SQLite engine, per
|
||
`docs/reviews/004-pre-release-general-review.md` §"Findings recorded,
|
||
not fixed" items 2 and 3 (the review says bundle them — both are
|
||
watcher-state changes).
|
||
|
||
**(a) Post-watcher-death subscribe hang (item 2).** After the watcher
|
||
thread exits (db-file replacement), `subscribe()` still enrolls new
|
||
senders with no poller alive (`substrate/watcher.rs:391-396`): a
|
||
`listen()` or stream `subscribe()` on the dead store parks forever,
|
||
contradicting engine-sqlite.md's "on watcher death every subscriber
|
||
closes" posture — which is enforced only for *pre-death* subscribers.
|
||
Fix shape: a dead-store/dead-watcher sign that the new subscription
|
||
paths consult — a state-machine change across `store.rs`/`notify.rs`/
|
||
`stream.rs`. New subscriptions on a dead store must fail closed (a
|
||
typed error, the established failure vocabulary) rather than park.
|
||
|
||
**(b) Initial-stat failure self-kill (item 3).** An identity-`stat`
|
||
failure at watcher start seeds `(0, 0)` (`watcher.rs:164-170`); the
|
||
first *successful* later stat mismatches it and drives the death path,
|
||
spuriously closing every subscriber on recovery from a transient stat
|
||
hiccup. Rare, but the fix — an identity-unknown state instead of a
|
||
false identity — is the same watcher-state family: the watcher treats
|
||
identity as unknown until the first successful stat, and that first
|
||
successful stat must not trigger the death path.
|
||
|
||
**Test shapes**: (a) kill the watcher (the db-file replacement seam or
|
||
a `cfg(test)` death injection), then `listen()`/`subscribe()` — assert
|
||
the typed failure, not a park; the pre-death subscribers' close-at-
|
||
death pins stay green. (b) inject an initial stat failure, let the
|
||
first successful stat land, assert the watcher survives and
|
||
subscribers stay open.
|
||
|
||
## Acceptance Criteria
|
||
|
||
- [ ] Subscribing (listen or stream) after watcher death fails closed
|
||
with a typed error instead of parking — pinned by test
|
||
- [ ] Pre-death subscribers still close at watcher death — the
|
||
existing pins green unchanged
|
||
- [ ] An initial identity-stat failure no longer seeds a false
|
||
identity: the first successful stat does not trigger the death
|
||
path and subscribers stay open — pinned by injected-failure test
|
||
- [ ] engine-sqlite.md's watcher-death posture text covers the
|
||
new-subscriber arm (and the identity-unknown state if it changes
|
||
observable behavior)
|
||
- [ ] `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" items 2–3 (the findings, the fix shapes, the bundling)
|
||
- docs/architecture/engine-sqlite.md (the watcher-death posture)
|
||
- alkstore-sqlite/src/substrate/watcher.rs (both sites),
|
||
store.rs / notify.rs / stream.rs (the subscription paths)
|
||
|
||
## Notes
|
||
|
||
> To be filled by implementation agent
|
||
|
||
## Summary
|
||
|
||
> To be filled on completion |