Skip to content

perf(server): thread cost reads only that thread's transcripts - #219

Merged
tusharbhardwaj-bk merged 1 commit into
expbkmainfrom
t3code/perf-4-usage-cache
Sep 26, 2026
Merged

tusharbhardwaj-bk merged 1 commit into
expbkmainfrom
t3code/perf-4-usage-cache

Conversation

@tusharbhardwaj-bk

@tusharbhardwaj-bk tusharbhardwaj-bk commented Sep 26, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

threadUsage.get is requested by the chat header's cost chip (ThreadCostControl) whenever a thread is opened. In prod it averaged 7.6 s, with a 21 s max. Two costs, both in the fork block of UsageService.readThreadUsage:

  1. It listed every transcript modified since the thread started, in every provider's history, and parsed each one. Only then did it keep the records whose session id matched.
  2. It then re-encoded the whole in-memory scan cache and rewrote usage-scan-cache.json (23 MB in prod). That happens whenever anything changed, which is always while any session is streaming. The JSON.stringify alone blocks the event loop.

Fix

Only the thread's own files. New fork module usage/threadTranscriptFiles.expbkt3.ts. The provider session id is in the file layout, so the walk skips everything else before any stat or parse:

  • Claude: <projects>/<slug>/<sessionId>.jsonl, plus everything under <projects>/<slug>/<sessionId>/. Checked on this host: subagent transcripts live in <sessionId>/subagents/agent-*.jsonl and carry the parent's sessionId, so they still count.
  • Codex: rollout-<timestamp>-<sessionId>.jsonl. Checked on this host: the filename suffix equals the file's session_meta id.
  • Grok: keeps the full walk, but now passes fileName: "updates.jsonl" as the summary scan already does.

Cache write debounce.

  • A thread read rewrites the scan cache at most every 5 minutes; the first read still writes.
  • Dirty entries are written by the next summary scan (upstream path, unchanged) or by a new finalizer that flushes the cache when the service shuts down.

The edits stay inside the existing T3-CUSTOM(expbkt3) block of UsageService.ts, plus a marked import. The upstream summary scan is untouched.

Evidence

UsageService.expbkt3.test.ts, 2 tests.

Listing test:

  • Claude returns only proj-a/session-1.jsonl and proj-a/session-1/subagents/agent-1.jsonl, not another session's file.
  • Codex returns only rollout-…-thread-b.jsonl.
  • No session ids returns nothing.

Service test (TestClock):

  • The totals count session-1 and its subagent (12 tokens, not the other session's 100).
  • A second read after the transcript grows sees the new tokens (42) but does not rewrite the cache file.
  • After 5 minutes the next read rewrites it.
  • An unpersisted change is flushed when the service scope closes.

The whole src/usage/ suite passes (9 files, 93 tests). vp run typecheck in apps/server and lint are clean, and the fork-marker check passes.

After the expbkt3 deploy I will compare ws.rpc.threadUsage.get span times on expbkt3.

Model/harness: Claude Opus 5.5 via Claude Code in T3 Code.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

threadUsage.get (the chat header's cost chip) walked and parsed every
transcript modified since the thread started, then rewrote the whole 23 MB
usage-scan-cache.json on every call (7.6 s average, 21 s max in prod).

It now lists only the thread's own files (Claude <sessionId>.jsonl plus its
subagents folder, Codex rollout-*-<sessionId>.jsonl), and a thread read
rewrites the scan cache at most every 5 minutes; the service flushes the
cache on shutdown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L labels Sep 26, 2026
@github-actions

Copy link
Copy Markdown

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ℹ️ No successful main baseline artifact is available yet. This run establishes the initial measurement.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire — 13.8 KiB — 15.1 KiB ✅
Codex Thread snapshot wire — 7.3 KiB — 7.8 KiB ✅
Codex Live turn WebSocket wire — 6.5 KiB — 7.8 KiB ✅
Codex Live turn WebSocket decoded — 57.1 KiB — 66.4 KiB ✅
Codex Live turn messages — 9 — 21 ✅
Claude Total thread wire — 13.8 KiB — 15.1 KiB ✅
Claude Thread snapshot wire — 7.3 KiB — 7.8 KiB ✅
Claude Live turn WebSocket wire — 6.5 KiB — 7.8 KiB ✅
Claude Live turn WebSocket decoded — 57.9 KiB — 66.4 KiB ✅
Claude Live turn messages — 9 — 21 ✅

Baseline: unavailable · PR result: f12367a · 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: 114.9 KiB
  • Claude decoded thread snapshot: 115.6 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@tusharbhardwaj-bk
tusharbhardwaj-bk merged commit d14caa4 into expbkmain Sep 26, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants