Skip to content

[Bug] responses-state.json full-snapshot rewrite on a 2s debounce causes 34–137 GB/day of disk writes under agent traffic #1385

Closed as not planned

Description

@trukhinyuri

Summary

Under sustained agent traffic, the proxy rewrites the entire ~/.opencodex/responses-state.json snapshot (bounded at 24 MiB, and in practice sitting right at that cap) on a 2-second debounce. Because the debounce re-arms after every state mutation, continuous traffic turns this into a full ~25 MB atomic rewrite every ~2–17 s, i.e. 34–137 GB of file-backed dirty writes per day on the host SSD. macOS flags the process for exceeding its disk-writes resource budget — I collected 19 disk writes resource-exception diagnostics for bun.exe in one week on this machine.

Environment

  • opencodex 2.11.1 (npm global install, launchd service start --port 10100)
  • macOS Tahoe 26.5.2 (arm64, Mac14,6 / M2 Max, 96 GB)
  • bun from the bundled @bitkyc08/opencodex/node_modules/bun
  • Workload: several concurrent Claude Code / Codex agent sessions routed through the proxy for multiple hours a day

Evidence

macOS disk writes resource-exception reports for bun.exe (/Library/Logs/DiagnosticReports/bun.exe_*.diag), 19 in 7 days. Representative Writes: lines:

8608.53 MB dirtied over 1991 s  (4324.74 KB/s avg)
34.38 GB  dirtied over 64613 s  (532.06 KB/s avg)
8593.88 MB dirtied over 1561 s  (5503.65 KB/s avg)
34.36 GB  dirtied over 9272 s   (3706.14 KB/s avg)
137.45 GB dirtied over 46925 s  (2929.11 KB/s avg)   <- 137 GB in ~13 h
2168.07 MB dirtied over 342 s   (6340.77 KB/s avg)   <- 6.3 MB/s peak

On this host responses-state.json is pinned at the snapshot cap (24,966,911 bytes, 104 states + spill files), and its mtime advances continuously while agents are active.

Mechanism (v2.11.1, src/responses/state.ts)

  • const SNAPSHOT_DEBOUNCE_MS = 2_000; (line 19)
  • const SNAPSHOT_TOTAL_MAX_BYTES = 24 * 1024 * 1024; with MAX_STORED_RESPONSES = 1_000 and a 1 h TTL — so under real traffic the snapshot quickly grows to ~24 MiB and stays there.
  • Every mutation path calls schedulePersist(); persistNow() → writeBoundedSnapshot() re-serializes all retained entries (JSON.stringify per entry for the byte cap, then again for the file) and atomically rewrites the whole file.

So steady-state write amplification is snapshot_size / debounce ≈ 24 MiB / 2 s ≈ 12 MB/s worst case; the observed 0.5–6.3 MB/s averages match (mutations pause between turns). The CPU cost of the double serialization on every flush is a secondary effect of the same design.

The code itself notes the snapshot is "a cache, not a source of truth", which makes the 2 s durability window look much stricter than the artifact warrants.

Suggested directions

Any of these would cut writes by 1–2 orders of magnitude:

  1. Raise/adapt the debounce — even a fixed 30 s bound caps steady-state writes at ~0.8 MB/s; an adaptive bound proportional to snapshot size (e.g. "don't spend more than N MB/min on snapshots") degrades gracefully.
  2. Skip no-op rewrites — hash the serialized payload and skip the write when unchanged.
  3. Append-only journal + periodic compaction — mutations append deltas; the full snapshot is compacted on interval/shutdown. This also removes the MAX_SNAPSHOT_REWRITE_ATTEMPTS revision-race loop.
  4. Make the debounce configurable (env var) so operators can trade durability of previous_response_id chains against SSD wear.

Workaround applied locally

Patched SNAPSHOT_DEBOUNCE_MS to 30_000 in the installed copy — write bursts dropped accordingly, chains still recover fine after a graceful restart. Happy to test a proper fix.

Activity

  1. github-actions commented on Aug 9, 2026

    @github-actions
    Contributor

    Automated translation bookkeeping — detected language: English.

  2. github-actions commented on Aug 9, 2026

    @github-actions
    Contributor

    Issue closed: use an issue template

    Thanks for taking the time to submit this issue. It was closed automatically because it was not opened with a recognized issue template (Bug report, Feature request, Documentation, or Provider compatibility). Freeform and API-opened issues cannot skip this check.

    • This issue does not use a recognized issue template.

    Please edit the issue to add:

    • Open a new issue with the Bug report, Feature request, Documentation, or Provider compatibility template.
    • Or edit this issue so the body uses the template section headings for the kind of report you are filing.

    See the Contributing guide for details.

    Once the report passes the automated checks, it will be reopened automatically unless a maintainer has changed its state.

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