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:
@@ -14,11 +14,12 @@ use tracing::{debug, info, warn};
|
||||
/// DCTS 历史计算结果导入工具。
|
||||
///
|
||||
/// 双职责:
|
||||
/// 1. **入库 + 种子库**:经 `/api/admin/import_seed` 把旧版 `run_grid.py` 的收敛结果
|
||||
/// (`conv.json` + `.7` 大气)上传服务端,标记 grid_points 为 converged,避免重算。
|
||||
/// 2. **完整产物树迁移**:把旧目录的全部产物按**新版 node result 格式 + workflow 配置
|
||||
/// 1. **完整产物树迁移**:把旧目录的全部产物按**新版 node result 格式 + workflow 配置
|
||||
/// 的小数精度命名**,拷贝到本地 `data/result/<name>/`(与 node 的 `DCTS_RESULT_DIR`
|
||||
/// 同卷,docker-compose 里 server/node 都挂载 `./data:/app/data`)。
|
||||
/// 2. **入库 + 种子库**(可选):经 `/api/admin/import_seed` 把旧版 `run_grid.py` 的收敛结果
|
||||
/// (`conv.json` + `.7` 大气)上传服务端,标记 grid_points 为 converged,避免重算。
|
||||
/// 仅做本地产物整理时加 `--no-upload` 跳过入库。
|
||||
///
|
||||
/// 命名以 **workflow 配置为准**(`GridConfig::from_yaml_str` 保留源小数,如 `logg: 5.0`
|
||||
/// → `g5.0`),而非旧 conv.json 里的 name —— 配置是当前项目权威。
|
||||
@@ -49,7 +50,7 @@ struct Args {
|
||||
result_dir: PathBuf,
|
||||
|
||||
/// Master 服务端 API 地址(默认: http://127.0.0.1:8090)。
|
||||
/// 若服务端未运行或不想入库,可指向不可达地址,工具会继续完成本地产物树迁移。
|
||||
/// 仅入库步骤使用;加 `--no-upload` 后此参数无效。
|
||||
#[arg(short, long, default_value = "http://127.0.0.1:8090")]
|
||||
server: String,
|
||||
|
||||
@@ -62,6 +63,12 @@ struct Args {
|
||||
/// 服务端 Admin 鉴权令牌(/api/admin/import_seed 需 Admin 角色)。
|
||||
#[arg(short, long)]
|
||||
token: Option<String>,
|
||||
|
||||
/// 仅处理本地文件(扫描 + 产物树迁移),不向服务端 /api/admin/import_seed 入库。
|
||||
/// 用于"先保全/整理文件、暂不标记 grid_points=converged"的场合;之后如需入库,
|
||||
/// 去掉该标志重跑即可(服务端 DB 幂等,只补未入库点)。
|
||||
#[arg(long)]
|
||||
no_upload: bool,
|
||||
}
|
||||
|
||||
/// 一个经校验的待导入历史结果点。
|
||||
@@ -117,7 +124,16 @@ async fn main() -> Result<()> {
|
||||
);
|
||||
|
||||
// 2. 扫描旧版结果目录,匹配到配置里的网格点。
|
||||
let candidates = scan_dir_for_results(&args.dir, &name_map).await?;
|
||||
let mut candidates = scan_dir_for_results(&args.dir, &name_map).await?;
|
||||
// 按 cno_sum 升序排序后再上传(2026-08-04 wave 修复配套):服务端 import_seed 按
|
||||
// "该工作流已有点中 cno_sum 严格小于本点的去重值个数"计算 wave。升序导入保证即使是
|
||||
// 全新工作流,后导入的高 cno 点 rank 也自然递增、无需回头改前面的点,最终波次与
|
||||
// initialize_grid 完全一致(不再依赖文件系统遍历顺序)。
|
||||
candidates.sort_by(|a, b| {
|
||||
candidate_cno_sum(a)
|
||||
.partial_cmp(&candidate_cno_sum(b))
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
});
|
||||
info!(
|
||||
"扫描完成,共找到 {} 个经校验无 NaN 且物理收敛、且命中配置网格点的合格结果!",
|
||||
candidates.len()
|
||||
@@ -146,6 +162,11 @@ async fn main() -> Result<()> {
|
||||
info!("产物树迁移完成:{}/{}", migrated, candidates.len());
|
||||
|
||||
// 4. 经 /api/admin/import_seed 批量入库(标记 converged,避免重算)。
|
||||
if args.no_upload {
|
||||
info!("已指定 --no-upload:跳过服务端入库,仅完成本地产物树迁移。");
|
||||
info!("=== 本地文件处理完成(迁移 {migrated} 个点,未入库)===");
|
||||
return Ok(());
|
||||
}
|
||||
info!("=== 启动 HTTP 结果入库(→ grid_points=converged)===");
|
||||
let ingested = upload_results_to_server(
|
||||
&args.server,
|
||||
@@ -222,6 +243,16 @@ fn param_fingerprint(k: &[f64; 6]) -> String {
|
||||
s
|
||||
}
|
||||
|
||||
/// 从候选结果的 conv.json 提取 `cno_sum`(用于按难度升序排序,保证服务端逐点 rank
|
||||
/// 计算 wave 与 initialize_grid 一致)。解析失败兜底 `f64::MAX`(排到最后,服务端仍会
|
||||
/// 按现有库的 rank 计算其 wave,只是顺序不保证升序)。
|
||||
fn candidate_cno_sum(c: &CandidateResult) -> f64 {
|
||||
serde_json::from_str::<ModelSummary>(&c.summary_json)
|
||||
.ok()
|
||||
.map(|s| s.params.cno_sum())
|
||||
.unwrap_or(f64::MAX)
|
||||
}
|
||||
|
||||
/// 从 workflow 配置派生收敛链最后阶段标签。
|
||||
///
|
||||
/// 旧版 run_one.py 每阶段覆盖同名 .5/.6/.err/nst,只留最后阶段的版本。迁移时需把这些
|
||||
@@ -333,10 +364,7 @@ async fn scan_dir_for_results(
|
||||
// 判定该历史点的收敛途径(供服务端写入 success_method,让导入点融入冷启动/种子步进统计):
|
||||
// 旧 run_grid.py 流程——冷启动失败才改用 seed_step 链重跑,种子链首段 label 为 "seed_nc"。
|
||||
// 故 stages 含 seed_nc → seed_step(种子步进救活);否则 → cold_run(冷启动一次收敛)。
|
||||
let has_seed_nc_stage = summary
|
||||
.stages
|
||||
.iter()
|
||||
.any(|s| s.label == "seed_nc");
|
||||
let has_seed_nc_stage = summary.stages.iter().any(|s| s.label == "seed_nc");
|
||||
let success_method = if has_seed_nc_stage {
|
||||
"seed_step"
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user