Files
DCTS/docs/runbook-20260802-zombie-vortex-fix.md
fmq cd370d88e7 feat(all): 任务引擎双阶段解耦、僵尸涡旋修复、动态 CPU 配额与前端详情页重构
将 TLUSTY/SYNSPEC 拆为各自独立的 enabled/policy/strategies 阶段,
以策略链自动弹栈取代单级 seed_step 布尔回退;定向修复 2026-08-02
僵尸任务涡旋事故;新增节点并发配额热调;前端详情页从 1412 行巨型
视图拆为薄控制器 + detail 子模块,并补齐工具层与单测。

引擎与调度(task_engine_decoupling_design.md)
- models.rs: 新增 StagePolicy / EngineStageConfig / TaskSpec 阶段字段、
  normalize_compat() 校正旧版在途消息策略链、failed_stage 归因
- scheduler.rs: resolve_dispatchable_chain 派发门控、
  trigger_strategy_fallback 按 failed_stage 精确弹栈;启动期
  force_recompute/skip_converged(默认)/skip_failed 三策略
- db.rs: tasks 表 +7 列持久化阶段配置;终态守卫
  (mark_grid_point_running 仅 pending/queued→running;
  record_task_report 拒绝迟到失败翻黑 converged);策略弹栈快照

僵尸涡旋修复(runbook-20260802-zombie-vortex-fix.md)
- 全链路跨库活性交叉校验:派发/claim/孤儿回收/回退统一查 MQ 队列活性,
  活则放行、死则清僵尸,结构性消除"每点重复派发"
- stop/重启卫生:清队列同步 delete_tasks_by_ids,杜绝遗留 pending 行
- report_task: 幂等吸收 + 409 区分迟到冗余结果,仅 state_changed 时回退
- MQ: NULL workflow_name 回填 __legacy__、requeue 后迟到上报被 403 竞态修复

动态 CPU 配额(dynamic_cpu_slots_design.md)
- admin.rs: POST /admin/nodes/:id/quota(Option<Option<i32>> 区分
  缺字段/显式 null);nodes 表 +admin_max_slots
- worker.rs: effective_max_slots = min(admin, physical),心跳下发原子生效

科学产物保全(tlusty_result_artifacts.md)
- runner.rs: SYNSPEC 启动前快照 fort.12/fort.14 → .bfac/.emflux 防覆盖
- 半失败点(大气收敛+光谱失败)改判 Failed 并写入 note;仅 SYNSPEC
  场景不再恒判失败;撤销归档 LRU 200 上限改为永久保留
- executor.rs: 透传 synspec_params 数值参数(此前固定 None)

前端(dashboard/)
- workflowDetail.js 1412→328 行,拆出 views/detail/{ctx,overview,
  pointsTable,parSets,pointPanel}.js,AbortController 治理监听/请求生命周期
- 删除 wfActions.js,新增 wfEnginePanel.js(双阶段三维配置编辑面板)
- 新增 utils/{errors,format,icons,polling,yamlStage}.js 纯函数模块
- 路由级动态 import 代码分割;节点配额三点菜单 + Modal 管理
- 首次引入 node:test 单测(format/polling/yamlStage/psCache,644 行)
- 系统性补齐 a11y:skip-link、ARIA、Tab 键盘漫游、toast 关闭、退出动画

文档与工具
- 新增 6 篇设计/调研:引擎解耦、动态配额、涡旋 runbook、
  光谱正确性分析、收敛判断、产物归档
- PIPELINE/design/api/database 等协同重写为分布式 C/S 架构口径
- scripts/fetch_results.sh 跨节点产物备份;import_results 按 cno 升序导入
- workflows/sdB_cno.yaml: 新增 tlusty/synspec_stage 配置块,修正 wstart 笔误
2026-08-04 23:40:52 +08:00

4.9 KiB
Raw Permalink Blame History

Runbook:僵尸任务涡旋修复上线(2026-08-02)

背景

2026-08-01 晚重启工作流后发生重复派发涡旋(单点最高被算 101 次、wave3-9 积压饿死 6+ 小时)与收敛点被迟到失败报告翻黑(converged 净减少)。根因与修复见本次代码变更(三条不变量:每点至多一个活任务;converged 吸收态;stop/重启不产生僵尸)。

当前生产状态(14:24 实时):工作流已暂停、槽位已排空、converged 4621 / failed 1139 / pending 3456。本 runbook 从"部署新版"开始。

第 1 步:部署新版服务端

cd /home/fmq/program/tlusty/tl208-s54/dcts   # 本机(代码所在)
./scripts/deploy.sh -e remote -b compose -r server

部署期间工作流保持 paused,无影响。部署完成后确认容器正常:

ssh fmq@100.66.1.2 'cd /vol1/1000/program/dcts && docker compose ps server'
curl -s "http://100.66.1.2:8090/api/status" -H "Authorization: Bearer <admin_token>" | head -c 300

第 2 步:存量清理(维护窗口,约 2 分钟)

在 100.66.1.2 上执行。建议停容器避免 WAL 锁争用(节点心跳会短暂失败并自动恢复):

ssh fmq@100.66.1.2
cd /vol1/1000/program/dcts
docker compose stop server

# 确认队列库路径(compose 默认为 data/dcts_queue.db
ls -la data/dcts.db data/dcts_queue.db

# 备份
cp data/dcts.db data/dcts.db.bak.$(date +%s)
cp data/dcts_queue.db data/dcts_queue.db.bak.$(date +%s)

# 清理 + 修复(先审计后执行)
sqlite3 data/dcts.db <<'SQL'
ATTACH 'file:data/dcts_queue.db?mode=ro' AS q;

-- 审计 1:将删除的僵尸行数(预期数千条;= 无队列凭证的 pending 行)
SELECT COUNT(*) AS zombies FROM main.tasks
WHERE status='pending' AND workflow_name='sdB_cno'
  AND task_id NOT IN (SELECT task_id FROM q.task_queue);

-- 审计 2:被翻黑的收敛点数(success_method 非空的 failed 点)
SELECT COUNT(*) AS flipped FROM grid_points
WHERE workflow_name='sdB_cno' AND status='failed' AND success_method IS NOT NULL;

-- 审计 3:从未真正获得种子救援的 failed 点数
SELECT COUNT(*) AS never_rescued FROM grid_points
WHERE workflow_name='sdB_cno' AND status='failed' AND success_method IS NULL
  AND NOT EXISTS (SELECT 1 FROM tasks t
                  WHERE t.point_name=grid_points.name
                    AND t.workflow_name='sdB_cno'
                    AND t.task_type='seed_step');

-- 执行 1:清僵尸(claimed 在途行自动保留,NOT IN 子查询不过滤 status
DELETE FROM main.tasks
WHERE status='pending' AND workflow_name='sdB_cno'
  AND task_id NOT IN (SELECT task_id FROM q.task_queue);

-- 执行 2:修复被翻黑的收敛点
UPDATE grid_points SET status='converged'
WHERE workflow_name='sdB_cno' AND status='failed' AND success_method IS NOT NULL;

-- 执行 3(可选):复活从未获救援的 failed 点,走正常 cold_run → 种子回退路径
UPDATE grid_points SET status='pending'
WHERE workflow_name='sdB_cno' AND status='failed' AND success_method IS NULL
  AND NOT EXISTS (SELECT 1 FROM tasks t
                  WHERE t.point_name=grid_points.name
                    AND t.workflow_name=grid_points.workflow_name
                    AND t.task_type='seed_step');
SQL

docker compose up -d server

注意:执行 3 会把约百个点打回 pending,重启后它们会先跑一轮 cold_run 再触发种子回退——这是预期行为。

第 3 步:启动工作流

curl -s -X POST "http://100.66.1.2:8090/api/workflows/sdB_cno/start" \
  -H "Authorization: Bearer <admin_token>"

启动时 initialize_grid 卫生逻辑会再清一遍残留(与第 2 步不冲突),随后 3456 个 pending 点按 wave 升序正常派发。

第 4 步:验证(启动后 1 小时内)

# 1. 无重复派发:最近 1 小时每个点应至多 1 个任务(预期空结果)
ssh fmq@100.66.1.2 "sqlite3 /vol1/1000/program/dcts/data/dcts.db \
  \"SELECT point_name, COUNT(*) n FROM tasks WHERE created_at > datetime('now','-1 hour') GROUP BY point_name HAVING n > 1;\""

# 2. converged 单调不减、queued 以 ~100/h 下降(每 10 分钟看一次)
watch -n 600 "curl -s http://100.66.1.2:8090/api/status -H 'Authorization: Bearer <admin_token>' | head -c 200"

# 3. 历史涡旋点不再产生新任务
ssh fmq@100.66.1.2 "sqlite3 /vol1/1000/program/dcts/data/dcts.db \
  \"SELECT COUNT(*) FROM tasks WHERE point_name='t55000_g5.0_he2_c-4_n-3_o-4' AND created_at > datetime('now','-1 hour');\""

# 4. 观察项(非承诺):快照 running 计数是否恢复 >0。若节点满载但 running 恒 0,
#    属独立显示问题,查 server 日志中 claim 的 "跳过 running 标记" info 行定位。

回滚预案

若新版出现异常:docker compose stop server → 恢复备份(cp data/dcts.db.bak.<ts> data/dcts.db,队列库同理)→ 部署旧镜像 → up。清理 SQL 的逆操作不需要(僵尸行清除与翻黑修复都是单向正确方向)。