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
+109 -9
View File
@@ -10,8 +10,11 @@
erDiagram
WORKFLOWS ||--o{ GRID_POINTS : contains
GRID_POINTS ||--o{ TASKS : logs
WORKFLOWS ||--o{ WORKFLOW_PROGRESS_SNAPSHOTS : samples
NODES ||--o{ TASK_QUEUE : executes
NODES ||--o| NODE_CREDENTIALS : authenticates
SEEDS }o--o{ GRID_POINTS : hot-starts
subgraph PrimaryDB ["主数据库 (dcts.db)"]
WORKFLOWS {
string name PK
@@ -22,7 +25,8 @@ erDiagram
datetime updated_at
}
GRID_POINTS {
string name PK
integer id PK
string name
string workflow_name FK
double teff
double logg
@@ -35,16 +39,31 @@ erDiagram
string status
integer attempt_count
string success_method
double last_elapsed_sec
}
TASKS {
string task_id PK
string point_name FK
string point_name
string node_id
string task_type
string seed_point_name
string status
double max_relc
boolean atmosphere_has_nan
integer retry_count
datetime created_at
datetime started_at
datetime completed_at
string error_message
string workflow_name
boolean tlusty_enabled
string tlusty_policy
text tlusty_strategies
boolean synspec_enabled
string synspec_policy
text synspec_strategies
string atmosphere_ref
double elapsed_sec
}
NODES {
string node_id PK
@@ -54,8 +73,40 @@ erDiagram
double cpu_usage
double memory_usage
datetime last_heartbeat
string registration_secret
integer admin_max_slots
}
}
NODE_CREDENTIALS {
string node_id PK
string token_hash
datetime issued_at
integer revoked
string raw_token_pending
}
SEEDS {
integer id PK
string point_name UK
double teff
double logg
double loghe
double logc
double logn
double logo
string file_path
boolean is_clean
}
WORKFLOW_PROGRESS_SNAPSHOTS {
integer id PK
string workflow_name
datetime ts
integer total
integer pending
integer queued
integer running
integer converged
integer failed
}
end
subgraph QueueDB ["队列库 (dcts_queue.db / mq)"]
TASK_QUEUE {
@@ -91,28 +142,76 @@ erDiagram
`name` 共同构成复合唯一约束 `UNIQUE(workflow_name, name)`
- `teff`, `logg`, `loghe`, `logc`, `logn`, `logo` (`REAL NOT NULL`)6 维物理参数。
- `cno_sum` (`REAL NOT NULL`)CNO 丰度之和(调度排序用)。
- `wave` (`INTEGER`):按 cno_sum 分组的批次波次(调度优先级用)。
- `wave` (`INTEGER NOT NULL DEFAULT 0`):按 cno_sum 分组的批次波次(调度优先级用)。
- `status` (`VARCHAR(32)`)`pending` / `queued` / `running` / `converged` / `failed`
- `attempt_count` (`INTEGER`):失败重试计数(仅观测用)。
- `success_method` (`VARCHAR(32)`):收敛时的成功手段 (`cold_run` 冷启动成功 / `seed_step` 种子步进成功)。
- `success_method` (`VARCHAR(32)`):收敛时的成功手段 (`cold_run` 冷启动成功 / `seed_step` 种子步进成功 / `imported` 历史导入)。
- `last_elapsed_sec` (`REAL`):最近一次尝试的墙钟耗时(详情页 ETA 估算用,兼容旧数据回退)。
> **多工作流分区(per-workflow partitioning**`grid_points` 与 `task_queue` 均按 `workflow_name` 隔离。
> 调度、状态更新、stale 重投、`stop_workflow` 重置都限定在单个工作流内,互不影响。
> 历史旧库(无 `workflow_name` 列)在启动时自动迁移:表重建为复合唯一结构,旧行 `workflow_name`
> 标记为 `__legacy__`,不干扰新工作流查询。
### 2.3 `nodes` (计算节点心跳与状态表)
### 2.3 `tasks` (任务尝试记录表)
每次派发/尝试生成一行,记录任务的阶段配置快照与执行结果(详情页/归因/回退弹栈的数据源)。
- `task_id` (`VARCHAR(128) PRIMARY KEY`):任务 IDUUID)。
- `point_name` (`TEXT NOT NULL`):网格点权威名(源精度,非 params 重推)。
- `node_id` (`TEXT`):执行节点 ID。
- `task_type` (`VARCHAR(32)`):兼容字段,等于 `tlusty_strategies[0]``cold_run` / `seed_step`),供旧节点识别。
- `seed_point_name` (`TEXT`):种子步进时注入的近邻种子点(`GET /api/seed/<name>` 下载依据)。
- `status` (`VARCHAR(32)`)`pending` / `claimed` / `running` / `completed` / `failed` / `timeout`
- `max_relc` (`REAL`):最终最大相对变化(收敛判据量)。
- `atmosphere_has_nan` (`BOOLEAN`):最终大气是否含 >10% NaN 行(无效化标记)。
- `retry_count` (`INTEGER`):重试计数。
- `created_at` / `started_at` / `completed_at` (`DATETIME`):创建 / 开始 / 完成时间。
- `error_message` (`TEXT`):失败原因(含 synspec 错误/超时等)。
- `workflow_name` (`TEXT`):所属工作流(多工作流分区键)。
- **阶段独立配置快照(派发时落库)**:`tlusty_enabled` (`BOOLEAN`)、`tlusty_policy` (`TEXT`)、
`tlusty_strategies` (`JSON`)、`synspec_enabled` (`BOOLEAN`)、`synspec_policy` (`TEXT`)、
`synspec_strategies` (`JSON`)、`atmosphere_ref` (`TEXT`,显式大气来源点)。
回退时弹 `*_strategies` 链首(见 `task_engine_decoupling_design.md §4.2`),policy/策略链取派发时快照。
- `elapsed_sec` (`REAL`):任务墙钟耗时(详情页 ETA 估算优先用此值)。
### 2.4 `nodes` (计算节点心跳与状态表)
- `node_id` (`TEXT PRIMARY KEY`):节点唯一标识(未指定 `DCTS_NODE_ID` 时自动生成 `node-<uuid>`)。
- `max_slots` (`INTEGER NOT NULL`):节点声明的最大并发计算槽位数(即 `DCTS_MAX_SLOTS`,领用上限)。
- `active_slots` (`INTEGER NOT NULL DEFAULT 0`):当前正在执行的任务数,随 claim 自增、report 完结自减。
- `status` (`VARCHAR(32) NOT NULL DEFAULT 'online'`)`pending_approval`(注册待审批)/ `online`(在线)/ `offline`(心跳超时离线)/ `disabled`(管理员手动停用,保持心跳但不再分发任务)/ `rejected`
- `cpu_usage` / `memory_usage` (`REAL NOT NULL DEFAULT 0.0`):心跳上报的 CPU/内存使用率(仅观测展示用,**不参与任务分发决策**)。
- `last_heartbeat` (`DATETIME NOT NULL`):最后一次心跳上报时间,后台线程据此判定 `online → offline`
- `registration_secret` (`TEXT`):节点注册时下发的一次性凭据,用于 `/node/check_status` 取走专属 token 的二次鉴权。
- `admin_max_slots` (`INTEGER`,可空):管理员强制并发槽位配额(`NULL` = 无限制,沿用物理 `max_slots`),经心跳响应下发给节点动态生效。
### 2.4 `task_queue` (分布式任务队列表 - `mq`)
### 2.5 `node_credentials` (节点 L2 鉴权凭据表)
- `node_id` (`TEXT PRIMARY KEY`):关联 `nodes.node_id`
- `token_hash` (`TEXT NOT NULL`):节点专属 token 的 SHA-256 哈希(**不存明文**)。
- `issued_at` (`DATETIME NOT NULL`):颁发时间。
- `revoked` (`INTEGER NOT NULL DEFAULT 0`):吊销标记(重新颁发 token 时旧行吊销)。
- `raw_token_pending` (`TEXT`):暂存待确认的明文 token(颁发流程过渡用,确认后清除)。
### 2.6 `seeds` (种子缓存池表)
全局共享的已收敛大气 `.7` 索引(跨工作流复用,种子步进热启动数据源)。
- `id` (`INTEGER PRIMARY KEY AUTOINCREMENT`)。
- `point_name` (`TEXT UNIQUE NOT NULL`):种子点权威名(源精度,与磁盘文件名一致)。
- `teff`, `logg`, `loghe`, `logc`, `logn`, `logo` (`REAL NOT NULL`):6 维物理参数(种子匹配距离计算用)。
- `file_path` (`TEXT NOT NULL`)`.7` 大气文件在 `seeds_dir` 的路径。
- `is_clean` (`BOOLEAN NOT NULL DEFAULT 1`):大气是否干净(无 NaN)——仅干净种子可被匹配(`reload_seed_cache` 只加载 `is_clean=1`)。
> 运行期维护内存 `seed_cache` + `seed_index`exact_family 桶索引),`find_best_seed_from_db`
> 先查桶索引(O(1)~O(小)),未命中退化为全量 global 扫描(见 `seed_finder.rs` / `design.md §3`)。
### 2.7 `workflow_progress_snapshots` (工作流进度时间序列表)
后台定期(每分钟)为每个 running 工作流记录进度计数,供详情页进度曲线与 ETA 估算。
- `id` (`INTEGER PRIMARY KEY AUTOINCREMENT`)。
- `workflow_name` (`TEXT NOT NULL`)。
- `ts` (`DATETIME NOT NULL DEFAULT (datetime('now'))`):采样时间。
- `total` / `pending` / `queued` / `running` / `converged` / `failed` (`INTEGER NOT NULL`):该时刻各状态计数。
### 2.8 `task_queue` (分布式任务队列表 - `mq`)
驱动分布式抢占与超时重试的核心表,使用 SQLite `WAL` 模式确保高吞吐并发安全(详见 [`sqlite_queue.rs`](file:///home/fmq/program/tlusty/tl208-s54/dcts/crates/mq/src/sqlite_queue.rs#L65))。
- `task_id` (`VARCHAR(128) PRIMARY KEY`):任务 IDUUID)。
- `payload` (`TEXT NOT NULL`):序列化的 [`TaskSpec`](file:///home/fmq/program/tlusty/tl208-s54/dcts/crates/common/src/models.rs)(含 point_name / params / task_type / seed_point_name / workflow_name 等)。
- `payload` (`TEXT NOT NULL`):序列化的 [`TaskSpec`](file:///home/fmq/program/tlusty/tl208-s54/dcts/crates/common/src/models.rs)(含 point_name / params / task_type / seed_point_name / workflow_name / tlusty_config / synspec_config 等)。
- `status` (`TEXT NOT NULL`)`pending`(就绪待领用)/ `claimed`(已被某 node 领用,计算中)。
- `created_at` (`DATETIME NOT NULL`):推入队列时间(同 `wave` 内 FIFO 排序键)。
- `claimed_at` (`DATETIME`):被领用的时间戳(`requeue_stale_tasks` 据此判定超时回投)。
@@ -129,3 +228,4 @@ erDiagram
1. **WAL (Write-Ahead Logging) 模式**SQLite 连接自动启用 `PRAGMA journal_mode=WAL;``PRAGMA busy_timeout=15000;`,解决多线程/多进程读写锁竞争。
2. **连接池机制**:借助 `r2d2` + `r2d2_sqlite` 维护异步连接池,防止高并发下数据库句柄冲突。
3. **原子 Claim 事务**:任务抢占在单个 `IMMEDIATE` 事务内完成——先 `SELECT ... WHERE status='pending' ORDER BY wave ASC, created_at ASC LIMIT 1` 取队首,再 `UPDATE SET status='claimed', claimed_by_node_id=?`,提交后返回。事务保证同一任务不会被两个 node 同时领用;遇到 `SQLITE_BUSY/LOCKED` 最多退避重试 5 次(详见 [`pop_task`](file:///home/fmq/program/tlusty/tl208-s54/dcts/crates/mq/src/sqlite_queue.rs#L145))。
4. **凭据零明文存储**`node_credentials.token_hash` 只存 SHA-256 哈希;节点注册的一次性 `registration_secret` 也仅在审批前短期有效,避免数据库泄漏直接泄露有效 token。