feat(all): 数据库模块化拆分与版本化迁移、任务引擎命名体系收敛、物理输出校验加固与用户配置接通

- server/db: 拆 4929 行 db.rs 单体为 db/ 目录,migrations.rs 引入 PRAGMA user_version
    版本化迁移运行器(M1~M13)
  - 任务引擎 Phase 6/7b/7c 改名收敛:EngineStageConfig→PhaseConfig、StagePolicy→ResumePolicy、
    Converged→Completed、删除 task_type 列、success_method 拆 tlusty_/synspec_ 双列、
    新增 tlusty_status/synspec_status 半失败阶段守卫
  - 科学正确性加固:conv_check 任意行 NaN/Inf/溢出判无效(0 行容忍)、新增 spec_is_valid
    校验 SYNSPEC 脏谱、itek_history 逐次迭代全量保真、fmt_abn powf 溢出饱和
  - 用户配置真正接通:tlusty_chain/tlusty_input 由死字段经 调度器→TaskSpec→executor→runner
    透传生效;config 加载期 validate + deny_unknown_fields + 解析失败记 warn
  - 调度修复:H1 活锁(pending_strategies 跳过已失败策略)、种子查找错误不再静默降级冷启动
  - dashboard: 阶段配置面板 tlusty_stage/synspec_stage、"已完成"标签、迭代诊断展示
  - docs: 新增 database_refactor_design.md,同步 database/api/PIPELINE/workflow_detail
This commit is contained in:
fmq
2026-08-06 20:51:21 +08:00
parent cd370d88e7
commit d16b3d3cdc
61 changed files with 10268 additions and 5881 deletions
+20 -19
View File
@@ -24,7 +24,7 @@
> - **未实现**:§2.2/§2.4 的「重试失败点」按钮与 `POST .../points/retry` 端点
> (前后端均未实现)。
> - **方法归因**`imported` 不再作为独立收敛途径(导入点按 seed_step 统计),
> `success_method` 过滤白名单 `cold_run` / `seed_step`。
> 大气方法过滤白名单 `cold_run` / `seed_step`(映射 `tlusty_success_method`);另有 `synspec_only` 光谱专用点过滤器(`tlusty IS NULL AND synspec IS NOT NULL`
---
@@ -35,12 +35,12 @@
| 数据 | 位置 | 说明 |
|---|---|---|
| 网格点 6 维参数、`cno_sum``wave` | `grid_points` 表(`db.rs:246-263` | 按 `workflow_name` 分区,复合唯一 `(workflow_name, name)` |
| 点状态 `pending/queued/running/converged/failed` | `grid_points.status` | `GridPointStatus``models.rs:235-243` |
| **成功手段** `cold_run` / `seed_step` / `imported` | `grid_points.success_method` | 收敛时由成功任务的 `task_type` 回填(`db.rs:1097-1101`);失败点为 NULL |
| 点状态 `pending/queued/running/completed/failed``completed` 由 7c 改名自 `converged` | `grid_points.status` | `GridPointStatus``models.rs:235-243` |
| **大气成功手段** `cold_run` / `seed_step` / `imported` | `grid_points.tlusty_success_method` | TLUSTY 阶段收敛时由成功任务的 `tlusty_strategies[0]` 派生回填(Phase 6 起 `task_type` 列已删除);TLUSTY 禁用(synspec-only)为 NULL。光谱阶段另存 `synspec_success_method`Phase 5a,取 `synspec_strategies[0]`)。整体归因由消费方派生 `tlusty ?? synspec`P9 拆分,原 `success_method` 值域混用列已删) |
| 重试次数 | `grid_points.attempt_count` | 每次回报 +1`db.rs:1089-1095` |
| 每次尝试的收敛指标 | `tasks.max_relc` | 收敛判据 `max_relc < chmax`(默认 0.001`conv_check.rs:105-109` |
| **每次尝试的种子来源** | `tasks.seed_point_name` | 派发时写入(`scheduler.rs:194-205` |
| 每次尝试的方法 / 错误 / 节点 / 完成时间 | `tasks.task_type / error_message / node_id / completed_at` | 一个点多行(每次尝试一行),工作流删除前长期保留 |
| 每次尝试的方法 / 错误 / 节点 / 完成时间 | `tasks.tlusty_strategies[0] / error_message / node_id / completed_at` | 一个点多行(每次尝试一行),工作流删除前长期保留;方法取自策略链首项(`task_type` 列已删) |
| 全局种子库 | `seeds` 表 + `data/seeds/<name>/<name>.7` | 仅收敛且无 NaN 的点入库(`task.rs:187-207` |
| **逐阶段完整诊断**lte→nc→nl / seed_nc→nl | `data/seeds/<name>/conv.json` | `ModelSummary``models.rs:376-391`):每阶段 `converged / best_max_relc / chmax / elapsed_sec`、总耗时、`synspec_sec`、所用种子。**成功与失败的点都会写**(`task.rs:178-184` |
| 按工作流的统计聚合 | `db.get_grid_summary_stats(Some(wf))` | **已实现且有单测**`db.rs:1582-1639`),但没有任何 HTTP 端点调用它 |
@@ -67,7 +67,7 @@
所以"哪些参数冷启动能成功" ≈ 低 cno_sum / 中低温区的点;高温 He-poor 区几乎全靠种子步进。
- **还有一次性失败回退**:冷启动发散且存在邻居种子时,自动以 `seed_step` 重投一次
`scheduler.rs:270-350`,受 `seed_step_fallback` 配置与 `has_seed_step_attempt` 一次性闸门约束)。
因此 `success_method='seed_step'` 的点可能是"冷启动失败后被救回"的——该归因需从 `tasks` 多行历史还原。
因此 `tlusty_success_method='seed_step'` 的点可能是"冷启动失败后被救回"的——该归因需从 `tasks` 多行历史还原。
- **第三类 `imported`**:历史 `run_grid.py` 结果导入(`db.rs:966-981`),UI 需单独呈现为"历史导入"。
- **收敛判据**:末次迭代最差深度点的最大相对修正 `max_relc < chmax`(默认 1e-3),
且大气 NaN 占比 ≤ 10%`conv_check.rs:127-149`)。
@@ -215,9 +215,9 @@
"data": {
"name": "sdB_cno", "status": "running",
"total": 432,
"pending": 120, "queued": 40, "running": 8, "converged": 261, "failed": 3,
"cold_run_converged": 220, "seed_step_converged": 41, "imported_converged": 0,
"waves": [ {"wave": 0, "total": 108, "converged": 108, "failed": 0}, ... ],
"pending": 120, "queued": 40, "running": 8, "completed": 261, "failed": 3,
"cold_run_converged": 220, "seed_step_converged": 41,
"waves": [ {"wave": 0, "total": 108, "completed": 108, "failed": 0}, ... ],
"avg_point_sec": 740.0, "eta_sec": 9600
}
}
@@ -242,11 +242,11 @@
"name": "t60000_g5.0_he-2_c-4_n-4_o-4",
"teff": 60000, "logg": 5.0, "loghe": -2, "logc": -4, "logn": -4, "logo": -4,
"cno_sum": -12, "wave": 0,
"status": "converged", "success_method": "seed_step", "attempt_count": 2,
"last_max_relc": 0.00043, "last_task_type": "seed_step",
"status": "completed", "tlusty_success_method": "seed_step", "synspec_success_method": "standard", "attempt_count": 2,
"last_max_relc": 0.00043,
"seed_point_name": "t60000_g5.0_he2_c-4_n-4_o-4",
"node_id": "node-a1b2", "last_completed_at": "2026-07-30T11:12:00Z",
"elapsed_sec": 126.4
"last_elapsed_sec": 126.4
}
]
}
@@ -256,7 +256,7 @@
- "最近一次尝试"用关联子查询取 `tasks` 最新行:
```sql
SELECT gp.*, t.max_relc AS last_max_relc, t.task_type AS last_task_type,
SELECT gp.*, t.max_relc AS last_max_relc,
t.seed_point_name, t.node_id, t.completed_at AS last_completed_at,
t.error_message AS last_error
FROM grid_points gp
@@ -283,12 +283,13 @@ LIMIT ? LIMIT OFFSET ?;
"data": {
"point": { /* */ },
"attempts": [
{"task_id": "...", "task_type": "cold_run", "seed_point_name": null,
{"task_id": "...", "seed_point_name": null, // Phase 6 起无 task_type 字段
"status": "failed", "max_relc": 954000.0, "atmosphere_has_nan": true,
"node_id": "node-a1b2", "error_message": "nl stage diverged...",
"created_at": "...", "completed_at": "..."},
{"task_id": "...", "task_type": "seed_step", "seed_point_name": "t60000_...",
"status": "completed", "max_relc": 0.00043, ...}
"failed_stage": "tlusty", "summary_json": "{...}",
"created_at": "...", "completed_at": "...", "elapsed_sec": 1800.0},
{"task_id": "...", "seed_point_name": "t60000_...", // 第二次尝试(seed_step 热启动救回)
"status": "completed", "max_relc": 0.00043, "failed_stage": null, ...}
],
"conv": { /* data/seeds/<name>/conv.json ModelSummary null */ }
}
@@ -304,7 +305,7 @@ LIMIT ? LIMIT OFFSET ?;
请求体:`{"names": ["t80000_..."], "all_failed": true}`(二选一)。
- 前置:工作流必须 `running`(否则 400,提示"请先启动工作流"——避免重置后无调度器消费的僵尸态)。
- 逻辑:新 db 函数 `reset_failed_points(wf, names|all)`
`UPDATE grid_points SET status='pending', success_method=NULL WHERE workflow_name=? AND status='failed' [AND name IN (...)]`
`UPDATE grid_points SET status='pending', tlusty_success_method=NULL, synspec_success_method=NULL WHERE workflow_name=? AND status='failed' [AND name IN (...)]`
随后立即触发一次 `schedule_pending_tasks()`(不必等 30s tick)。
- 重试后的方法仍由调度器按种子可用性自动决定(大概率 seed_step,因为此时种子池已更丰富)——
这与领域语义一致,前端文案如实说明:"重试的点将由调度器自动选择冷启动或种子步进"。
@@ -316,7 +317,7 @@ LIMIT ? LIMIT OFFSET ?;
```json
{"name": "...", "status": "...", "description": "...",
"created_at": "...", "updated_at": "...",
"stats": {"total": 432, "converged": 261, "failed": 3, "running": 8,
"stats": {"total": 432, "completed": 261, "failed": 3, "running": 8,
"cold_run_converged": 220, "seed_step_converged": 41}}
```
@@ -332,7 +333,7 @@ LIMIT ? LIMIT OFFSET ?;
2. `StageSummary` 携带 `last_iter / worst_depth / n_depths``models.rs:364-373` 扩字段,
`runner.rs:302-317``ConvCheckResult` 已有值,只是没搬过去)→ conv.json 与点详情获得迭代数。
3. 新表 `workflow_progress_snapshots(workflow_name TEXT, ts DATETIME, pending INT, queued INT,
running INT, converged INT, failed INT)`:由 `main.rs:111-197` 的 30s 后台循环顺手写一行;
running INT, completed INT, failed INT)`:由 `main.rs:111-197` 的 30s 后台循环顺手写一行;
保留策略:仅当计数相对上次快照有变化才写;定期清理 24h 前的行。供"进度-时间"曲线与精确 ETA。
### 3.3 明确不做 / 保持现状