feat: recover stale running agent runs
This commit is contained in:
@@ -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 已对齐到并发 2,worker 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 延迟写入 Redis,SLO 报告已展示。
|
||||
@@ -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。
|
||||
|
||||
Reference in New Issue
Block a user