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:
+37
-25
@@ -106,22 +106,23 @@ async fn main() -> Result<()> {
|
||||
);
|
||||
for (wf_name, wf_yaml) in &stuck {
|
||||
match GridConfig::from_yaml_str(wf_yaml) {
|
||||
Ok(cfg) => {
|
||||
match scheduler.initialize_grid(&cfg, wf_name).await {
|
||||
Ok(_) => {
|
||||
let _ = db.update_workflow_status(wf_name, "running").await;
|
||||
info!("启动恢复:工作流 {} 已完成重新初始化并切回 running", wf_name);
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!(
|
||||
"启动恢复:工作流 {} 重新初始化失败,回退为 idle: {}",
|
||||
wf_name,
|
||||
e
|
||||
);
|
||||
let _ = db.update_workflow_status(wf_name, "idle").await;
|
||||
}
|
||||
Ok(cfg) => match scheduler.initialize_grid(&cfg, wf_name).await {
|
||||
Ok(_) => {
|
||||
let _ = db.update_workflow_status(wf_name, "running").await;
|
||||
info!(
|
||||
"启动恢复:工作流 {} 已完成重新初始化并切回 running",
|
||||
wf_name
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!(
|
||||
"启动恢复:工作流 {} 重新初始化失败,回退为 idle: {}",
|
||||
wf_name,
|
||||
e
|
||||
);
|
||||
let _ = db.update_workflow_status(wf_name, "idle").await;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
tracing::warn!(
|
||||
"启动恢复:工作流 {} 的 YAML 配置解析失败,回退为 idle: {}",
|
||||
@@ -188,8 +189,11 @@ async fn main() -> Result<()> {
|
||||
let mut by_wf: std::collections::HashMap<String, Vec<String>> =
|
||||
std::collections::HashMap::new();
|
||||
for (point, wf) in &requeued {
|
||||
// 旧版任务(payload 无 workflow_name)归一到 '__legacy__',
|
||||
// 使 reset_specific_grid_points_to_pending 命中 legacy 网格点
|
||||
// (H1 修复:否则按 '' 更新 0 行,重投后的 legacy 点永远重置不回 pending)。
|
||||
by_wf
|
||||
.entry(wf.clone().unwrap_or_default())
|
||||
.entry(server::db::normalize_workflow_name(wf.as_deref()))
|
||||
.or_default()
|
||||
.push(point.clone());
|
||||
}
|
||||
@@ -218,17 +222,23 @@ async fn main() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
// 孤儿 running 点回收(#6 修复兜底):queue 行已消失(误删/崩溃丢队列)
|
||||
// 但 grid_points 仍卡在 running 的点,requeue_stale_tasks 找不到它们,
|
||||
// 在此按 tasks 表的 stale pending 记录兜底重置为 pending,让调度器重新派发。
|
||||
match bg_db_clone.reset_orphaned_running_points(stale_sec).await {
|
||||
// 孤儿点回收(#6 修复兜底,2026-08-02 涡旋事故重构):queue 凭证已消失
|
||||
// (误删/崩溃丢队列/insert 后 push 前崩溃)但 grid_points 仍卡在
|
||||
// running/queued 的点,requeue_stale_tasks 找不到它们。经 MQ 活性交叉
|
||||
// 校验确认真孤儿后重置为 pending 让调度器重新派发,并清除作为判据的
|
||||
// 僵尸 tasks 行(旧实现仅凭 tasks 表 stale pending 行判定,僵尸行使
|
||||
// 判据恒真 → 重复派发涡旋,已废弃)。
|
||||
match bg_scheduler_clone.reclaim_orphaned_points(stale_sec).await {
|
||||
Ok(reset) => {
|
||||
if reset > 0 {
|
||||
info!("已回收 {} 个孤儿 running 网格点(领用凭证丢失,重置为 pending)", reset);
|
||||
info!(
|
||||
"已回收 {} 个孤儿网格点(领用凭证丢失,重置为 pending)",
|
||||
reset
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!("回收孤儿 running 网格点失败: {}", e);
|
||||
tracing::warn!("回收孤儿网格点失败: {}", e);
|
||||
has_error = true;
|
||||
}
|
||||
}
|
||||
@@ -402,6 +412,10 @@ async fn main() -> Result<()> {
|
||||
"/admin/nodes/:node_id/enable",
|
||||
post(api::admin::enable_node),
|
||||
)
|
||||
.route(
|
||||
"/admin/nodes/:node_id/quota",
|
||||
post(api::admin::set_node_quota),
|
||||
)
|
||||
.layer(DefaultBodyLimit::max(DEFAULT_BODY_LIMIT));
|
||||
|
||||
// 合并两个子 router:各自携带自己的 body limit,互不覆盖。
|
||||
@@ -431,9 +445,7 @@ async fn main() -> Result<()> {
|
||||
let auth_layer = axum::middleware::from_fn_with_state(state.clone(), api::auth_middleware);
|
||||
api_router.layer(auth_layer).layer(rate_limit_layer)
|
||||
} else {
|
||||
info!(
|
||||
"DCTS_AUTH_DISABLE=1 已生效:服务端运行在无鉴权模式(仅限本地调试,切勿用于生产)。"
|
||||
);
|
||||
info!("DCTS_AUTH_DISABLE=1 已生效:服务端运行在无鉴权模式(仅限本地调试,切勿用于生产)。");
|
||||
api_router
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user