perf(shared): cached SQLite statements no longer hold large parameters#19305
Takeover of #17785 by @tris203, which conflicted with the reader worker from #19226. Their two commits are kept as authored. All credit for the diagnosis and the fix goes to them.
Problem
The SQLite client caches up to 200 prepared statements. SQLite keeps a copy of a statement's last text and blob parameters until it is bound again or finalized, and node:sqlite cannot clear them. So each cached statement pins the last payload it ran with, as native memory that shows in RSS but not in the heap. The reader worker added in #19226 has its own statement cache, with no TTL, so it has the same problem.
Fix
- From #17785: an execution that binds more than 64 KiB of text and blob data runs on a throwaway statement instead of a cached one. Small executions use the cache as before. The values path now sets its integer mode from the caller's
SafeIntegers, so a throwaway statement reads integers the same way a cached one does. - New here: reader worker requests apply the same rule, and ask the worker for an uncached statement when the parameters are large.
- New here: the size check reads only data properties of a named-parameter object, so getters still run once, and it counts raw
ArrayBufferblobs.
Proof
- Repro on Node 24.21, one process per case: 32 cached statements run 4 times with 1 MiB strings held +34.8 MiB RSS. The same work on throwaway statements held +2.0 to 2.4 MiB.
vp test run src/nodeSqliteClient.test.ts: 25 passed. With main'snodeSqliteClient.ts, the new tests fail. Each review fix also has a test that fails without it. With only the worker line reverted, the new worker test fails ([true, true]instead of[true, false]).tsc-rs --noEmitinpackages/shared, plusvp lintandvp fmt --checkon both files: clean.
Co-authored-by: tris203 admin@snappeh.com
Created with Claude Opus 5.5 in Claude Code, running in T3 Code.
š¤ Generated with Claude Code
Checks
Approvability
Verdict: Approved at 332ddf6
Macroscope's review found this PR approvable ā This is a focused SQLite memory-retention fix that bypasses statement caching only for large or unmeasurable bindings while preserving normal caching and query behavior. Writer and reader-worker paths are covered by targeted tests, with no schema, deployment, security, billing, default, or static-analysis changes.
You can add or adjust custom eligibility rules. Learn more.
Added 4 commits
- perf(shared): reader worker no longer caches statements with large parameters36f5f64
- perf(shared): cached SQLite statements no longer hold large parameters57c8429
- test(shared): detached view test no longer depends on the Node version6f60b46
- fix(shared): large-binding check no longer runs getters or misses raw buffers332ddf6
No actionable comments were generated in the recent review. š
ā¹ļø Recent review info
āļø Run configuration
- Configuration used: Path: .coderabbit.config.ts
- Review profile: CHILL
- Plan: Team
- Run ID:
2f577f94-4291-4cce-9062-0c05493bac1a
š„ Commits
Reviewing files that changed from the base of the PR and between 173a97097659be5f77ab2c05cf5dfe84be3eb094 and 332ddf66ccd6194339d8de25563733c36096111f.
š Files selected for processing: 2
packages/shared/src/nodeSqliteClient.test.tspackages/shared/src/nodeSqliteClient.ts
Included review availability: This review used your included allowance. 6 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 10 reviews per hour.
š Walkthrough
The SQLite client now bypasses statement caches for oversized or unmeasurable bindings. Value-array execution also applies the current safe-integer setting.
Changes: SQLite binding and execution
Layer / File(s) Summary Measure bindings and select statements
packages/shared/src/nodeSqliteClient.ts,packages/shared/src/nodeSqliteClient.test.tsBindings over 64 KiB or with sizes that cannot be measured use uncached statements in the writer and reader paths. Tests cover large and small parameters, detached views, and parameter getters. Apply value-array safe-integer settings
packages/shared/src/nodeSqliteClient.ts,packages/shared/src/nodeSqliteClient.test.tsValue-array execution sets safe-integer mode from the current fiber context. Tests check number and bigint results for small and large text parameters. Priority: ā¬ļø Low
Estimated code review effort: 3 (Moderate) | ~20 minutes
Change: Bug fix
Suggested reviewers:
juliusmarminge
Merge Risk: āŖ Minimal Ā· up to 332dd
The changed SQLite paths have no identified issue requiring a fix before merge.
Pre-merge checks: Passed 3 Ā· Failed 1
Failed Failed checks: 1 warning
Check name Status Explanation Resolution Description check Warning The description explains the problem, fix, affected components, and verification results. It does not include the required Scope and approval section or provide explicit maintainer approval, triaged i⦠Add a Scope and approval section. Link the triaged issue or approval discussion with the relevant maintainer approval comment, or explain why this focused fix qualifies for an exemption. Passed Passed checks: 3 passed
Check name Status Explanation Linked Issues check Passed Check skipped because no linked issues were found for this pull request. Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request. Title check Passed The title clearly and concisely describes the main change: preventing cached SQLite statements from retaining large parameters.
Full details: Description check
Explanation
The description explains the problem, fix, affected components, and verification results. It does not include the required Scope and approval section or provide explicit maintainer approval, triaged issue details, or a valid exemption rationale.
- Fix all pre-merge checks with AI
⨠Finishing Touches
š Generate docstrings
- Commit to this branch
- Create a new PR
š§Ŗ Generate unit tests (beta)
- Commit to this branch
- 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.
Thread transfer impact
ā Thread transfer remains within every enforced ceiling.
| Provider | Metric | Main baseline | This PR | Impact | PR ceiling | |
|---|---|---|---|---|---|---|
| Codex | Total thread wire | 5.0 KiB | 5.0 KiB | 0 B (0.0%) | 6.8 KiB | ā |
| Codex | Thread snapshot wire | 3.8 KiB | 3.8 KiB | 0 B (0.0%) | 4.9 KiB | ā |
| Codex | Live turn WebSocket wire | 1.2 KiB | 1.2 KiB | 0 B (0.0%) | 2.0 KiB | ā |
| Codex | Live turn WebSocket decoded | 20.9 KiB | 20.9 KiB | 0 B (0.0%) | 29.3 KiB | ā |
| Codex | Live turn messages | 2 | 2 | 0 (0.0%) | 8 | ā |
| Claude | Total thread wire | 5.0 KiB | 5.0 KiB | 0 B (0.0%) | 6.8 KiB | ā |
| Claude | Thread snapshot wire | 3.8 KiB | 3.8 KiB | 0 B (0.0%) | 4.9 KiB | ā |
| Claude | Live turn WebSocket wire | 1.2 KiB | 1.2 KiB | 0 B (0.0%) | 2.0 KiB | ā |
| Claude | Live turn WebSocket decoded | 21.2 KiB | 21.2 KiB | 0 B (0.0%) | 29.3 KiB | ā |
| Claude | Live turn messages | 2 | 2 | 0 (0.0%) | 8 | ā |
Baseline: 173a970 Ā· PR result: 332ddf6 Ā· Source CI: success
Scenario and decoded snapshot size
10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
- Codex decoded thread snapshot: 108.5 KiB
- Claude decoded thread snapshot: 108.8 KiB
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.