feat: recover stale running agent runs

This commit is contained in:
Your Name
2026-07-02 12:12:41 +08:00
parent af5396c127
commit c27bf04a71
7 changed files with 334 additions and 15 deletions
@@ -26,6 +26,7 @@
- P6.15 H5 Code-run Validation Metadata: 已完成,H5 code-run 自动声明 `.memind/agent-runs/<requestId>.json` receipt 校验,生产已恢复 `MEMIND_AGENT_CODE_RUNS_REQUIRE_VALIDATION=1` 并通过普通测试用户真实路径。
- P6.17 Controlled Worker Concurrency Canary: 已完成,external worker 短窗口提升到并发 2,两条真实 code-run 同时 running 并全部通过 receipt validation。
- P6.18 Concurrency Rollout Policy: 已完成,生产 worker 和 Portal runtime/status 已对齐到并发 2worker KeepAlive 保持开启,保留回滚备份。
- P8.1 Queue Lease / Stuck Run Recovery: 已完成第一步,worker dispatch 前自动回收超时 running run,新增 `--recover-stale` dry-run/apply 运维入口,生产 synthetic stale run 验证通过。
- P5.15 Active Stream TTL Reconcile: 已按用户要求跳过,暂不做报表/定时 reconcile。
- P5 Worker Pool 运维化: 已完成第一步,Redis Router 支持 worker drain。
- P5.9 First-token Latency EWMA: 已完成,StreamController 会把首个 SSE chunk 延迟写入 RedisSLO 报告已展示。
@@ -2736,6 +2737,74 @@ runtime/status:
- Portal 在一次重启窗口中出现 `read EADDRNOTAVAIL` 并由 LaunchAgent 自动拉起,随后 `/api/status``/api/runtime/status` 恢复 `ok`
- 后续建议优先做 running/stuck recovery 与 DB/session auth 异常不致命化,避免瞬时 DB/socket 错误扩大为进程退出。
### 2026-07-02 P8.1 Queue Lease / Stuck Run Recovery
目标:
- 防止 worker/进程在 run 进入 `running` 后异常退出,导致该 run 永久卡住。
- 在不新增 schema、不修改真实用户数据的前提下,先用 `started_at + runTimeoutMs` 作为最小 lease。
新增能力:
- `agent-run-gateway.mjs`:
- 新增 `recoverStaleRunningRuns()`
- `dispatchQueuedRuns()` 每轮 dispatch 前自动回收超过 `runTimeoutMs``running` rows。
- stale row 会标记为 `failed`,写入 `stale_recovered` event。
- `scripts/agent-run-worker.mjs`:
- 新增只读入口:
- `node scripts/agent-run-worker.mjs --recover-stale --stale-ms 900000 --limit 5`
- 新增应用入口:
- `node scripts/agent-run-worker.mjs --recover-stale --apply-recovery --stale-ms 900000 --limit 5`
- `scripts/check-agent-run-worker.mjs`:
- queue 摘要增加:
- `oldestRunningStartedAt`
- `oldestRunningAgeMs`
- runtime/status 的 queue 摘要增加 running age 字段。
测试:
- `node --check agent-run-gateway.mjs scripts/agent-run-worker.mjs scripts/check-agent-run-worker.mjs` 通过。
- `node --test agent-run-gateway.test.mjs agent-run-routes.test.mjs` 通过,29 tests pass。
- 新增测试:
- dry-run reports stale running rows without mutating them。
- apply marks stale running rows failed and writes `stale_recovered` event。
生产部署:
- 备份:
- `/Users/john/Project/memind_backups/20260702-120900-p81-stale-recovery`
- 已部署:
- bundled `server.mjs`
- bundled `scripts/agent-run-worker.mjs`
- `scripts/check-agent-run-worker.mjs`
- `RUNBOOK.txt`
- 已重启:
- `cn.tkmind.memind-portal`
- `cn.tkmind.memind-agent-run-worker`
生产验证:
- `/api/status` 返回 `ok`
- `/api/runtime/status.toolRuntime.queue`:
- `maxConcurrentRuns=2`
- `statusCounts={}`
- `oldestRunningStartedAt=null`
- `oldestRunningAgeMs=0`
- stale dry-run:
- `considered=0`
- `recovered=0`
- synthetic stale apply:
- run id `9e22ad3d-ad5d-42b5-9ce3-24fe7aace3f5`
- request id `p81-stale-recovery-1782965482040`
- recovery `considered=1`, `recovered=1`
- final status `failed`
- event chain includes `stale_recovered`
- post-check:
- worker running with `--limit 2`
- queue empty
- guard `shouldPause=false`
- SLO `ok=true`, `failures=[]`
## 回滚策略
- P0: 修改前保留 `server.mjs` 备份;如启动失败,恢复备份并 `launchctl kickstart` Portal。