feat(all): 科学计算正确性修复、调度竞态消除、节点优雅退出、安全加固与收敛分析重构
科学计算正确性: - 修复 Fortran 无-E 科学记数法(指数≥100 时 E 被挤掉,如 -1.35+118)导致 发散行被静默跳过、误判收敛的 bug;扩展大气无效检测覆盖 Inf 与 *** 溢出标记 - 种子匹配改为 CNO 有向距离(富金属方向重罚 4×、贫金属方向轻罚 1×), 基于 1191 个真实种子配对回测标定,回测净改善 314 个点 - GridAxisValue 反序列化拒绝非法文本(不再静默 NaN);chmax≤0 显式报错 - ions 行宽列宽对齐真实 fort.5 格式 调度与队列竞态: - 原子选点(IMMEDIATE 事务 SELECT+UPDATE)消除并发调度重复派发 (#5) - 调度互斥锁 + 冷启动优先策略(SeedStep 仅作失败后救援,不再正常路径热启动) - 毒消息 dead_letter 标记防出队死循环;clear_queue 保留 claimed 行 (#6) - 孤儿 running 点回收兜底;stale_sec 默认 7800→21600s(3 倍超时缓冲) 节点生命周期: - SIGTERM+SIGINT 双信号监听(修复 Docker stop 发 SIGTERM 不触发优雅退出) - SlotGuard RAII 防活动 slot 泄漏;子进程超时增加二级 30s wait 防 Fortran hang - SeedStep 种子下载 fail-fast + 沙盒私有副本解耦 LRU 清理竞争 - reqwest Client 增加连接/请求超时;启动清理残留 task_* 沙盒 安全加固: - 节点注册 registration_secret 二次凭据 (H8),恒定时间比对防时序旁路 - token 缓存 generation 机制消除 reissue 后旧 token TOCTOU 复活窗口 - 新增 /api/auth/logout 服务端 session 即时撤销;fail-closed 鉴权启动策略 - 前端 token 迁移 sessionStorage;YAML 高亮改 DOM API 消除 XSS 注入面 - 备份文件权限收紧 0600;点表动态值全面 escapeHtml 前端 Dashboard: - 收敛性分析从热力图重构为 Parallel Sets 平行集合图(6 维+状态轴,手写 SVG 零依赖) - 进度曲线横轴改为真实时间(服务端 now 锚定,停滞期诚实留白);轮询指数退避 - 移除 imported 收敛途径分类,导入点按实际途径 cold_run/seed_step 归类 - 初始化时 /api/auth/check 校验 token;401 toast 提示替代静默 reload 服务端恢复与工具链: - 启动恢复 initializing 态工作流;默认工作流 INSERT-only 不覆盖 API 编辑 - body limit 分层(10MB 不再截断 256MB report);multipart 显式错误处理 - 嵌入二进制原子写(tmp+rename)防半写损坏 - import_results 判定收敛途径透传 success_method;conv.json 格式对齐本项目 - push_import_results.sh 退出码修复 + .bat UTF-8 BOM + scp 上传 - Docker USE_MIRRORS 默认关闭;移除无用 assets 挂载;删除 hosts.ini 入库
This commit is contained in:
@@ -11,11 +11,18 @@ use crate::db::Database;
|
||||
pub struct GridScheduler {
|
||||
db: Database,
|
||||
queue: Arc<SqliteTaskQueue>,
|
||||
/// 调度互斥锁:防止 start_workflow 的即时调度与后台 30s 循环并发进入
|
||||
/// schedule_pending_tasks,消除 TOCTOU 竞态导致的重复派发(#5 修复)。
|
||||
schedule_lock: tokio::sync::Mutex<()>,
|
||||
}
|
||||
|
||||
impl GridScheduler {
|
||||
pub fn new(db: Database, queue: Arc<SqliteTaskQueue>) -> Self {
|
||||
Self { db, queue }
|
||||
Self {
|
||||
db,
|
||||
queue,
|
||||
schedule_lock: tokio::sync::Mutex::new(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Expands grid points from config and registers them into the database.
|
||||
@@ -140,12 +147,17 @@ impl GridScheduler {
|
||||
true
|
||||
}
|
||||
|
||||
/// Enqueues pending grid points into MQ with active seed detection and batching.
|
||||
/// Enqueues pending grid points into MQ with batching(冷启动优先).
|
||||
///
|
||||
/// 多工作流分区(#3 修复):对**每个** running/initializing 工作流分别派发任务,
|
||||
/// 替代原来「全局只一个 running workflow」的 LIMIT 1 假设。各工作流独立 batch、
|
||||
/// 独立 seed 匹配(seeds 仍是全局共享的物理资源池)。
|
||||
/// 替代原来「全局只一个 running workflow」的 LIMIT 1 假设。各工作流独立 batch;
|
||||
/// 正常路径一律派发 ColdRun 冷启动,种子匹配只发生在失败后的
|
||||
/// trigger_seed_step_fallback(seeds 仍是全局共享的物理资源池)。
|
||||
pub async fn schedule_pending_tasks(&self) -> Result<usize> {
|
||||
// 互斥锁:start_workflow 的即时调度与后台 30s 循环可能并发调用本方法,
|
||||
// 两者各自 SELECT 同一批 pending 点会产生重复任务(#5 修复)。
|
||||
let _guard = self.schedule_lock.lock().await;
|
||||
|
||||
let workflows = self.db.get_running_workflow_names().await?;
|
||||
if workflows.is_empty() {
|
||||
return Ok(0);
|
||||
@@ -184,46 +196,35 @@ impl GridScheduler {
|
||||
) -> Result<usize> {
|
||||
let timeout_sec = self.get_workflow_timeout_sec(workflow_name).await;
|
||||
|
||||
// SQL 层直接附加 LIMIT = batch_limit + workflow_name 筛选,完全免除数万点位无谓内存反序列化
|
||||
// 原子选点(#5 修复):IMMEDIATE 事务内完成 SELECT + UPDATE status='queued',
|
||||
// 替代原来 get_pending_grid_points_limit(SELECT)+ update_grid_status(UPDATE)
|
||||
// 的分离操作,杜绝两个并发调度调用 SELECT 到同一批 pending 点的 TOCTOU 竞态。
|
||||
let pending = self
|
||||
.db
|
||||
.get_pending_grid_points_limit(batch_limit, workflow_name)
|
||||
.claim_pending_grid_points(batch_limit, workflow_name)
|
||||
.await?;
|
||||
let mut dispatched = 0;
|
||||
|
||||
for (name, params, wave) in pending {
|
||||
// Check if any seed is available in DB for active SeedStep scheduling(seeds 全局共享)
|
||||
let (task_type, seed_name) = match self.db.find_best_seed_from_db(¶ms).await {
|
||||
Ok(Some(seed_match)) => {
|
||||
info!(
|
||||
"工作流 {} 网格点 {} 匹配到数据库近邻种子 {} (距离: {:.2}),安排 SeedStep 热启动调度",
|
||||
workflow_name, name, seed_match.name, seed_match.distance
|
||||
);
|
||||
(TaskType::SeedStep, Some(seed_match.name))
|
||||
}
|
||||
_ => (TaskType::ColdRun, None),
|
||||
};
|
||||
|
||||
// 冷启动优先:正常调度路径一律走 ColdRun 自包含冷启动(lte 阶段 ltgray="T"
|
||||
// 生成 grey start,不依赖任何种子文件)。SeedStep 仅作为冷启动失败后的救援
|
||||
// 任务出现,由 trigger_seed_step_fallback(见下)派发——「冷启动优先、失败再
|
||||
// 种子步进」的单向语义,避免旧版「正常路径热启动优先 + 节点端缺种子回退冷启
|
||||
// 动链」的双向纠缠。
|
||||
let task_spec = TaskSpec {
|
||||
task_id: Uuid::new_v4(),
|
||||
point_name: name.clone(),
|
||||
params,
|
||||
task_type,
|
||||
seed_point_name: seed_name,
|
||||
task_type: TaskType::ColdRun,
|
||||
seed_point_name: None,
|
||||
timeout_sec,
|
||||
workflow_name: Some(workflow_name.to_string()),
|
||||
wave,
|
||||
};
|
||||
|
||||
self.db.insert_task(&task_spec).await?;
|
||||
// 采用先标记 DB 状态为 Queued 后发 MQ 的时序,防止推入 MQ 后数据库修改异常导向下一轮误重投
|
||||
self.db
|
||||
.update_grid_status(
|
||||
&name,
|
||||
common::models::GridPointStatus::Queued,
|
||||
workflow_name,
|
||||
)
|
||||
.await?;
|
||||
// 点已在 claim_pending_grid_points 的 IMMEDIATE 事务中原子标记为 queued,
|
||||
// 无需再单独 update_grid_status(Queued)。push 失败时回滚为 pending 即可。
|
||||
match self.queue.push_task(&task_spec).await {
|
||||
Ok(_) => {
|
||||
dispatched += 1;
|
||||
@@ -250,6 +251,9 @@ impl GridScheduler {
|
||||
);
|
||||
}
|
||||
let _ = self.queue.remove_task(&task_spec.task_id.to_string()).await;
|
||||
// 同步清理先于 push 插入的 tasks 历史行,避免遗留 pending 历史记录
|
||||
// 污染每点尝试计数统计(attempt_count 依赖 tasks 表聚合)。
|
||||
let _ = self.db.delete_task(&task_spec.task_id).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -333,6 +337,9 @@ impl GridScheduler {
|
||||
)
|
||||
.await;
|
||||
let _ = self.queue.remove_task(&task_spec.task_id.to_string()).await;
|
||||
// 清理先于 push 插入的 tasks 历史行,与上方 schedule_pending_tasks_for_workflow
|
||||
// 回滚口径一致,避免遗留 pending 历史污染尝试计数。
|
||||
let _ = self.db.delete_task(&task_spec.task_id).await;
|
||||
return Err(e);
|
||||
}
|
||||
info!(
|
||||
|
||||
Reference in New Issue
Block a user