Перейти к содержимому

Рост памяти на Windows

Некоторые пользователи Windows наблюдают, как процесс bun, на котором работает opencodex, разрастается до многих гигабайт RSS во время долгих streaming-сессий (issue #314). Эта страница честно объясняет, что именно происходит и что вы можете сделать сейчас.

Корневая причина: проблемы в upstream-рантайме Bun

Заголовок раздела «Корневая причина: проблемы в upstream-рантайме Bun»

opencodex поставляет рантайм Bun (сейчас это 1.3.14). Рост памяти вызван известными проблемами upstream Bun, а не JavaScript-утечками внутри прокси:

Проблема Bun Состояние (проверено 2026-07-23)
#28035 — backpressure в fetch() receive не связан с потреблением на стороне JS Исправлено в PR #29831; в каком релизе это появилось, не подтверждено — мы исходим из того, что в комплектном 1.3.14 этого ещё нет
#32111 — crash при отмене async-pull stream клиентом Исправление PR #32120 смёржено 2026-06-21; не предполагается, что оно уже есть в 1.3.14. Важно: этот crash не специфичен для Windows (он воспроизводился и на macOS/Linux)
PR #31654 — утечка socket handle в node:net Всё ещё open в upstream

На Windows opencodex вынужден оставлять streaming-ответы на более консервативном пути кода, чтобы избежать crash из #32111, а именно этот путь сильнее всего подвержен проблеме backpressure: медленный или зависший клиент может оставить рантайм буферизовать upstream-данные в нативной памяти, которую JavaScript уже не может ограничить.

Есть ограниченные меры и наблюдаемость — не исправление. На комплектном рантайме 1.3.14 сама утечка остаётся проблемой upstream:

  • Memory watchdog — прокси раз в минуту измеряет собственную память и пишет rate-limited предупреждение, когда наблюдаемая память пересекает 4 GiB. Наблюдаемая память — это максимум из RSS, external и arrayBuffers (а не их сумма), потому что счётчики working set/RSS на Windows могут занижать коммит удерживаемой external-памяти.
  • ocx doctor — раздел “Memory / runtime” показывает версию Bun у service-процесса, RSS, счётчики external/ArrayBuffers, контекст JS-heap и выбранный stream mode. На комплектном Bun 1.3.14 одних только heapUsed / jscHeap недостаточно, чтобы отличить утечку; сравнивайте наблюдаемую память с responseState и повторными сэмплами, прежде чем записывать это на app-level leak.
  • GET /api/system/memory — те же данные через аутентифицированный management API для дашбордов и сценариев. Помимо счётчиков RSS/heap/external он возвращает скалярный блок responseState (число записей, суммарный/максимальный сериализованный размер, возраст самой старой записи) для in-memory store продолжений previous_response_id в прокси. Это помогает точнее отнести рост: если растут и responseState.totalBytes, и наблюдаемая память, проблема может быть в удержании длинных цепочек store:false; если responseState плоский, а память растёт, причину нужно искать вне этого store. Значения только скалярные — без тел запросов, токенов, путей и идентификаторов аккаунтов — и чтение не имеет побочных эффектов: ничего не prune’ится и не evict’ится. Карточка Memory observability в дашборде показывает те же поля и даёт confirm-gated действие Drain & restart: она показывает текущее число активных ходов, ждёт до 60 секунд, пока они закончатся (используя уже существующий drain через 503 + Retry-After), затем прерывает оставшиеся ходы, просит аттестованный живой процесс заменить себя и проверяет другой PID на том же порту, не снимая внедрение в Codex. Это более длинный и осознанный recycle, чем короткий drain у POST /api/stop.
  • Альтернативный stream path под флагом — bounded single-reader relay, полностью убирающий форму неограниченной буферизации. На Windows он станет значением по умолчанию автоматически, когда в комплектный Bun подтверждённо войдёт исправление #32111; сегодня это только opt-in (см. ниже). На macOS он останется opt-in даже после такого релиза — решение по auto для macOS будет приниматься отдельно.

Подтверждение реального снижения RSS от этих изменений ещё ждёт проверки пользователями Windows — мы не утверждаем, что утечка уже исправлена.

Пороговый auto-restart намеренно не поставляется. Если процесс всё же падает, его и так перезапускают service manager’ы (Task Scheduler/WinSW, launchd, systemd).

  1. Подождать обновления комплектного рантайма. Когда релиз Bun подтверждённо будет содержать эти исправления, opencodex обновит bundled runtime, и более безопасный stream path автоматически включится на Windows (на macOS он всё равно останется только явным opt-in).

  2. Запустить Bun, которому вы доверяете, через OPENCODEX_BUN_PATH. Это непроверенная территория — вы запускаете opencodex на рантайме, который мы не тестировали, на свой риск. Важно для service-установок: override считывается при генерации артефакта службы, а не при её старте. Задайте переменную окружения и заново выполните ocx service repair из той же оболочки, чтобы путь оказался зашит в долговременное определение службы. Одной только переменной для уже установленной службы недостаточно.

  3. Явно перейти на bounded relay через streamMode: "eager-relay". Есть два пути: отредактировать config.json (добавить "streamMode": "eager-relay") или вызвать management API — PUT /api/settings с {"streamMode":"eager-relay"} применит изменение к новым ходам без перезапуска. Предупреждение о риске crash: на Bun 1.3.14 это использует форму streaming, затронутую #32111, и процесс может упасть прямо посреди потока (на любой ОС, не только на Windows). Service manager перезапустит его, но все запросы в полёте потерпят неудачу. "legacy-tee" жёстко фиксирует текущий путь по умолчанию. На Windows "auto" (по умолчанию) позволяет рантайму выбрать путь самому. На macOS "auto" всегда остаётся на tee; явный "eager-relay" — это opt-in.

Если вы попробуете любой из этих вариантов на реальной Windows-нагрузке, пожалуйста, пришлите разделы памяти из ocx doctor до и после в #314 — именно такой верификации эта мера и ждёт.