[CL-79] bounded graceful drain in-flight runs на shutdown #84

Merged
andrei merged 1 commit from feature/claude-graceful-drain into fix/PIX-7823-v2-local 2026-06-18 09:09:32 +00:00
Owner

Что сделано

Bounded graceful drain в shutdown() (server/src/index.ts) ПЕРЕД reap/cancel: ждём пока runningProcesses опустеет (in-flight runs завершатся естественно), bounded SHUTDOWN_DRAIN_MAX_MS (default 20s, env-configurable).

  • server/src/services/fleet-shutdown-drain.ts — чистая тестируемая drainActiveRuns (injectable clock/sleep)
  • server/src/index.ts — drain после prepareForShutdown, до early-kill
  • server/src/__tests__/fleet-shutdown-drain.test.ts — 4 cases

Зачем

На pm2 restart (агенты self-деплоят ~1/час) shutdown() СРАЗУ убивает claude-детей mid-turn → "TOOL ERRORED / Exit code 5 / INPUT empty" в transcript → run ретраится впустую. Это root cause исходного вопроса "почему агенты вызывают инструменты с ошибками". Drain даёт runs закончиться.

FAIL-SAFE: try/catch + pm2 kill_timeout = hard backstop → баг может только задержать shutdown (bounded), не повесить рестарт. Нет регрессии (fallthrough к текущему immediate kill).

План тестирования

  • vitest src/__tests__/fleet-shutdown-drain.test.ts4/4 pass
  • tsc --noEmit (server) → 0 errors

Деплой (требуется)

pm2 kill_timeout >= 30s (сейчас 10s) чтобы 20s drain + kill-grace (1+5s) поместились, иначе pm2 SIGKILL обрежет cleanup. Применяется при деплое (выставлю отдельно).

Где могу ошибиться

  • runningProcesses может пополняться во время drain — bounded регардлесс; prepareForShutdown вызван до drain.
  • Эффект виден когда флот busy (сейчас capped до Jun 23 reset).
  • base = fix/PIX-7823-v2-local (ветка флота, не master).

Closes #79

## Что сделано Bounded graceful drain в `shutdown()` (server/src/index.ts) ПЕРЕД reap/cancel: ждём пока `runningProcesses` опустеет (in-flight runs завершатся естественно), bounded `SHUTDOWN_DRAIN_MAX_MS` (default 20s, env-configurable). - `server/src/services/fleet-shutdown-drain.ts` — чистая тестируемая `drainActiveRuns` (injectable clock/sleep) - `server/src/index.ts` — drain после `prepareForShutdown`, до early-kill - `server/src/__tests__/fleet-shutdown-drain.test.ts` — 4 cases ## Зачем На `pm2 restart` (агенты self-деплоят ~1/час) `shutdown()` СРАЗУ убивает claude-детей mid-turn → "TOOL ERRORED / Exit code 5 / INPUT empty" в transcript → run ретраится впустую. Это root cause исходного вопроса "почему агенты вызывают инструменты с ошибками". Drain даёт runs закончиться. **FAIL-SAFE**: try/catch + pm2 `kill_timeout` = hard backstop → баг может только задержать shutdown (bounded), не повесить рестарт. Нет регрессии (fallthrough к текущему immediate kill). ## План тестирования - `vitest src/__tests__/fleet-shutdown-drain.test.ts` → **4/4 pass** - `tsc --noEmit` (server) → **0 errors** ## Деплой (требуется) pm2 `kill_timeout` >= 30s (сейчас 10s) чтобы 20s drain + kill-grace (1+5s) поместились, иначе pm2 SIGKILL обрежет cleanup. Применяется при деплое (выставлю отдельно). ## Где могу ошибиться - `runningProcesses` может пополняться во время drain — bounded регардлесс; `prepareForShutdown` вызван до drain. - Эффект виден когда флот busy (сейчас capped до Jun 23 reset). - base = `fix/PIX-7823-v2-local` (ветка флота, не master). Closes #79
[CL-79] fix(shutdown): bounded graceful drain in-flight runs перед reap
All checks were successful
Agents CI / Typecheck and Build (pull_request) Successful in 5m11s
39923486a4
## Что
На `pm2 restart` (агенты self-деплоят платформу ~1/час) сервер ловит SIGINT/SIGTERM и в `shutdown()` (index.ts) СРАЗУ убивает claude-детей + отменяет live runs (`server_shutdown`), без ожидания. Прерванные tool calls = "TOOL ERRORED / Exit code 5 / INPUT empty" в transcript, runs ретраятся впустую (root cause "почему агенты вызывают инструменты с ошибками").

## Фикс
Bounded graceful drain ПЕРЕД reap/cancel: ждём пока `runningProcesses` опустеет (in-flight runs завершатся естественно), bounded `SHUTDOWN_DRAIN_MAX_MS` (default 20s, env-configurable). FAIL-SAFE: try/catch + pm2 `kill_timeout` = hard backstop → баг может только задержать shutdown (bounded), не повесить рестарт; нет регрессии (fallthrough к текущему immediate kill).

- `server/src/services/fleet-shutdown-drain.ts` — чистая тестируемая `drainActiveRuns` (injectable clock/sleep)
- `server/src/index.ts` — drain call в `shutdown()` после `prepareForShutdown`, до early-kill
- `server/src/__tests__/fleet-shutdown-drain.test.ts` — 4 cases

## Деплой (требуется)
pm2 `kill_timeout` >= 30s (сейчас 10s) чтобы 20s drain + существующие kill-grace (1+5s) поместились, иначе pm2 SIGKILL обрежет cleanup. Выставляется отдельно (start/ecosystem) — будет применено при деплое.

## План тестирования
- `vitest`: 4/4 pass; `tsc --noEmit`: 0 errors.

## Где могу ошибиться
- `runningProcesses` может пополняться новыми runs во время drain — bounded регардлесс. `prepareForShutdown` уже вызван до drain.
- Эффект виден когда флот busy (сейчас capped до Jun 23 reset).

Closes #79
andrei merged commit faab08e5c0 into fix/PIX-7823-v2-local 2026-06-18 09:09:32 +00:00
andrei deleted branch feature/claude-graceful-drain 2026-06-18 09:09:32 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
europa-tech-srl/europa-tech-agents!84
No description provided.