fix(coding-agent): incremental single-flight session metadata scans - #2043
fix(coding-agent): incremental single-flight session metadata scans#2043snimu wants to merge 4 commits into
Conversation
…ne shared in-flight scan per file readSessionInfo re-read every session file from byte 0 whenever (size, mtime) changed, streamed with no end bound past the stat snapshot on actively growing files, and let concurrent callers stampede duplicate scans of the same path. Scans now fold into a per-file accumulator resumed from the last consumed byte offset (rewrites detected by shrink, same-size mtime change, or a changed prefix tail), are bounded to the size seen at scan start, and concurrent readers share one in-flight scan. A torn trailing line folds into the snapshot only, never into the resumable accumulator. Fixes the defects reported in discussions #1536 and the per-child scan cost of #1671.
…d bound resumable scan state Review fixes for the incremental scan owner: a caller arriving after an append could join an earlier in-flight scan and observe the pre-append snapshot, so per-path scans now chain instead of joining (unchanged files settle with one stat in the cached-hit path); rename rewrites that grow the file while preserving the 16-byte tail window were resumed stale, so resume now also requires an unchanged dev+ino; and resumable accumulators are LRU bounded (1024 files) with eviction when a listed directory disappears, so transcript-sized scan state cannot grow the daemon heap monotonically.
|
Addressed all three review findings in 41427fa:
Bench after the changes (72MB fixture): 15 refresh cycles 0.1MB read / 44ms (main: 396MB / 634ms); 30 concurrent hot readers still collapse to one incremental scan. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 41427fa. Configure here.
…rify inode after each scan Review fixes for the resumable scan cache: the 1024-file LRU bounded entry count, not memory (a huge transcript retains one usage record per assistant message), and a listing larger than the cap evicted its own earlier entries, re-paying full rescans every refresh. The cache is now bounded by total retained usage entries (100k, roughly a few tens of MB worst case) with whole-state LRU eviction only while over the bound, so small states never thrash regardless of catalog size. And a rename rewrite racing a scan between the pre-scan stat and the reads could mix two files' bytes into one cached accumulator: the inode is now re-verified after each scan, discarding the state and rescanning once when it changed.
|
Addressed the three findings in 8467701:
Resource bounds remain unpinned by prior agreement pattern (no behavioral observable without instrumentation); the bound and accounting are stated in the code. |
…ts pins Comment blocks collapse to one- or two-line invariants; the concurrency pins merge into one serialized-scan test, the two rewrite-detection pins become one table (rename vs truncate mode), and the torn-tail pin folds into the resume test. Every fail-unfixed behavior keeps its assertion.

Purpose
Session-list metadata scans treated append-only session files as immutable documents: every (size, mtime) change triggered a full re-read from byte 0, the read stream had no end bound (so a scan of an actively-appended file chases the moving EOF and holds its FD), and concurrent callers of
readSessionInfofor the same path each opened their own duplicate stream. With ten daemon call sites (catalog list, RLM tree walks, ledger, supervisor) this is the read-amplification and FD-stampede half of the large-tree incidents.Mechanism
One owner per session file for metadata scanning, in
session-manager.ts:readLinesAsBuffersgained an optional{start, end}range), so a growing file cannot extend a scan.All ten
readSessionInfocall sites become readers of this one derivation; no call-site changes.Measurement (72MB fixture: 200 sessions + one 24MB hot session)
Validation
test/session-manager(157),rlm-ledger(31 withfile-lines) pass; rootnpm run checkpasses.LOC
Total src: +323/−138 (net +185); tests: +137/−1 (net +136).
Src +219/−139 (net +80): mechanism change in one owner — full-rescan cache replaced by resumable accumulator + single-flight; no deletions elsewhere. Tests +115.
Squashes discussion #1536 and the per-child scan cost of #1671.
Linear: RES-1272 https://linear.app/primeintellect/issue/RES-1272
Note
Medium Risk
Core daemon hot path for session catalog metadata; incorrect resume or torn-tail handling could show stale counts or usage, though rewrite detection and tests mitigate this.
Overview
Replaces full-file session-list metadata rescans on every size/mtime change with resumable per-file scan state in
session-manager.ts. Each path keeps a fold accumulator plus consumed byte offset, inode identity, and a short prefix tail so appends only read new bytes; shrink, inode/rename, or prefix mismatch forces a full rescan.readSessionInfonow serializes concurrent readers on the same path (queued follow-up scans) and caps retained scan memory via LRU eviction of whole states. Scans are bounded to the file size atstattime through optional{ start, end }onreadLinesAsBuffersand a newreadBytesSynchelper infile-lines.ts. Incomplete trailing JSONL lines contribute to the returned snapshot only, not the persistent offset, avoiding double-count when the line completes.Session directory listing drops scan state for removed or missing files. Tests cover concurrency, incremental resume, rewrite detection, and file recreation.
Reviewed by Cursor Bugbot for commit df032c1. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Add incremental resumable single-flight session metadata scans in
session-managerreadLinesAsBuffersand a newreadBytesSynchelper in file-lines.ts support byte-range reads, enabling prefix validation and range-bounded line streaminglistSessionsFromDirevict scan state for deleted or missing session files to prevent recreated paths from inheriting stale metadataMacroscope summarized df032c1.