Repository navigation
[Bug] responses-state.json full-snapshot rewrite on a 2s debounce causes 34–137 GB/day of disk writes under agent traffic #1385
Description
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 servicestart --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;withMAX_STORED_RESPONSES = 1_000and 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.stringifyper 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:
- 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.
- Skip no-op rewrites — hash the serialized payload and skip the write when unchanged.
- Append-only journal + periodic compaction — mutations append deltas; the full snapshot is compacted on interval/shutdown. This also removes the
MAX_SNAPSHOT_REWRITE_ATTEMPTSrevision-race loop. - Make the debounce configurable (env var) so operators can trade durability of
previous_response_idchains 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
github-actions commentedon Aug 9, 2026
Automated translation bookkeeping — detected language: English.
github-actions commentedon Aug 9, 2026
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.
Please edit the issue to add:
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.