Skip to content

[Bug] headless bootstrap 无超时:子进程 initialize 永不应答时会话进程无限滞留,可放大为整机任务数耗尽 #910

Description

@ThatQuixote

环境

  • 版本:v2.1.2(release 安装)
  • 平台:Linux(Arch Linux),systemd user service,codex 后端
  • 触发场景:headless pool 高频拉起实例(inst-headless-pool-*)

现象

当某个 headless 实例的 codex 子进程启动链路被阻塞、永远不回 initialize 应答时,launchCodexChildSession 会永久阻塞在 bootstrapHeadlessCodex,该会话进程与其整棵子进程树全部滞留,且无任何超时或退避,新实例继续被拉起,进程数无上限堆积。

本机实测 13 小时的放大过程:

  1. 用户 PATH 中的 codex 启动脚本(本机自建 shim,非本仓库产物)内部调用 mise use -g codex,该命令需要抢一个文件锁;上游排队进程达到上千后,每个新 codex 启动都卡在锁上,永远到不了 initialize 应答。
  2. wrapper 侧 bootstrapHeadlessCodex 读循环(internal/app/wrapper/app_headless.go:44)对 ReadBytes 无 deadline,launch 路径(internal/app/wrapper/app_child_session.go:52)随之永久阻塞。三条后端链路同病:app_headless.go(codex)、app_headless_claude.go(claude)、app_child_session_opencode.go(opencode)。
  3. 滞留进程持续累积,cgroup 任务数顶满 systemd TasksMax(本机 38071)。此后该 cgroup 内所有 clone() 返回 EAGAIN:
    • codex-remote 自身 Go runtime 直接 fatal error: newosproc(errno 11),实例日志中可复现;
    • 被拉起的外部命令全部 panic/失败(本机 mise 当日产生 527 个 SIGABRT coredump);
    • daemon 仍持续重试拉起,故障自我放大,持续 13 小时,直到人工重启服务。

/proc/locks 实测锁队列 19+ 层深;崩溃期间 cgroup pids.current == pids.max == 38071。

根因

触发源在本机(shim 的全局写锁),但上游缺陷是它从"局部变慢"放大为"整机资源耗尽"的原因:

  1. bootstrap 握手无超时——子进程不应答时会话进程无限滞留;
  2. bootstrap 阶段不响应 ctx 取消——即使会话被要求停止也无法回收滞留中的 launch;
  3. launch 失败/阻塞无退避与并发上限,资源耗尽后仍以原有频率重试,crash-loop 放大故障。

建议

  1. 为三个 backend 的 bootstrap 握手加超时(超时后 kill 子进程并返回错误);本机 codex app-server 正常握手在 1s 内,建议 15s 量级;
  2. 让 bootstrap 阶段响应 ctx 取消;
  3. (可选)launch 失败指数退避,或在检测到资源耗尽(EAGAIN)时熔断。

我可以提交一个最小修复 PR:统一封装带超时 + ctx 取消的 bootstrap 调用,三个调用点切换过去,并附回归测试。

Activity

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