Skip to content

fix(cli): avoid full part-table scan in session model usage#11893

Merged
marius-kilocode merged 1 commit into
mainfrom
equatorial-licorice
Jul 2, 2026
Merged

fix(cli): avoid full part-table scan in session model usage#11893
marius-kilocode merged 1 commit into
mainfrom
equatorial-licorice

Conversation

@marius-kilocode

Copy link
Copy Markdown
Collaborator

Problem

Opening or switching sessions in the VS Code sidebar and Agent Manager stalls for several seconds before the transcript renders on large histories. The session header's per-model token usage breakdown is fetched on every session open, and its aggregation query re-derives the session family with an inline recursive CTE. SQLite cannot estimate the CTE size and chooses a plan that full-scans the entire part table, running json_extract on every row. The cost is proportional to total database size rather than the opened session, so it grows past a second on large histories (about 1.2s on a 3 GB, 425k-row database here). Because the query runs synchronously on the single-threaded server, it head-of-line blocks the otherwise fast messages request, so the transcript cannot load until the scan finishes.

Change

The family session IDs are already resolved separately just before the usage query runs. This scopes the aggregation to those IDs with a concrete IN (...) list instead of re-deriving the family inline, which lets the planner seek the existing part_session_idx and read only the family's own parts. The aggregation output is unchanged.

Impact

Session opening no longer blocks on a database-wide scan. The query drops from a full part scan (seconds, scaling with total history size) to an index seek (milliseconds, scaling with the opened session tree), and it no longer stalls message loading.

The regression was introduced by #11608 (the aggregation query) and made visible on the VS Code session-open path by #11746.

Comment thread packages/opencode/src/kilocode/session/model-usage.ts
@kilo-code-bot

kilo-code-bot Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
packages/opencode/src/kilocode/session/model-usage.ts 115 Unbounded IN (...) parameter list built from the family session IDs could exceed SQLite's bound-parameter limit for very large session families, throwing instead of degrading gracefully

The core fix is sound: scoping the aggregation to the already-resolved family IDs via a concrete IN (...) list lets the planner use part_session_idx instead of forcing a full part scan, and the existing test (test/kilocode/session-model-usage.test.ts) already exercises the family-scoping/exclusion behavior, confirming the aggregation output is unchanged. The change is confined to packages/opencode/src/kilocode/, so no fork-hygiene concerns apply, and a changeset is included with a clear user-facing description.

Fix these issues in Kilo Cloud

Files Reviewed (2 files)
  • packages/opencode/src/kilocode/session/model-usage.ts - 1 issue
  • .changeset/fix-model-usage-full-scan.md

Reviewed by claude-sonnet-5-20260630 · Input: 30 · Output: 10K · Cached: 671K

Review guidance: REVIEW.md from base branch main

@marius-kilocode

Copy link
Copy Markdown
Collaborator Author

Benchmark results

Profiled with the VS Code self-test harness against a real 3.1 GB / 425k-part history DB, identical scenario before and after the change.

Root cause confirmed by profiling

The webview renderer is idle-waiting on a cold session open, not CPU-bound. The messages endpoint itself returns in <30ms even for a 4 MB session. The stall came entirely from the per-model token usage query, which is fetched on every session open: its inline recursive family CTE made SQLite SCAN part (all 425k rows, json_extract per row), taking ~1.2s regardless of session size. Running synchronously on the single-threaded server, it head-of-line blocked the fast messages request.

EXPLAIN QUERY PLAN after the change: SCAN part becomes SEARCH part USING INDEX part_session_idx.

Server endpoint latency

Measurement Before After
model-usage, small session (7 msgs) ~1.2s 3ms
model-usage, large session (983K tokens) 1.2-2.3s 8-46ms
messages alone 9ms 11ms
messages fired concurrently with model-usage 1190ms (blocked) 7.6ms

End-to-end cold worktree open in Agent Manager (webview message timeline)

Event Before After
sessionModelUsageLoaded 2612ms 36ms
messagesLoaded (154 msgs) 2794ms 234ms

Transcript renders in ~234ms instead of ~2.8s. The token usage header still populates correctly (verified visually), and both model-usage unit tests pass, so aggregation output is unchanged.

@marius-kilocode marius-kilocode enabled auto-merge (squash) July 2, 2026 13:53
@marius-kilocode marius-kilocode disabled auto-merge July 2, 2026 13:54
@marius-kilocode marius-kilocode enabled auto-merge (squash) July 2, 2026 14:00
@marius-kilocode marius-kilocode merged commit a8b4b72 into main Jul 2, 2026
27 checks passed
@marius-kilocode marius-kilocode deleted the equatorial-licorice branch July 2, 2026 14:06
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