Repository navigation
[Bug] responses-state.json repeatedly rewrites a near-24 MiB snapshot under sustained traffic #2460
Description
Client or integration
Codex App
Area
Proxy and routing
Summary
@bitkyc08/opencodex@2.31.0 still rewrites the complete responses-state.json snapshot on a fixed 2-second debounce. On one Windows host the file reached 25,165,601 bytes (approximately the 24 MiB snapshot budget). While the snapshot was near that size and actively mutating, process-wide Bun counters showed approximately 2.4–5.2 MB/s of write I/O and 20–50% of one logical core.
Those counters were not isolated to responses-state.json; the tagged source behavior plus the file's size/mtime establish the repeated full-snapshot rewrite mechanism. The process-wide samples quantify the host impact during the same active window, not an exclusively path-attributed SSD-write rate.
This is a properly templated re-report and current-version confirmation of #1385, which was closed automatically because it did not use a recognized issue template. The underlying design remains present in 2.31.0.
Reproduction
- Install and run
@bitkyc08/opencodex@2.31.0as a local proxy. - Route sustained Codex/agent traffic through it until the continuation-state snapshot approaches its 24 MiB budget.
- Monitor
~/.opencodex/responses-state.jsonsize/mtime together with Bun process write-I/O and CPU counters while responses continue mutating state. - Observe repeated atomic replacement of the complete near-24 MiB JSON snapshot after successive debounce/flush cycles.
Observed on this machine:
- Snapshot size:
25,165,601bytes. - Bun process-wide sampled write I/O during active state mutation: approximately
2.4–5.2 MB/s. - Bun process-wide CPU: approximately
20–50%of one logical core during the same active samples. - No path-filtered Procmon/ETW capture was taken, so the process counters may include other Bun I/O/CPU work.
- The file stayed near the designed cap, so each persistence cycle paid the complete retained-snapshot serialization and replacement cost.
Expected behavior:
Persistence cost should scale with changed state rather than the complete retained snapshot, or operators should have a supported way to trade continuation-cache durability against write amplification.
Potential directions (also consistent with #1385):
- adaptive/longer debounce based on snapshot size;
- skip identical payloads;
- append-only journal with periodic compaction;
- a supported configuration knob for snapshot cadence/budget;
- move continuation state to an incremental store.
Actual behavior:
The first state mutation while no flush is pending schedules a complete snapshot rewrite two seconds later; additional mutations coalesce into that pending flush. Under sustained mutation, later flush cycles continue serializing and atomically replacing the complete bounded snapshot.
Version
@bitkyc08/opencodex@2.31.0 (npm global installation).
Operating system
Windows 11 Home, build 10.0.26200.
Provider and model
Not provider-specific. This is local continuation-state persistence performed by the proxy.
Logs or error output
responses-state.json size: 25,165,601 bytes
sampled Bun process-wide write I/O while active: approximately 2.4–5.2 MB/s
sampled Bun process-wide CPU while active: approximately 20–50% of one logical coreSource behavior in v2.31.0:
- fixed
SNAPSHOT_DEBOUNCE_MS = 2_000; - approximately 24 MiB snapshot-entry budget;
JSON.stringifyof retained state followed by atomic replacement of the complete file.
Screenshots and supporting files
No private screenshots attached. The issue was diagnosed from file metadata sampling, process-wide I/O/CPU sampling, and the tagged v2.31.0 source.
Prior report: #1385 (auto-closed for missing template, not technically resolved).
Redacted configuration
{
"install_method": "npm -g",
"version": "2.31.0",
"service_backend": "scheduler",
"listen_port": 10100,
"providers": "redacted",
"credentials": "removed"
}Checks
- I searched existing issues and documentation.
- I removed secrets, tokens, account details, request credentials, and personal data.
Activity
coderabbitai commentedon Aug 23, 2026
📝 Issue Planner
Check the box below or use the
@coderabbitai plancommand to generate an implementation plan and prompts that you can use with your favorite coding assistant.🧪 Issue enrichment is currently in open beta.
To disable automatic issue enrichment, add the following to your
.coderabbit.yaml:💬 Have feedback or questions? Drop into our discord!
lidge-jun commentedon Aug 24, 2026
리뷰 · 우선순위 54 / 80
설명: 이 이슈는 이어가기 상태 스냅샷
responses-state.json이 고정 2초 디바운스로 통째로 다시 쓰인다고 한다. 한 윈도우 호스트에서 파일이 25,165,601 바이트, 곧 24 메비 예산 근처까지 컸다. 스냅샷이 그 크기에서 계속 바뀔 때, 프로세스 전체 번 쓰기 카운터는 초당 대략 2.4에서 5.2 메가, 논리 코어 하나는 20에서 50 퍼센트였다. 그 카운터는 이 파일만의 숫자가 아니다. 태그 소스와 파일 크기와 시각이 통째 다시 쓰기 길을 보여 준다. 프로세스 숫자는 같은 창의 호스트 부담이다. 경로만의 에스에스디 속도는 아니다. 이것은 1385 의 템플릿을 갖춘 재보고다. 1385 는 템플릿이 없어서 자동으로 닫혔다. 기술적으로 고친 것이 아니다. 작성자 버전은 엔피엠 전역 2.31.0 이다. 지금 CURRENTdevHEAD 는35a89903c이다. 이번 시간에 origin/dev 는 그대로다. 새 머지는 없다. package.json 은 2.27.0 이다. src/config.ts 는 3238줄이다. src/runtime 폴더는 지금 HEAD 에 없다.살아 있는 재현은 이렇다. 로컬 프록시로 코덱스 에이전트 요청을 오래 보낸다. 이어가기 스냅샷이 24 메비 예산 근처까지 큰다. 파일 크기와 수정 시각을 보고, 같은 창의 번 쓰기와 시피를 본다. 디바운스와 플러시가 돌 때마다 통째 스냅샷이 원자로 교체된다. 이 기계에서는 파일 25,165,601 바이트였다. 경로를 가린 프로크몬은 없다. 그래서 프로세스 카운터에는 다른 번 일도 섞일 수 있다. 기대는 바뀐 상태만큼만 쓰거나, 운영자가 이어가기 캐시 내구성과 쓰기 증폭을 바꿀 손잡이를 갖는 것이다. 실제는 대기 플러시가 없을 때 첫 변경이 2초 뒤 통째 다시 쓰기를 예약한다. 그 사이 변경은 그 플러시에 합쳐진다. 계속 바뀌면 다음 주기마다 예산만큼의 스냅샷을 다시 직렬화하고 원자로 바꾼다.
지금 HEAD 의 src/responses/state.ts 20줄은
SNAPSHOT_DEBOUNCE_MS = 2_000이다. 27줄은 스냅샷 전체 예산 24 메바다. 24줄 메모리 캡은 64 메바다. 스냅샷과 메모리는 다른 한도다. 760줄 근처 쓰기 함수는 고른 항목을 최신부터 담는다. 항목이 2 메비를 넘으면 건너뛴다. 합이 24 메비를 넘으면 멈춘다. 그 다음 793줄이 전체 제이슨을 문자열로 만들어 원자 파일로 바꾼다. 같은 내용인지 비교하지 않는다. 812줄 예약은 타이머가 이미 있으면 그냥 돌아온다. 대기 중에는 합쳐진다. 그런데 820줄 지금 쓰기가 끝나면 타이머가 비고, 다음 변경이 또 2초 통째 쓰기를 건다. 지속 변경에서는 2초마다 최대 24 메비를 다시 쓴다. 스필은 램을 줄인다. 디스크 스냅샷은 여전히 고른 항목 전체를 다시 쓴다. 이 설계는 지금 HEAD 에 있다.1385 는 템플릿 없어서 자동 닫힘이다. 이번 이슈는 템플릿을 갖췄다. 1385 를 다시 열지 말 것. 이번을 닫지도 말 것. 코더래빗이 중복이라고만 적었다. 그 봇 댓글은 기술 판결이 아니다. 고침 후보는 크다. 스냅샷 크기에 따라 디바운스를 늘리기, 같은 페이로드 건너뛰기, 추가만 하는 저널과 가끔 압축, 운영자 손잡이, 이어가기를 증분 저장소로 옮기기. 지금은 범위를 좁혀야 한다. 같은 페이로드 건너뛰기와 큰 스냅샷에서 디바운스를 늘리는 것이 가장 작다. 저널과 증분 저장소는 별 사이클이다. 손잡이는 기본값을 안전하게 둔 뒤에만 넣는다. 2459 윈도우 모듈 지도와 길이 다르다. 한 기차에 섞지 말 것. 프로세스 전체 쓰기 숫자를 이 파일만의 속도로 단정하지 말 것. 작성자도 그렇게 적었다.
사용자 길이로는 긴 세션에서 디스크가 2초마다 24 메비 근처를 다시 써서 노트북이 뜨거워질 수 있어서 54. 요청이 바로 502 로 죽지는 않는다. 카탈로그 팁은 Ox Alpha x-preview-f-free + deepseek-v4-flash-vision-exp. Cursor 정적 카탈로그는 opus-4-8-fast / opus-5-fast. 2334 CursorCredentialRouter 는 여전히 src/providers/cursor-pool.ts 모듈+테스트만 있고 어댑터에 연결되지 않았다. 2332 H2 는 discovery 전용. 2320 overflow + 2342 는 이미 dev. 2188 사이드카는 이미 dev. 2361
__omit__는 이미 HEAD 다. combo-stream-preflight.ts 는 지금 HEAD 에 있다. src/lib/bounded-body.ts 128줄 취소 없는 던짐과 2426 나가는 크기 가드는 그대로다. 2451 wait 허용 목록은 아직 하이픈이다. 이번 스냅샷 쓰기는 그 구멍들이 아니다.src/responses/state.ts 라인 20 - 디바운스가 고정 2초다. 스냅샷이 커져도 그대로다
src/responses/state.ts 라인 27 - 스냅샷 전체 예산은 24 메바다. 작성자 파일은 그 근처다
src/responses/state.ts 라인 793 - 고른 항목 전체를 문자열로 만들어 원자로 바꾼다. 같은 내용 비교가 없다
src/responses/state.ts 라인 812 - 대기 타이머가 있으면 합친다. 쓰기가 끝나면 다음 변경이 또 통째 쓰기를 건다
src/responses/state.ts 라인 24 - 메모리 캡은 64 메바다. 스필은 램을 줄인다. 디스크 통째 쓰기는 남는다
이슈 1385 - 템플릿 없어서 자동 닫힘이다. 다시 열지 말 것. 이번 이슈를 그 이유로 닫지 말 것
메인테이너의 판단이 필요한 지점
너의 추천
이슈는 열어 둔다. 같은 페이로드 건너뛰기와 큰 스냅샷의 더 긴 디바운스만 좁게 본다. 저널과 증분 저장소는 미룬다. 1385 를 다시 열지 말 것. 2459 와 2451 은 닫지 않는다. 호출 길을 넓히지 말 것. 라벨은 그대로 둔다. 프리뷰 배포가 아니다.
이 댓글은 grok-bot이 작성했습니다
fix(responses): stop rewriting an unchanged snapshot every two seconds
fix(responses): stop rewriting an unchanged snapshot every two seconds (
lidge-jun commentedon Aug 24, 2026
Landed via #2476 at 02c302a
Merge from upstream (#8)
fix(responses): stop rewriting an unchanged snapshot every two seconds (
fix(responses): stop rewriting an unchanged snapshot every two seconds (