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:
+109
-9
@@ -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`):任务 ID(UUID)。
|
||||
- `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`):任务 ID(UUID)。
|
||||
- `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。
|
||||
|
||||
Reference in New Issue
Block a user