Skip to content

feat(settings): WAL lifecycle probe for .qrate design (ASNT-16) - #18

Merged
devnull03 merged 3 commits into
mainfrom
worktree-buzzing-leaping-panda
Jul 9, 2026
Merged

devnull03 merged 3 commits into
mainfrom
worktree-buzzing-leaping-panda

Conversation

@devnull03

Copy link
Copy Markdown
Owner

Summary

  • Adds a standalone example probe (crates/settings/examples/qrate_wal_probe.rs) that opens a throwaway .qrate SQLite file in WAL mode in an OS-temp scratch dir and drives real process-level open/close/crash cycles.
  • Answers the question behind ASNT-16 (".qrate project file: SQLite container with clean WAL handling"): does the file stay single, or leave -wal/-shm siblings behind?

Findings

  • Clean close (any drop, with or without an explicit wal_checkpoint(TRUNCATE)) with zero remaining connections auto-deletes -wal/-shm. No manual checkpoint is strictly required, though it's cheap defensive insurance to keep.
  • Crash (hard taskkill /F) leaves -wal/-shm on disk. The next open auto-recovers them, and a subsequent clean close removes them again — no special crash-handling code needed, SQLite's WAL recovery is automatic and safe.
  • Cleanup needs ALL connections closed, not just the writer — a single idle second connection (e.g. a pooled reader) is enough to keep -wal/-shm around indefinitely. This is the decisive point against a connection-pool crate (r2d2/deadpool-style) for the project file: it favors the single-owned-connection pattern already used in crates/settings/src/db.rs (one connection / writer thread, short-lived reads), not a pool.

Design recommendation for the real ASNT-16 implementation

  • Keep this in crates/settings as a sibling module to the existing global-settings db.rs (e.g. project.rs), not a new crate — same dependency footprint (rusqlite, anyhow), same persistence-layer concern, just a different file/schema. A new crate only pays off if project-file code needs something the settings crate doesn't already pull in.
  • Reuse the db.rs background-writer-thread + mpsc pattern for writes to the .qrate file; avoid r2d2/deadpool pooling per the findings above.

Test plan

  • cargo fmt -p settings / cargo clippy -p settings --examples -- -D warnings clean on the new file
  • Ran probe through write/write-raw/crash/two-conn cycles manually, confirmed findings above
  • Not yet wired into the app — this is a throwaway research probe, not the real .qrate schema/implementation

Standalone example (crates/settings/examples/qrate_wal_probe.rs) that opens a
throwaway .qrate SQLite file in a temp scratch dir and exercises real
process-level open/close/crash cycles to answer: does a WAL-mode .qrate file
stay a single file, or does it leave -wal/-shm siblings behind?

Findings (see agent notes / Notion task memory log for full writeup):
- Clean close (any drop, checkpoint or not) with zero remaining connections
  auto-deletes -wal/-shm. No manual checkpoint required, though it's cheap
  defensive insurance.
- A hard kill (crash) leaves -wal/-shm on disk; the next open auto-recovers
  and a subsequent clean close removes them again. No special crash handling
  needed - SQLite's WAL recovery is automatic and safe.
- Cleanup requires ALL connections closed, not just the writer - a single
  idle pooled connection is enough to keep -wal/-shm around indefinitely.
  This rules out a connection pool (r2d2/deadpool-style) for the project
  file and favors the single-connection pattern already used in db.rs
  (one owned connection / writer thread, short-lived reads).
@notion-workspace

Copy link
Copy Markdown

@devnull03 devnull03 self-assigned this Jul 9, 2026
@devnull03
devnull03 marked this pull request as ready for review July 9, 2026 10:53
Copilot AI review requested due to automatic review settings July 9, 2026 10:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a standalone settings example binary to probe SQLite WAL lifecycle behavior for a prospective .qrate project file design, focusing on whether -wal/-shm siblings remain across clean exits vs hard crashes and under multiple open connections.

Changes:

  • Introduces qrate_wal_probe example with multiple modes (write, write-raw, hang, two-conn, etc.) to exercise WAL creation and cleanup behavior in a temp scratch directory.
  • Implements a minimal schema and repeated upserts to reliably generate WAL activity and allow inspection of on-disk artifacts across process lifecycles.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/settings/examples/qrate_wal_probe.rs
devnull03 added 2 commits July 9, 2026 20:04
Fixes CI's format-check job, which was already failing on main due to
workspace-wide formatting drift unrelated to this PR. Also fixes two
pre-existing clippy -D warnings hits in settings/src/lib.rs
(collapsible_if, needless_borrow) surfaced while verifying
`cargo clippy --workspace --all-targets` passes clean.
…ntax

CI's clippy step (-D warnings) was failing on unused-for-now scaffolding
(LabeledInput, LabeledSelect, several helpers.rs functions) that has no
consumer yet - the same deliberate ahead-of-use pattern already documented
on crates/workspace/src/panels/log_viewer.rs. Carve out dead_code
specifically rather than scattering #[allow(dead_code)] across every future
scaffolded item; every other clippy/rustc warning still hard-fails.

Also addresses a review concern that the WAL probe's checkpoint call
(via rusqlite's pragma_update, which emits `PRAGMA wal_checkpoint =
TRUNCATE`) might be a no-op since SQLite's docs primarily show the
function-call form `PRAGMA wal_checkpoint(TRUNCATE)`. Added a `pragma-syntax`
mode that runs both forms (plus rusqlite's pragma()/query_row paths) back to
back and verifies actual effects: all forms truncate the WAL file to 0 bytes
and durably persist data (checked from a freshly reopened connection, not
just the writing connection). Along the way found a real but harmless
SQLite quirk: TRUNCATE mode's own (busy, log, checkpointed) return row
always reports 0/0/0 regardless of syntax or actual frame count, while a
bare/PASSIVE checkpoint correctly reports real counts. Updated the `write`
mode's own assertions to check WAL file size instead of the checkpointed
count, which was trivially always-true under TRUNCATE.
@devnull03
devnull03 merged commit e18954c into main Jul 9, 2026
2 checks passed
@devnull03
devnull03 deleted the worktree-buzzing-leaping-panda branch July 9, 2026 12:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants