feat(all): 源精度命名体系、工作流可观测台、节点停用管理与白名单归档
核心变更:
1. GridAxisValue 源精度命名
- 新增 GridAxisValue 类型,携带 f64 数值 + YAML 源书写文本(Deref 透明兼容算术)
- config.rs 绕过 serde_yaml 归一化,逐 token 捕获轴值原文(logg: 5.0 → g5.0)
- runner/executor/scheduler 全链路改用 DB TEXT 列权威 point_name,
修复 REAL 列回读丢精度导致的 model_name 错配
2. 工作流执行可观测台
- 新增 stats/progress/points 三组 API(进度时间序列、经验速率 ETA、
停滞预警、逐点明细分页、收敛性热力图数据)
- 新增 workflow_progress_snapshots 表 + tasks/grid_points 耗时列
- runner 携带 last_iter/worst_depth/n_depths 进 conv.json
- 前端新增 hash 路由、工作流详情页(概览/网格点/收敛分析三 Tab)、YAML 编辑器
3. 节点停用/启用管理
- 新增 disabled 状态 + disable/enable API;停用节点保持心跳但停止分发,
worker 空闲待命而非退出;移除 revoke API,token 失效统一走重发覆盖;
移除 host_name 字段
4. 白名单结果归档
- 新增 result_filter 模块,只归档有语义产物,丢弃 Tlusty 中间单元(~2MB/模型)
- executor 原子写入归档 + 200 点 LRU 上限
5. 历史数据导入
- sync_seeds 重写为 import_results:经 /admin/import_seed 标记 converged +
按新版命名迁移产物树
6. 部署与目录重规划
- data/results→seeds、data/archive→result + migrate_data_dirs.sh
- deploy.sh 增强(SSH 复用、Profile、远程 env);Dockerfile 瘦身
7. 文档同步更新 api/database/architecture/deployment
This commit is contained in:
@@ -11,16 +11,11 @@ use crate::db::Database;
|
||||
pub struct GridScheduler {
|
||||
db: Database,
|
||||
queue: Arc<SqliteTaskQueue>,
|
||||
_results_dir: String,
|
||||
}
|
||||
|
||||
impl GridScheduler {
|
||||
pub fn new(db: Database, queue: Arc<SqliteTaskQueue>, results_dir: String) -> Self {
|
||||
Self {
|
||||
db,
|
||||
queue,
|
||||
_results_dir: results_dir,
|
||||
}
|
||||
pub fn new(db: Database, queue: Arc<SqliteTaskQueue>) -> Self {
|
||||
Self { db, queue }
|
||||
}
|
||||
|
||||
/// Expands grid points from config and registers them into the database.
|
||||
@@ -52,19 +47,19 @@ impl GridScheduler {
|
||||
}
|
||||
let mut points = Vec::new();
|
||||
|
||||
for &teff in &cfg.grid.teff {
|
||||
for &logg in &cfg.grid.logg {
|
||||
for &loghe in &cfg.grid.loghe {
|
||||
for &logc in &cfg.grid.logc {
|
||||
for &logn in &cfg.grid.logn {
|
||||
for &logo in &cfg.grid.logo {
|
||||
for teff in &cfg.grid.teff {
|
||||
for logg in &cfg.grid.logg {
|
||||
for loghe in &cfg.grid.loghe {
|
||||
for logc in &cfg.grid.logc {
|
||||
for logn in &cfg.grid.logn {
|
||||
for logo in &cfg.grid.logo {
|
||||
points.push(GridPointParams {
|
||||
teff,
|
||||
logg,
|
||||
loghe,
|
||||
logc,
|
||||
logn,
|
||||
logo,
|
||||
teff: teff.clone(),
|
||||
logg: logg.clone(),
|
||||
loghe: loghe.clone(),
|
||||
logc: logc.clone(),
|
||||
logn: logn.clone(),
|
||||
logo: logo.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -196,7 +191,7 @@ impl GridScheduler {
|
||||
.await?;
|
||||
let mut dispatched = 0;
|
||||
|
||||
for (name, params, _wave) in pending {
|
||||
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)) => {
|
||||
@@ -217,6 +212,7 @@ impl GridScheduler {
|
||||
seed_point_name: seed_name,
|
||||
timeout_sec,
|
||||
workflow_name: Some(workflow_name.to_string()),
|
||||
wave,
|
||||
};
|
||||
|
||||
self.db.insert_task(&task_spec).await?;
|
||||
@@ -274,6 +270,7 @@ impl GridScheduler {
|
||||
pub async fn trigger_seed_step_fallback(
|
||||
&self,
|
||||
params: &GridPointParams,
|
||||
name: &str,
|
||||
workflow_name: &str,
|
||||
) -> Result<bool> {
|
||||
// 该工作流须仍处于 running 态才回退(避免 stop 后继续派发)
|
||||
@@ -291,9 +288,13 @@ impl GridScheduler {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let name = params.model_name();
|
||||
// name 取自权威的 report.point_name(= grid_points.name 列,源精度正确),
|
||||
// 而非 params.model_name()。原因:此处 params 经 node 上报回传,其 logg 等
|
||||
// 轴在服务端 DB REAL 列回读时已丢精度(5.0→"5"),重推 model_name() 会得到
|
||||
// 降级名(g5 而非 g5.0),导致回退任务的 point_name 与 grid_points.name 列错配,
|
||||
// 状态更新静默失败。与 runner 的修复保持同一原则:用权威 name。
|
||||
// 种子回退仅一次:该点在该工作流中已经派发过 seed_step 任务就不再触发新的回退
|
||||
if self.db.has_seed_step_attempt(&name, workflow_name).await? {
|
||||
if self.db.has_seed_step_attempt(name, workflow_name).await? {
|
||||
info!(
|
||||
"网格点 {} 已使用过一次种子热启动回退,不再重复回退,保持 failed 终态",
|
||||
name
|
||||
@@ -306,30 +307,27 @@ impl GridScheduler {
|
||||
|
||||
if let Some(seed_match) = seed_match_opt {
|
||||
let timeout_sec = self.get_workflow_timeout_sec(workflow_name).await;
|
||||
let name = params.model_name();
|
||||
let task_spec = TaskSpec {
|
||||
task_id: Uuid::new_v4(),
|
||||
point_name: name.clone(),
|
||||
point_name: name.to_string(),
|
||||
params: params.clone(),
|
||||
task_type: TaskType::SeedStep,
|
||||
seed_point_name: Some(seed_match.name.clone()),
|
||||
timeout_sec,
|
||||
workflow_name: Some(workflow_name.to_string()),
|
||||
// seed_step 是失败后的回退任务,wave 设 0 不抢占正常调度队列里的低难度 wave 优先级。
|
||||
wave: 0,
|
||||
};
|
||||
|
||||
self.db.insert_task(&task_spec).await?;
|
||||
self.db
|
||||
.update_grid_status(
|
||||
&name,
|
||||
common::models::GridPointStatus::Queued,
|
||||
workflow_name,
|
||||
)
|
||||
.update_grid_status(name, common::models::GridPointStatus::Queued, workflow_name)
|
||||
.await?;
|
||||
if let Err(e) = self.queue.push_task(&task_spec).await {
|
||||
let _ = self
|
||||
.db
|
||||
.update_grid_status(
|
||||
&name,
|
||||
name,
|
||||
common::models::GridPointStatus::Pending,
|
||||
workflow_name,
|
||||
)
|
||||
@@ -358,7 +356,6 @@ mod tests {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let db_path = temp_dir.path().join("sched_db.db");
|
||||
let queue_db_path = temp_dir.path().join("sched_queue.db");
|
||||
let results_dir = temp_dir.path().join("results");
|
||||
|
||||
let db = Database::new(&db_path.to_string_lossy()).await.unwrap();
|
||||
let queue = Arc::new(
|
||||
@@ -366,20 +363,17 @@ mod tests {
|
||||
.await
|
||||
.unwrap(),
|
||||
);
|
||||
let scheduler = GridScheduler::new(
|
||||
db.clone(),
|
||||
queue.clone(),
|
||||
results_dir.to_string_lossy().to_string(),
|
||||
);
|
||||
let scheduler = GridScheduler::new(db.clone(), queue.clone());
|
||||
|
||||
#[allow(deprecated)] // results 是死字段,构造时必须填 None
|
||||
let cfg = GridConfig {
|
||||
grid: GridAxesConfig {
|
||||
teff: vec![35000.0],
|
||||
logg: vec![5.5],
|
||||
loghe: vec![-1.0],
|
||||
logc: vec![-2.0],
|
||||
logn: vec![-2.0],
|
||||
logo: vec![-2.0],
|
||||
teff: vec![35000.0.into()],
|
||||
logg: vec![5.5.into()],
|
||||
loghe: vec![(-1.0).into()],
|
||||
logc: vec![(-2.0).into()],
|
||||
logn: vec![(-2.0).into()],
|
||||
logo: vec![(-2.0).into()],
|
||||
},
|
||||
chain: vec![],
|
||||
synspec: None,
|
||||
@@ -425,16 +419,17 @@ mod tests {
|
||||
.await
|
||||
.unwrap(),
|
||||
);
|
||||
let scheduler = GridScheduler::new(db.clone(), queue.clone(), "results".to_string());
|
||||
let scheduler = GridScheduler::new(db.clone(), queue.clone());
|
||||
|
||||
#[allow(deprecated)] // results 是死字段,构造时必须填 None
|
||||
let mk_cfg = |teff: f64| GridConfig {
|
||||
grid: GridAxesConfig {
|
||||
teff: vec![teff],
|
||||
logg: vec![5.5],
|
||||
loghe: vec![-1.0],
|
||||
logc: vec![-2.0],
|
||||
logn: vec![-2.0],
|
||||
logo: vec![-2.0],
|
||||
teff: vec![teff.into()],
|
||||
logg: vec![5.5.into()],
|
||||
loghe: vec![(-1.0).into()],
|
||||
logc: vec![(-2.0).into()],
|
||||
logn: vec![(-2.0).into()],
|
||||
logo: vec![(-2.0).into()],
|
||||
},
|
||||
chain: vec![],
|
||||
synspec: None,
|
||||
@@ -466,12 +461,12 @@ mod tests {
|
||||
// 重新推一个 wf_a 任务(上一行 pop 掉了),再初始化 wf_b
|
||||
db.update_grid_status(
|
||||
&GridPointParams {
|
||||
teff: 35000.0,
|
||||
logg: 5.5,
|
||||
loghe: -1.0,
|
||||
logc: -2.0,
|
||||
logn: -2.0,
|
||||
logo: -2.0,
|
||||
teff: 35000.0.into(),
|
||||
logg: 5.5.into(),
|
||||
loghe: (-1.0).into(),
|
||||
logc: (-2.0).into(),
|
||||
logn: (-2.0).into(),
|
||||
logo: (-2.0).into(),
|
||||
}
|
||||
.model_name(),
|
||||
common::models::GridPointStatus::Pending,
|
||||
@@ -496,12 +491,12 @@ mod tests {
|
||||
// 关键断言:把 wf_a 任务重新推回队列后,初始化 wf_b 不应清空它。
|
||||
db.update_grid_status(
|
||||
&GridPointParams {
|
||||
teff: 35000.0,
|
||||
logg: 5.5,
|
||||
loghe: -1.0,
|
||||
logc: -2.0,
|
||||
logn: -2.0,
|
||||
logo: -2.0,
|
||||
teff: 35000.0.into(),
|
||||
logg: 5.5.into(),
|
||||
loghe: (-1.0).into(),
|
||||
logc: (-2.0).into(),
|
||||
logn: (-2.0).into(),
|
||||
logo: (-2.0).into(),
|
||||
}
|
||||
.model_name(),
|
||||
common::models::GridPointStatus::Pending,
|
||||
|
||||
Reference in New Issue
Block a user