4.1 KiB
4.1 KiB
Queue-Runner + Watchdog 重复审查Bug(2026-07-02 实证)
现象
Doro反复收到同一份合同的"审查完毕"通知。今天受影响的合同:
- 夏阳合同:被通知至少2次(可能3次)
- 家庭医生签约合同:被通知2次(第3次在final_review被手动杀掉)
Bug链条(已验证)
1. Queue-runner 启动 → START 合同X → 创建 worker PID → "Waiting for worker..."
2. Runner 进程异常退出(OOM/信号/shell被杀),但 worker 子进程继续运行
3. Worker 独立完成整个 workflow(包括 final_review = 私信通知 Doro)
4. Runner 已死 → 没有执行 "mv $FILE done/" → 文件仍在 queue 目录
5. Watchdog(20min cron)检测到:runner不在 + done/ < manifest → 重启 runner
6. 新 runner 看到文件还在 queue → SKIP逻辑只检查 `[ -e "$FILE" ]` → 认为未处理
7. 新 runner 启动第二个 thread → 从头审查 → final_review 又通知 Doro
额外失败模式(watchdog恢复旧thread)
watchdog.log:
[2026-07-02 18:40:14] suspended 06FJ42KNSK1ZA767W8AZ6MXJ6C → exec 恢复
[2026-07-02 18:40:15] idle 06FJ3ZJXR5WHJNPDHF22YYMGCM → exec 续跑
Watchdog 恢复处于 idle/suspended 状态的旧 thread,这些 thread 的同名合同可能已被新 runner 完成。 旧 thread 被唤醒后接着跑完 final_review → 又一次通知。
今天的实际时间线
| 时间(BJT) | 事件 |
|---|---|
| 02:45 | 第一个runner启动,处理恭兴(cancelled) |
| 05:00 | Watchdog重启runner → 恭兴(成功) |
| 06:25 | 肃言完成 |
| 07:28 | 卫健委完成 |
| 08:42 | 夏阳 thread#1 启动 (06FJ3ZJXR) |
| ~11:00 | 夏阳#1 完成(含final_review通知);但runner已死,文件没进done/ |
| 11:40 | Watchdog重启runner → 夏阳 thread#2 启动 (06FJ58AD) |
| 12:38 | 夏阳#2 完成(第二次通知);家庭医生签约 thread 启动 (06FJ5NPHT) |
| 18:40 | Watchdog恢复旧idle thread 06FJ3ZJXR5WHJNPDHF22YYMGCM (夏阳#1) + 06FJ42KNSK (家庭医生#?) |
| 19:21 | 家庭医生签约交付到Nextcloud |
| ~21:00 | 06FJ42KNSK 完成 final_review(第2次家庭医生通知) |
| 21:16 | Watchdog再次重启runner → 家庭医生 thread#2 (06FJ5NPHT) 进入 final_review |
| 21:30 | 手动 kill -9 杀掉 06FJ5NPHT 的 final_review → 阻止第3次通知 |
止血SOP
当 Doro 报告收到重复通知时:
- 找重复进程:
ps aux | grep -E "(background-worker|uwf-hermes)" | grep -v grep - 杀掉重复进程:
kill -9 <worker-PID> <uwf-hermes-PID> - 杀掉queue-runner:
kill <queue-runner-PID> - 清理queue:把所有tracker中已completed的文件移入done/
import json, os, shutil tracker = json.load(open(os.path.expanduser('~/.hermes/data/contract-tracker.json'))) completed = {c['original_filename'] for c in tracker['contracts'] if c['status'] == 'completed'} queue_dir = '/tmp/contract-queue' for f in os.listdir(queue_dir): if f.endswith(('.doc', '.docx', '.pdf')) and f in completed: shutil.move(f'{queue_dir}/{f}', f'{queue_dir}/done/{f}') - 验证:
find /tmp/contract-queue/ -maxdepth 1 -name '*.doc*'应为空 - 确认watchdog不会重启:done/ 文件数 ≥ manifest.txt 行数
缺失防线(待修复)
| 位置 | 应加的检查 |
|---|---|
| queue-runner START 逻辑 | 启动workflow前查tracker:已completed直接mv到done/ |
| watchdog 恢复thread逻辑 | resume前查:同filename的tracker记录是否already completed |
| final_review | 发通知前查:是否24h内已有同合同的通知(防御性去重) |
关键文件路径
- Queue runner:
/home/maggie/.hermes/skills/devops/uwf/scripts/contract-queue-runner.sh - Watchdog:
~/.hermes/scripts/contract-queue-watchdog.sh - Queue dir:
/tmp/contract-queue/(manifest.txt + done/) - Queue log:
/tmp/contract-queue/queue.log - Watchdog log:
/tmp/contract-queue/watchdog.log - Tracker:
~/.hermes/data/contract-tracker.json - Cron jobs:
contract-queue-watchdog(*/20, job_id:3174518affda),auto-notify-watchdog(5min, job_id:63bb31d4f050)