5.0 KiB
5.0 KiB
调查结论
2026-07-12 根因证据
ccwebPID 1606383 持续占满单个 CPU 核心,RSS 约 130 MiB,系统可用内存约 4.4 GiB。curl http://127.0.0.1:8002/能立即建立 TCP,但 8–10 秒无响应,符合事件循环阻塞而非端口未监听。logs/process.log每约 0.6 秒出现codex_app_notification_unrouted,来源主要是三个 Codex App threadId。- 三个 threadId 均存在于当前
sessions/*-run/codexapp-state.json,却未恢复进内存路由映射。 findCodexAppRouteByRuntime在内存命中失败后调用adoptCodexAppUnroutedTurn。findCodexAppSessionByThreadId对每条未路由通知执行readdirSync(SESSIONS_DIR),再逐个loadSession;当前 113 个会话 JSON 合计约 61.3 MiB。- 高频 delta × 同步全量扫描导致 Node 主线程单核 100%,所有 HTTP 请求饥饿。
- PM2 error log 中的 V8 OOM 修改时间为 2026-06-15,是历史故障,不是当前直接原因。
- 当未路由子线程通知停止后,未部署补丁的旧进程 CPU 从单核 100% 降至 0–2%,本地首页恢复为 HTTP 200/约 2.3 ms;这证明故障与通知风暴强相关,而非持续内存不足或端口问题。
工作区约束
- 用户已有修改:
public/app.js、public/style.css、scripts/regression.js。 - 现有未跟踪计划/任务目录属于其他工作,不得覆盖或删除。
.trellis/.current-task当前指向00-bootstrap-guidelines,另有07-11-subagent-card-metadata进行中;不得切换共享 current-task,以免干扰并发会话。- 项目要求重启前检查运行会话;只有除当前会话外没有其他 running 会话时才能重启。
- 当前磁盘运行态检查只发现本对话
4b9700f7-...的一个codexapp-state.json,状态为running;没有发现其他会话 run 目录或 classic PID 文件。最终重启前仍需再次复核。 - 重启前通过运行中 ccweb 的内部 MCP
ccweb_list_conversations检查:仅当前对话为running,其余全部为idle,满足项目重启条件。
部署验证
- 执行
pm2 restart ccweb --update-env成功,新 PID 为 1802039,PM2 restart 计数从 60 增至 61。 - 启动日志出现
ccweb_mcp_child_threads_recovered,restored=3,证明真实恢复文件中的三个子线程路由被重建。 - 连续 5 次本地 HTTP 探针均返回 200,后续最终探针约 3.6 ms。
- 新进程 RSS 约 64 MiB,事件循环 P95 约 1.5 ms,短时 CPU 回落至 0–12%,没有持续占满单核。
- 重启后的日志窗口仅包含 recovery/server_start 事件,没有新的
codex_app_notification_unrouted风暴。
项目上下文
- Trellis 将项目识别为单仓库,规范层为 backend/frontend;本次只涉及 backend。
- codebase-memory 项目
home-cc-web索引状态为ready,当前 3091 个节点、7446 条边。 - 独立计划审查已通过;建议在实施记录中明确测试入口与重启检查。
- 项目测试入口为
npm run regression(实际执行node scripts/regression.js),服务入口为node server.js。 - backend 规范目录当前仍是模板状态,没有额外项目特定限制;本次重点遵守现有结构化
plog、同步 I/O 热路径规避和回归脚本惯例。 - 预计修改位置:
recoverCodexAppTurnState、findCodexAppSessionByThreadId、adoptCodexAppUnroutedTurn、handleCodexAppNotification,以及独立/现有回归脚本。 - 当前
codexapp-state.json只持久化父线程 entry;子线程 ID 仅间接存在于toolCalls[*].input.agentThreadId等协作工具数据中,recoverCodexAppTurnState没有重建ccwebMcpChildThreads。 findCodexAppRouteByRuntime当前先调用父线程磁盘收养逻辑,再检查ccwebMcpChildThreads;对子线程通知会先触发昂贵的父会话全盘扫描,路由顺序本身也需要调整。- 现有
scripts/regression.js含用户未提交的“子代理卡片元数据”测试,修复必须追加且保留这些改动。 - 真实恢复文件中,协作子线程以
toolCalls[].name/kind = subAgentActivity持久化;input.agentThreadId是路由键,input.agentPath可作为恢复标签,input.kind表示 started/interacted 等活动。
HAPI 对比
- HAPI 的“归档”主要是会话生命周期操作:将 active session 断开并把
metadata.lifecycleState设为archived,支持后续 reopen;它不是专门为解决 ccweb 本次每条通知全盘扫描而设计。 - HAPI 的归档确实能减少活动会话集合和实时订阅压力,但历史记录仍保留在存储/缓存层,因此只有配合索引化 SessionCache/SyncEngine 才能避免热路径扫描。
- 对 ccweb 而言,未来增加归档有产品和容量管理价值,但不能替代当前 threadId 路由索引、恢复映射和负缓存修复。
- 源码确认 HAPI
archiveSession调用rpcGateway.killSession后进入handleSessionEnd;Hub 存储基于bun:sqlite,SessionCache维护内存态并以active作为权威运行标志。