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 笔误
This commit is contained in:
fmq
2026-08-04 23:40:52 +08:00
parent c8fd24b120
commit cd370d88e7
74 changed files with 13386 additions and 3033 deletions
+112
View File
@@ -0,0 +1,112 @@
# Runbook:僵尸任务涡旋修复上线(2026-08-02)
## 背景
2026-08-01 晚重启工作流后发生重复派发涡旋(单点最高被算 101 次、wave3-9 积压饿死 6+ 小时)与收敛点被迟到失败报告翻黑(converged 净减少)。根因与修复见本次代码变更(三条不变量:每点至多一个活任务;converged 吸收态;stop/重启不产生僵尸)。
**当前生产状态(14:24 实时)**:工作流已暂停、槽位已排空、converged 4621 / failed 1139 / pending 3456。本 runbook 从"部署新版"开始。
## 第 1 步:部署新版服务端
```bash
cd /home/fmq/program/tlusty/tl208-s54/dcts # 本机(代码所在)
./scripts/deploy.sh -e remote -b compose -r server
```
部署期间工作流保持 paused,无影响。部署完成后确认容器正常:
```bash
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 锁争用(节点心跳会短暂失败并自动恢复):
```bash
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 步:启动工作流
```bash
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 小时内)
```bash
# 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 的逆操作不需要(僵尸行清除与翻黑修复都是单向正确方向)。