Skip to content

perf(subagents): bound repeated full-tree discovery for missing Codex rollouts #803

Description

@Juliusolsson05

Confirmed read amplification at 5d641845 (Node v24.14.1, macOS arm64). Priority: medium/high for large Codex session archives. All evidence uses empty synthetic rollout files, with no private transcript data.

Expected and actual behavior

A child whose rollout is not present yet should have bounded, shared discovery retries. Unrelated parent records should not cause fresh full-history filesystem traversals.

findFileContaining recursively calls readdir and then stat on every entry under the sessions root. readKnownChildren caches only successful paths. A missing child therefore re-walks the entire tree on every 1,200 ms tick and every parent JSONL entry, including unrelated token-count records. Each unresolved child performs its own traversal.

Reproduction and measurements

  1. Create a temporary directory named sessions, with 10 child directories containing 100 empty .jsonl files apiece (1,000 total). Ensure no filename contains missing-child.
  2. Instantiate the actual CodexSubAgentTracker and call observeParentEntry with a response_item/function_call_output whose output is {"agent_id":"missing-child"}, pointing at a parent path beneath that sessions root.
  3. Send zero or nine additional event_msg/token_count parent records immediately.
  4. Count node:fs/promises.readdir/stat calls using wrappers that delegate to the real filesystem, await outstanding discovery, then call refresh() three more times sequentially.
  5. Stop the temporary tracker and delete only the generated fixture tree.
Scenario readdir calls stat calls Wall time Process CPU time
One parent record, one missing child 11 1,010 13 ms 15 ms
Ten-record parent burst, same missing child 110 10,100 45 ms 116 ms
Three subsequent unchanged refreshes 33 3,030 not timed not timed

Times are one warm temporary-filesystem sample under concurrent machine load, not an app benchmark. Operation counts are the primary reproducible evidence. The timer was disabled only in this counting harness so it could not contaminate counts; production's periodic retry adds work. The three sequential retries demonstrate that fixing overlapping-refresh races alone does not eliminate this waste.

Impact and proposed next steps

Main-process filesystem work and callback/parse contention grow with total archived rollout count, missing children, and parent event volume. Delayed child creation or references to unavailable rollouts keep paying this cost while the tracker retains the child id. Exact production CPU share is unmeasured.

  • Prefer a shared root-scoped session-id-to-path index, or at least one coalesced directory discovery per retry window serving all unresolved children.
  • Cache misses with bounded backoff and prompt invalidation when the matching rollout is created. A miss must not be permanent: freshly spawned children must eventually appear.
  • Use directory entries with types to avoid a stat for every ordinary file, while preserving safe handling of symlinks and vanished entries.
  • Avoid initiating child discovery for unrelated parent records; retain prompt correlation/status repaint behavior.
  • Acceptance: 1,000 unrelated parent records in one retry window cause bounded discovery work, multiple missing children share the scan, and a child created after a miss appears within a documented latency bound.

Tradeoff: negative-cache lifetime exchanges discovery latency for reduced I/O. An event-backed index still needs a recovery path for missed filesystem notifications and rollover. Reuse existing path/index ownership where practical rather than creating another unbounded history cache.

Refs #103, #94. The earlier offset-tail and accumulator fixes remain useful; this is path discovery, not rereading known child transcript bodies.

Related correctness race: #802. Coalescing overlapping reads is necessary there; bounded discovery retries remain needed even after reads are serialized.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions