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:
+121
-37
@@ -46,7 +46,7 @@
|
||||
|
||||
### 1.3 限流机制与错误模型 (Rate Limiting & Unified Error)
|
||||
|
||||
- **API 限流 (Rate Limiting)**:服务端对敏感接口(如 `/login`、`/node/register`)应用了基于 Governor / 漏桶算法的请求限流器。超出速率上限时返回 `429 Too Many Requests`。
|
||||
- **API 限流 (Rate Limiting)**:服务端对敏感接口(如 `/login`、`/node/register`)应用了**滑动窗口**(sliding window)请求限流器(`rate_limit.rs`)。超出速率上限时返回 `429 Too Many Requests`。
|
||||
- **统一错误格式 (AppError)**:所有 RESTful API 的错误响应均格式化为标准 JSON:
|
||||
```json
|
||||
{
|
||||
@@ -103,23 +103,31 @@
|
||||
### 2.2 任务规格 (`TaskSpec`)
|
||||
服务端派发给 Worker 节点的单个计算任务定义。
|
||||
|
||||
- **Rust 定义** ([models.rs](file:///home/fmq/program/tlusty/tl208-s54/dcts/crates/common/src/models.rs#L98-L106)):
|
||||
- **Rust 定义** ([models.rs](file:///home/fmq/program/tlusty/tl208-s54/dcts/crates/common/src/models.rs#L389-L425)):
|
||||
```rust
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct TaskSpec {
|
||||
pub task_id: Uuid,
|
||||
pub point_name: String,
|
||||
pub params: GridPointParams,
|
||||
pub task_type: TaskType, // ColdRun | SeedStep
|
||||
pub seed_point_name: Option<String>,// 步进种子点名称(如适用)
|
||||
pub task_type: TaskType, // ColdRun | SeedStep(兼容字段 = strategies[0])
|
||||
pub seed_point_name: Option<String>, // 步进种子点名称(如适用)
|
||||
pub timeout_sec: u64,
|
||||
pub workflow_name: Option<String>, // 多工作流分区键
|
||||
pub wave: i32, // 难度波次
|
||||
pub tlusty_config: EngineStageConfig, // TLUSTY 阶段配置(enabled/policy/strategies)
|
||||
pub synspec_config: EngineStageConfig, // SYNSPEC 阶段配置
|
||||
pub synspec_params: Option<serde_json::Value>, // SYNSPEC 数值参数(波长范围等)
|
||||
pub atmosphere_ref: Option<String>, // 显式大气来源点(仅 SYNSPEC-only 场景)
|
||||
}
|
||||
```
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum TaskType {
|
||||
ColdRun,
|
||||
SeedStep,
|
||||
`EngineStageConfig`(阶段独立配置,见 `task_engine_decoupling_design.md §3`):
|
||||
```rust
|
||||
pub struct EngineStageConfig {
|
||||
pub enabled: bool,
|
||||
pub policy: String, // skip_converged | force_recompute | skip_failed
|
||||
pub strategies: Vec<String>, // 策略链:["cold_run","seed_step"],失败回退弹首项
|
||||
}
|
||||
```
|
||||
|
||||
@@ -127,6 +135,12 @@
|
||||
```typescript
|
||||
export type TaskType = 'cold_run' | 'seed_step';
|
||||
|
||||
export interface EngineStageConfig {
|
||||
enabled: boolean;
|
||||
policy: string;
|
||||
strategies: string[];
|
||||
}
|
||||
|
||||
export interface TaskSpec {
|
||||
task_id: string;
|
||||
point_name: string;
|
||||
@@ -134,6 +148,12 @@
|
||||
task_type: TaskType;
|
||||
seed_point_name?: string | null;
|
||||
timeout_sec: number;
|
||||
workflow_name?: string | null;
|
||||
wave: number;
|
||||
tlusty_config: EngineStageConfig;
|
||||
synspec_config: EngineStageConfig;
|
||||
synspec_params?: Record<string, unknown> | null;
|
||||
atmosphere_ref?: string | null;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -142,7 +162,7 @@
|
||||
### 2.3 任务上报报告 (`TaskReport`)
|
||||
Worker 节点向服务端上报的任务计算结果。
|
||||
|
||||
- **Rust 定义** ([models.rs](file:///home/fmq/program/tlusty/tl208-s54/dcts/crates/common/src/models.rs#L126-L140)):
|
||||
- **Rust 定义** ([models.rs](file:///home/fmq/program/tlusty/tl208-s54/dcts/crates/common/src/models.rs#L496-L513)):
|
||||
```rust
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct TaskReport {
|
||||
@@ -158,6 +178,7 @@ Worker 节点向服务端上报的任务计算结果。
|
||||
pub elapsed_sec: f64,
|
||||
pub error_message: Option<String>,
|
||||
pub summary_json: String,
|
||||
pub failed_stage: Option<String>, // "tlusty" | "synspec":失败阶段归因,决定弹哪条策略链
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
@@ -187,6 +208,7 @@ Worker 节点向服务端上报的任务计算结果。
|
||||
elapsed_sec: number;
|
||||
error_message?: string | null;
|
||||
summary_json: string;
|
||||
failed_stage?: 'tlusty' | 'synspec' | null;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -237,7 +259,7 @@ Worker 节点向服务端上报的任务计算结果。
|
||||
node_id: string;
|
||||
max_slots: number;
|
||||
active_slots: number;
|
||||
status: 'online' | 'offline';
|
||||
status: 'online' | 'offline' | 'pending_approval' | 'disabled' | 'rejected';
|
||||
cpu_usage: number;
|
||||
memory_usage: number;
|
||||
last_heartbeat: string;
|
||||
@@ -281,9 +303,19 @@ Worker 节点向服务端上报的任务计算结果。
|
||||
export interface WorkflowSummary {
|
||||
name: string;
|
||||
description?: string | null;
|
||||
status: 'idle' | 'running' | 'paused' | 'completed';
|
||||
status: 'idle' | 'initializing' | 'running' | 'paused' | 'completed';
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
stats?: WorkflowListStats | null; // 内联网格点聚合计数(未启动为 null,见 §8.11)
|
||||
}
|
||||
|
||||
export interface WorkflowListStats {
|
||||
total: number;
|
||||
converged: number;
|
||||
failed: number;
|
||||
running: number;
|
||||
cold_run_converged: number;
|
||||
seed_step_converged: number;
|
||||
}
|
||||
|
||||
export interface WorkflowItem {
|
||||
@@ -328,9 +360,12 @@ Worker 节点向服务端上报的任务计算结果。
|
||||
{
|
||||
"status": "pending_approval",
|
||||
"message": "节点注册申请已成功提交!请在管理 Dashboard 控制台上点击【同意接入】授权该节点",
|
||||
"node_token": null
|
||||
"node_token": null,
|
||||
"registration_secret": "<一次性凭据>"
|
||||
}
|
||||
```
|
||||
> `registration_secret`:节点注册时下发的一次性凭据,节点须在后续
|
||||
> `/api/node/check_status` 时回传,才能取走待发 token(防止仅知 node_id 的攻击者抢先冒领)。
|
||||
- `200 OK` (已授权节点带专属 token 刷新配置):
|
||||
```json
|
||||
{
|
||||
@@ -353,15 +388,16 @@ Worker 节点向服务端上报的任务计算结果。
|
||||
|
||||
### 3.2 节点心跳保活 (`POST /api/node/heartbeat`)
|
||||
|
||||
- **处理函数**: [`heartbeat_node`](file:///home/fmq/program/tlusty/tl208-s54/dcts/crates/server/src/api/node.rs#L17-L25)
|
||||
- **处理函数**: [`heartbeat_node`](file:///home/fmq/program/tlusty/tl208-s54/dcts/crates/server/src/api/node.rs#L167-L190)
|
||||
- **函数签名**:
|
||||
```rust
|
||||
pub async fn heartbeat_node(
|
||||
State(state): State<AppState>,
|
||||
Extension(auth_node): Extension<AuthenticatedNode>,
|
||||
Json(req): Json<NodeHeartbeatRequest>,
|
||||
) -> impl IntoResponse
|
||||
```
|
||||
- **鉴权**: 是 (若配置 Token)
|
||||
- **鉴权**: 是(Node 角色必填;`req.node_id` 必须与鉴权身份一致,否则 403)
|
||||
- **请求 Header**: `Content-Type: application/json`
|
||||
- **请求 Body**:
|
||||
```json
|
||||
@@ -376,9 +412,12 @@ Worker 节点向服务端上报的任务计算结果。
|
||||
- `200 OK` (成功):
|
||||
```json
|
||||
{
|
||||
"status": "ok"
|
||||
"status": "ok",
|
||||
"admin_max_slots": 4
|
||||
}
|
||||
```
|
||||
> `admin_max_slots`:管理员强制的并发槽位配额(`null` = 无限制,沿用物理 `max_slots`)。
|
||||
> 节点据此动态调整本地 `effective_max_slots`(见 `dynamic_cpu_slots_design.md`)。
|
||||
- `200 OK` (失败):
|
||||
```json
|
||||
{
|
||||
@@ -415,12 +454,14 @@ Worker 节点向服务端上报的任务计算结果。
|
||||
"node_id": "node-worker-01",
|
||||
"status": "online",
|
||||
"token_status": "active",
|
||||
"token_issued_at": "2026-07-28T12:00:00Z"
|
||||
"token_issued_at": "2026-07-28T12:00:00Z",
|
||||
"admin_max_slots": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
> `token_status` 取值:`active`(有效)/ `none`(无凭据记录)。token 失效靠重发覆盖 hash 实现,不存在「已吊销」中间态。
|
||||
> `admin_max_slots`:管理员强制并发槽位配额(`null` = 无限制),Dashboard 据此渲染节点配额状态。
|
||||
|
||||
#### 2. 同意节点接入申请 (`POST /api/admin/nodes/:node_id/approve`)
|
||||
- **权限**: Admin 角色
|
||||
@@ -459,6 +500,22 @@ Worker 节点向服务端上报的任务计算结果。
|
||||
-H "Authorization: Bearer <ADMIN_TOKEN>"
|
||||
```
|
||||
|
||||
#### 7. 调整节点并发配额 (`POST /api/admin/nodes/:node_id/quota`)
|
||||
- **权限**: Admin 角色
|
||||
- **说明**: 动态调整节点并发槽位配额上限(不重启 Worker 进程,经下一次心跳响应下发生效)。传 `null` 解除限制,恢复物理 `max_slots`。详见 `dynamic_cpu_slots_design.md`。
|
||||
- **请求 Body**:
|
||||
```json
|
||||
{ "admin_max_slots": 4 }
|
||||
```
|
||||
- **响应**:
|
||||
- `200 OK`: `{"success": true, "message": "节点配额已更新"}`
|
||||
- **curl 示例**:
|
||||
```bash
|
||||
curl -X POST http://localhost:8090/api/admin/nodes/node-worker-01/quota \
|
||||
-H "Authorization: Bearer <ADMIN_TOKEN>" -H "Content-Type: application/json" \
|
||||
-d '{"admin_max_slots": 4}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 任务调度与结果上报 API (Task Processing)
|
||||
@@ -470,9 +527,12 @@ Worker 节点向服务端上报的任务计算结果。
|
||||
- **处理函数**: [`claim_task`](file:///home/fmq/program/tlusty/tl208-s54/dcts/crates/server/src/api/task.rs#L15-L25)
|
||||
- **函数签名**:
|
||||
```rust
|
||||
pub async fn claim_task(State(state): State<AppState>) -> impl IntoResponse
|
||||
pub async fn claim_task(
|
||||
State(state): State<AppState>,
|
||||
Extension(auth_node): Extension<AuthenticatedNode>,
|
||||
) -> impl IntoResponse
|
||||
```
|
||||
- **鉴权**: 是 (若配置 Token)
|
||||
- **鉴权**: 是(Node 角色必填;服务端据注入身份校验节点是否被停用)
|
||||
- **请求 Body**: 无
|
||||
- **响应 Schema**:
|
||||
- `200 OK` (有可计算任务):
|
||||
@@ -533,21 +593,23 @@ Worker 节点向服务端上报的任务计算结果。
|
||||
```rust
|
||||
pub async fn report_task(
|
||||
State(state): State<AppState>,
|
||||
Extension(auth_node): Extension<AuthenticatedNode>,
|
||||
mut multipart: Multipart,
|
||||
) -> impl IntoResponse
|
||||
```
|
||||
- **鉴权**: 是 (若配置 Token)
|
||||
- **鉴权**: 是(Node 角色必填;服务端据注入身份做任务归属校验 `verify_task_claim`,防跨节点伪造)
|
||||
- **请求格式**: `multipart/form-data`
|
||||
- Part `report`: JSON 字符串 (映射为 `TaskReport`)
|
||||
- Part `seed_file` *(可选)*: 二进制数据 (收敛网格点的 `.7` 大气结构种子文件)
|
||||
- **响应 Schema**:
|
||||
- `200 OK` (成功):
|
||||
- `200 OK` (成功 / 幂等重放):
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"message": "上报成功"
|
||||
}
|
||||
```
|
||||
> **幂等重放**:已结算任务(终态守卫已吸收)的重复/迟到上报仍返回 200,并补写种子,不重复结算。
|
||||
- `400 Bad Request` (缺少 `report` 字段):
|
||||
```json
|
||||
{
|
||||
@@ -562,7 +624,10 @@ Worker 节点向服务端上报的任务计算结果。
|
||||
"message": "无法解析 params 或 summary_json"
|
||||
}
|
||||
```
|
||||
- **说明**: 当 `converged == true` 且 `atmosphere_has_nan == false` 且包含 `seed_file` 时,服务端会将种子保存至 `seeds_dir/<point_name>/<point_name>.7` 并记入 `seeds` 表。若冷启动任务失败,服务端会自动唤醒 `GridScheduler` 触发针对该网格点的步进回退算法 (Seed-step Fallback)。
|
||||
- `409 Conflict` (任务归属校验失败): 任务被其它节点重新领用、或队列中已无该 task_id(已被结算清理)。此时返回冲突,不写库。
|
||||
- **说明**:
|
||||
- 当 `converged == true` 且 `atmosphere_has_nan == false` 且包含 `seed_file` 时,服务端会将种子原子写入 `seeds_dir/<point_name>/<point_name>.7` 并记入 `seeds` 表(源精度 `point_name`)。
|
||||
- 失败上报(未收敛 / 失败 / 超时)且 `state_changed == true` 时,服务端才触发**策略链回退**(`trigger_strategy_fallback`,按 `failed_stage` 弹 TLUSTY 或 SYNSPEC 策略链);被终态守卫吸收的重复失败(`state_changed=false`)不再触发,杜绝重复派发涡旋(2026-08-02 修复)。
|
||||
- **curl 示例**:
|
||||
```bash
|
||||
curl -X POST http://localhost:8090/api/task/report \
|
||||
@@ -591,6 +656,7 @@ Worker 节点向服务端上报的任务计算结果。
|
||||
- **请求格式**: `multipart/form-data`
|
||||
- Part `report`: 旧版 `conv.json` 的**原文 JSON**(映射为 `ModelSummary`,服务端解析出 `name` / `params` / `converged` / `final_max_relc`)
|
||||
- Part `seed_file`: 二进制数据(`.7` 大气种子文件;收敛点必传)
|
||||
- Part `success_method` *(可选)*: 文本 `cold_run` / `seed_step`。由 `tools/import_results` 依据旧 `conv.json` 的 stages 是否含 `seed_nc` 判定后设置,决定导入点最终归因(缺省按 seed_step 统计)
|
||||
- **Query 参数**: `workflow`(可选,默认 `imported`):目标工作流名,种子导入到该工作流的 `grid_points`。
|
||||
- **命名保真**: `point_name` 取旧 `conv.json` 的 `name` 字段(源精度真名,如 `t20000_g5.0_...`),**逐字符**落库(磁盘目录、`grid_points.name`、`seeds.point_name`),与旧版 Python `gen_input5.model_name` 完全一致。
|
||||
- **幂等**: `ON CONFLICT DO NOTHING` upsert `grid_points`、`conv.json` 与 `.7` 原子覆盖写,可重复运行。
|
||||
@@ -742,15 +808,19 @@ Worker 节点向服务端上报的任务计算结果。
|
||||
"grid_stats": {
|
||||
"total": 512,
|
||||
"pending": 210,
|
||||
"queued": 60,
|
||||
"running": 32,
|
||||
"converged": 260,
|
||||
"failed": 10
|
||||
"failed": 10,
|
||||
"cold_run_converged": 200,
|
||||
"seed_step_converged": 60
|
||||
}
|
||||
}
|
||||
```
|
||||
> `grid_stats` 为**全部工作流的合计**(跨工作流全局聚合)。多工作流并发运行时,此处展示所有
|
||||
> 工作流 grid_points 的汇总进度;如需查看单个工作流的进度,可读取该工作流各自的 grid_points 统计
|
||||
> (`Database::get_grid_summary_stats(Some(workflow_name))`)。
|
||||
> 工作流 grid_points 的汇总进度;`queued` 与 `pending` 分开计数(多工作流分区新语义),
|
||||
> `cold_run_converged` / `seed_step_converged` 为收敛手段归因;如需查看单个工作流的进度,
|
||||
> 可读取该工作流各自的 grid_points 统计(`Database::get_grid_summary_stats(Some(workflow_name))`)。
|
||||
- **curl 示例**:
|
||||
```bash
|
||||
curl -X GET http://localhost:8090/api/status \
|
||||
@@ -881,7 +951,6 @@ Worker 节点向服务端上报的任务计算结果。
|
||||
"success": true,
|
||||
"message": "成功获取工作流详情",
|
||||
"data": {
|
||||
"id": 1,
|
||||
"name": "sdB_cno",
|
||||
"description": "sdB CNO 6D Stellar Atmosphere Grid",
|
||||
"config_yaml": "...",
|
||||
@@ -944,13 +1013,13 @@ Worker 节点向服务端上报的任务计算结果。
|
||||
AxumPath(name): AxumPath<String>,
|
||||
) -> impl IntoResponse
|
||||
```
|
||||
- **说明**: 校验工作流,解析 YAML 中定义的所有 6 维网格坐标点,通过 `GridScheduler::initialize_grid` 展开网格点并计算保序难度 Wave,写入 SQLite 任务队列并开启节点调度。
|
||||
- **说明**: 以原子 CAS(`transition_workflow_to_initializing`)抢占启动权,随后**异步 spawn** 网格初始化:解析 YAML 的 6 维网格点、按 policy 决定点状态(跳过收敛/打回失败)、展开并计算保序难度 Wave、把 pending 点推入 MQ 开启节点调度。接口先返回 200,后台推进为 `running`。
|
||||
- **响应 Schema**:
|
||||
- `200 OK` (成功启动):
|
||||
- `200 OK` (成功启动,后台初始化中):
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "工作流 'sdB_cno' 已成功启动并安排计算任务",
|
||||
"message": "工作流 'sdB_cno' 已进入后台异步建立与挂载流程",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
@@ -962,6 +1031,14 @@ Worker 节点向服务端上报的任务计算结果。
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
- `409 Conflict` (并发启动,初始化抢占失败):
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "工作流 'sdB_cno' 初始化抢占挂起异常,请稍后重试",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
- `404 Not Found`:
|
||||
```json
|
||||
{
|
||||
@@ -1028,7 +1105,6 @@ Worker 节点向服务端上报的任务计算结果。
|
||||
"failed": 3,
|
||||
"cold_run_converged": 220,
|
||||
"seed_step_converged": 41,
|
||||
"imported_converged": 0,
|
||||
"waves": [
|
||||
{ "wave": 0, "total": 108, "converged": 108, "failed": 0 }
|
||||
],
|
||||
@@ -1037,6 +1113,8 @@ Worker 节点向服务端上报的任务计算结果。
|
||||
}
|
||||
}
|
||||
```
|
||||
> 收敛手段归因仅 `cold_run_converged` / `seed_step_converged` 两字段;**无独立
|
||||
> `imported_converged`**——历史导入点统一按 seed_step 途径计入(`success_method` 语义见 §8.8)。
|
||||
> `avg_point_sec` = `AVG(COALESCE(tasks.elapsed_sec, created_at→completed_at 时间戳差))`——
|
||||
> 优先用 Worker 回报的精确墙钟(不含排队等待),历史无 `elapsed_sec` 的行回退时间戳差近似;
|
||||
> `eta_sec` = `avg_point_sec × (total - converged - failed) ÷ 在线节点总槽位`(并发感知;
|
||||
@@ -1048,14 +1126,14 @@ Worker 节点向服务端上报的任务计算结果。
|
||||
|
||||
- **处理函数**: `get_workflow_points`(`crates/server/src/api/workflow.rs`)
|
||||
- **权限**: Admin
|
||||
- **用途**: 详情页"网格点明细"表与"收敛性分析"热力图、概览"最近动态"流的数据源。
|
||||
- **用途**: 详情页"网格点明细"表与"收敛性分析"(Parallel Sets 平行集合图)、概览"最近动态"流的数据源。
|
||||
每行附带**最近一次尝试**信息(`tasks` 关联子查询取最新行,从未派发过的点 `last_*` 为 null)。
|
||||
- **查询参数**(全部可选,枚举值白名单校验,非法 → `400`):
|
||||
|
||||
| 参数 | 取值 | 默认 |
|
||||
| :--- | :--- | :--- |
|
||||
| `status` | `pending`/`queued`/`running`/`converged`/`failed` | 不过滤 |
|
||||
| `method` | `cold_run`/`seed_step`/`imported` | 不过滤 |
|
||||
| `method` | `cold_run`/`seed_step`(白名单**不含** `imported`,传入即 `400`) | 不过滤 |
|
||||
| `wave` | 整数波次 | 不过滤 |
|
||||
| `q` | 点名子串(LIKE 通配符已转义) | 不过滤 |
|
||||
| `sort` | `wave`/`teff`/`max_relc`/`attempts`/`last_completed_at` | `wave` |
|
||||
@@ -1167,13 +1245,17 @@ Worker 节点向服务端上报的任务计算结果。
|
||||
"running": 8, "converged": 261, "failed": 3 }
|
||||
],
|
||||
"rate_per_hour": 12.5,
|
||||
"now": "2026-07-31T09:00:00Z",
|
||||
"done_rate_per_hour": 11.0,
|
||||
"rate_span_hours": 23.5,
|
||||
"stalled_minutes": 0.0
|
||||
}
|
||||
}
|
||||
```
|
||||
> `series` 超 300 条自动降采样(首末点保留);`rate_per_hour` = 窗口首末
|
||||
> converged 增量 ÷ 时长(快照不足 2 条为 null);`stalled_minutes` = 终态数
|
||||
> (converged+failed)最后一次增长至窗口末端的分钟数(前端 >10 分钟触发停滞预警)。
|
||||
> converged 增量 ÷ 时长(快照不足 2 条为 null);`now` = 服务端当前 UTC 时刻(前端锚定曲线右缘);
|
||||
> `done_rate_per_hour` = 终态完成速率(用于 ETA);`rate_span_hours` = 速率统计实际时间跨度;
|
||||
> `stalled_minutes` = 终态数(converged+failed)最后一次增长至窗口末端的分钟数(前端 >10 分钟触发停滞预警)。
|
||||
|
||||
---
|
||||
|
||||
@@ -1213,11 +1295,13 @@ Worker 节点向服务端上报的任务计算结果。
|
||||
| HTTP 状态码 | 触发场景说明 | 响应格式 | 核心原因与解决建议 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **`200 OK`** | 请求正常处理 | JSON / Binary Stream | 操作成功执行。 |
|
||||
| **`400 Bad Request`** | 参数校验失败、缺失关键字段或 YAML 格式错误 | `application/json` / Plain Text | 检查请求 JSON 结构,验证 YAML 配置语法是否正确。 |
|
||||
| **`400 Bad Request`** | 参数校验失败、缺失关键字段、YAML 格式错误或枚举白名单拒绝 | `application/json` / Plain Text | 检查请求 JSON 结构,验证 YAML 配置语法,确认 `method`/`sort`/`order` 等枚举在合法取值内。 |
|
||||
| **`401 Unauthorized`** | 鉴权失败或缺失 Authorization Header | `application/json` | 确认环境变量配置,并在 Request Header 中包含正确的 Token。 |
|
||||
| **`403 Forbidden`** | 身份校验失败(心跳 `node_id` 与鉴权身份不匹配、跨点上报拒绝、角色权限不足) | `application/json` | 确认节点 token 与上报身份一致,检查 RBAC 角色。 |
|
||||
| **`404 Not Found`** | 资源、种子文件或工作流不存在 | `application/json` | 校验请求 URL 中的资源文件名或工作流 `name` 是否拼写无误。 |
|
||||
| **`429 Too Many Requests`** | API 请求超出速率限制(限流生效) | `application/json` | 降低请求频率或配置漏桶/令牌桶容量参数。 |
|
||||
| **`409 Conflict`** | 状态冲突:节点停用/启用状态不支持、start 初始化抢占失败、report 任务归属校验失败 | `application/json` | 检查目标状态是否合法,或稍后重试。 |
|
||||
| **`429 Too Many Requests`** | API 请求超出速率限制(滑动窗口限流生效) | `application/json` | 降低请求频率;限流为滑动窗口实现(`rate_limit.rs`),非漏桶。 |
|
||||
| **`500 Internal Server Error`** | 服务端数据库错误、I/O 打开失败或队列异常 | `application/json` | 检查服务端日志以进一步厘清 SQLite 锁冲突、磁盘空间或资源路径问题。 |
|
||||
|
||||
---
|
||||
*文档生成于 2026-07-27 | DCTS Server 0.1.0*
|
||||
*文档生成于 2026-07-27,2026-08-04 更新以对齐阶段独立配置 / 多工作流隔离 / 节点配额 / 失败阶段归因。*
|
||||
|
||||
Reference in New Issue
Block a user