fix(server): run windowed snapshots on the SQLite reader#21869

open50+
main
GitHub fork: fix/21840-snapshot-reader
opened5d agobySScottN-PV
View on GitHubTwo-way sync enabledPR last imported 2d ago

Problem

Opening a thread reads its bounded snapshot inside a writer transaction, so a long snapshot blocks thread creation, message dispatch, and other database writes. A file-backed WAL reproduction fails on main when another connection holds the writer.

Change

Route the entire bounded snapshot transaction through the existing SQLite reader helper. Anchor lookup, fork-parent projections, and snapshot sequence stay in one consistent transaction. Preserve the contributor’s implementation and regression test from this PR; the updated branch also includes current main.

Scope and approval

Fixes #21840 GitCafe: https://git.cafe/pingdotgg/t3code/issues/21840 Triaged bug via the automated issue flow. Accepted bug. Scope is bounded snapshot reader routing; query indexing and other projection reads are separate work.

Verification

  • Red at 9f597f2058: windowed snapshots return committed data while the writer is held fails with “snapshot must return while another connection holds the writer”; the cause is SQLite writer lock contention.
  • Green at 247703a96b: server vp test run src/orchestration-v2/ProjectionStore.test.ts src/orchestration-v2/ThreadSearch.test.ts passes 37 tests; shared vp test run src/nodeSqliteClient.test.ts passes 16 tests.
  • Both focused commands rerun after merging the contributor commit and main at b16843cb8d: 53 tests pass. Its tree equals the green-tested tree.
  • Scoped format, lint, and server typecheck pass. Two independent reviewers report no findings.
  • Before/after runtime transcript: isolated Linux dev server, real browser, same seeded stopped threads; authenticated bounded snapshot returns HTTP 200 on both revisions. The snapshot transaction changes from writer on main to reader with the fix.

Runtime excerpt (both enclosing bounded requests succeeded):

text
1before 8d3b061121: sql.transaction db.connection=writer durationMs=13.297433 exit=Success
2after b16843cb8d: sql.transaction db.connection=reader durationMs=32.628450 exit=Success

This server-only change has an empty visual fixture matrix. No render paths or wire contracts changed. The reporter’s 10 GB cold-cache latency was not reproduced; no latency improvement is claimed.

Model: GPT-6.1-Sol and Claude Opus 5.5. Harness: Codex and Claude Code.

Checks

All checks passed
Synced from GitHub

Approvability

Verdict: Approved at b0493ba

Macroscope's review found this PR approvable — This is a small, self-contained SQLite concurrency bug fix that reuses the existing reader transaction mechanism and adds a regression test for writer contention. Its runtime impact is limited to routing existing windowed snapshots to the reader when available, with the established writer fallback preserved.

You can add or adjust custom eligibility rules. Learn more.

Synced from GitHub

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Path: .coderabbit.config.ts
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: abc3c733-8f87-4463-96be-d5734ccd51bf
📥 Commits

Reviewing files that changed from the base of the PR and between b0493ba0411c0f8600be4dbb352dab49b639d812 and 135b85c12c9a782d7bc2cdf577b708e0c128c775.

📒 Files selected for processing: 2
  • apps/server/src/orchestration-v2/ProjectionStore.test.ts
  • apps/server/src/orchestration-v2/ProjectionStore.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

📝 Walkthrough

getThreadSnapshotWindow now uses Node SQLite’s read-only operator. A live SQLite test verifies that the snapshot succeeds during a writer transaction and uses the reader connection.

Changes: Snapshot reader connection

Layer / File(s)Summary
Read snapshots through a reader connection
apps/server/src/orchestration-v2/ProjectionStore.ts, apps/server/src/orchestration-v2/ProjectionStore.test.ts
getThreadSnapshotWindow applies NodeSqliteClient.readOnly. A live test holds a writer lock, reads the 76-row snapshot, checks the thread identity and title, and verifies the "reader" connection trace.

Priority: ⬆️ High

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: t3dotgg

Merge Risk: ⚪ Minimal · up to 135b8

The snapshot change is mergeable after normal checks; no actionable issue remains in the supplied evidence.

Security architecture risk: ⚪ Minimal · up to b0493

The change preserves the snapshot transaction and response contract while avoiding writer contention when a reader is available. No introduced security weakness was identified. Slow snapshots can still delay other work sharing the reader.

Retained concerns No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The changed execution path remains within the configured SQLite database and the same thread and fork-parent projection queries. It changes connection selection, not the requested data scope; the comparison shows no added credentials, service access or caller-controlled routing authority.

Resilience and Maintainability Implications

  • observed — Transaction acquisition makes permit waiting interruptible and registers release under an uninterruptible cleanup boundary. Existing shared-client tests cover reader failure/restart and interrupted startup recovery; the new projection test itself covers successful reading under writer contention, not those recovery cases.
Pre-merge checks: Passed 4
Check nameStatusExplanation
Title checkPassedThe title follows conventional commit form and names the main change: windowed thread snapshot reads now run on the SQLite reader connection instead of the writer. It matches the raw summary, which sh…
Linked Issues checkPassedDirectly linked issue #21840 requires snapshot reads to avoid holding the SQLite writer. The supplied PR evidence states that getThreadSnapshotWindow runs anchor lookup, projection reads, fork-paren…
Out of Scope Changes checkPassedThe whole-PR summary identifies one production change in ProjectionStore.ts and one focused regression test in ProjectionStore.test.ts. The production change implements the reader routing for #218…
Description checkPassedThe description includes all required sections: Problem, Change, Scope and approval, and Verification. It links the triaged issue and approval, explains the implementation, documents focused test resu…
Full details: Title check

Explanation

The title follows conventional commit form and names the main change: windowed thread snapshot reads now run on the SQLite reader connection instead of the writer. It matches the raw summary, which shows the ProjectionStore.ts read path switched to NodeSqliteClient.readOnly, and the PR objectives, which describe the same change.

Full details: Linked Issues check

Explanation

Directly linked issue #21840 requires snapshot reads to avoid holding the SQLite writer. The supplied PR evidence states that getThreadSnapshotWindow runs anchor lookup, projection reads, fork-parent reads, and MAX(sequence) through NodeSqliteClient.readOnly. The reviewed test holds BEGIN IMMEDIATE on another connection and verifies committed snapshot data and db.connection: reader. Calls inside a write transaction and reader fallback remain supported. The separate index-only cold-open suggestion is assigned to #21193 and is not required by #21840 for this PR.

Full details: Out of Scope Changes check

Explanation

The whole-PR summary identifies one production change in ProjectionStore.ts and one focused regression test in ProjectionStore.test.ts. The production change implements the reader routing for #21840. The test verifies WAL concurrency and reader selection for that behavior. No unrelated production behavior or unrelated test scope is shown.

Full details: Description check

Explanation

The description includes all required sections: Problem, Change, Scope and approval, and Verification. It links the triaged issue and approval, explains the implementation, documents focused test results and limitations, and identifies the models and harness used.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

Added 50 commits

  • fix(opencode): cancelled todos no longer keep the todo list running (#22938)2333f16
  • fix(web): copy to machine clones the fork, not its upstream (#22492)0f74ff8
  • fix(server): JetBrains IDEs open file links at the right column (#22826)1befc71
  • fix(server): sync pull request draft status changes (#22832)70e5e38
  • test(web): chat shortcut test types through the composer handle (#23126)bd6940b
  • Merge commit from forka12d6ed
  • fix(source-control): merge messages drop Claude Code's current claude.com footer (#23133)b5a226e
  • fix(build): pin Node 26.12.1 for development and standalone releases (#23165)ed9b7ca
  • fix(mobile): keep Duo toolbar actions clear of workspace tabs (#22976)9cdd059
  • fix(mobile): respect Duo safe areas in overlays (#22977)ec2fd4d
  • fix(mobile): retain resting scroll offsets through Duo inset changes (#22978)eec07b6
  • fix(mobile): use native full-width sheets on iPhone Duo (#22980)13eea3f
  • fix(mobile): deliver the first tab selection after Duo folding (#22979)61bdec0
  • fix(mobile): the pull requests tab's empty detail asks for a pull request (#23143)added62
  • fix(mobile): the iPhone Duo keeps list titles through folds and unfolds with its sidebar (#23144)801701d
  • fix(mobile): the iPhone Duo searches from a field at the bottom of its list (#23145)9fa551a
  • fix(mobile): a closed search no longer risks freezing the app (#23146)37d6fce
  • fix(mobile): folding the Duo keeps the thread's end or the reader's line in place (#23147)5ed2ea2
  • fix(mobile): Screens 5.0.0-t3.36 stops an iPhone Duo unfold crash (#23149)83a8501
  • fix(mobile): the iPhone Duo's search field and composer line up with its tab bar (#23148)e6c2aeb
  • fix(mobile): the iPhone Duo's inspector panes use the full column (#23150)89fe509
  • fix(gitcafe): show line counts on linked and listed pull requests (#22633)8acbefe
  • docs(import): correct conversation size and batch limits (#23226)49e97d4
  • fix(server): stop abandoned turns before continuing threads (#23169)6f6f68a
  • feat(server): export how long runs take and how they end (#22094)f8e4834
  • fix(mobile): new-thread heading clears the iPad header (#23206)6318bfc
  • fix(pi): fork completed turns while the next turn is running (#23052)aa0158d
  • fix(mobile): drafts swipe to discard and every row move animates (#23205)b295dd8
  • feat(server): webhook filters fan out over arrays with a [] path wildcard (#23282)defb7e6
  • fix(web): Arabic and Hebrew text reads and navigates right to left (#22777)3690c86
  • fix(client-runtime): refresh Git refs when the RPC session changes (#23314)a98ebde
  • fix(web): cap list layout passes to prevent React error 185 (#23315)05b697f
  • fix(mobile): align iPad composer without shifting on keyboard focus (#23355)e075957
  • feat(gitcafe): request reviewers and apply labels on GitCafe pull requests (#23034)6e3c5e6
  • fix(source-control): clones retry over HTTPS when the SSH key is refused (#23170)a49d18c
  • fix(mobile): the iOS composer reopens where you left a long draft (#23096)efa5381
  • fix(mobile): preserve new-thread drafts and native header layout (#23366)002e941
  • feat: ChatGPT Business usage limits offer a copyable admin request (#23361)6f75bcb
  • fix(web): paused sidebar videos stop reloading after every command (#23106)3b95e78
  • chore(typecheck): typescript is the tsc-rs drop-in, no effect-tsgo patch (#23023)4040867
  • fix(mobile): Dynamic Island status no longer wraps next to other Live Activities (#23327)96bae54
  • perf(web): streamed reply text no longer re-renders ChatView (#23117)83f118a
  • perf(web): visit marks during a reply no longer re-render ChatView (#23124)ee33c4c
  • fix(server): a Claude agent stops when the server that started it dies (#23389)5ffe045
  • fix(server): SSH hosts no longer keep every old 300 MB runtime after desktop updates (#23113)ff52547
  • fix(settings): older clients connect again after idle compaction modes (#23454)43879d9
  • fix(mobile): unify draft and thread navigation and composer behavior (#23467)8d3b061
  • test(server): windowed snapshots must not wait on the SQLite writer9f597f2
  • fix(server): windowed thread snapshots no longer wait on the SQLite writer247703a
  • Merge contributor snapshot reader fix and current main validationb16843c