将 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 笔误
5.7 KiB
5.7 KiB
动态调整计算节点 CPU 核数(并发槽位)设计方案
状态:已实现(
effective_max_slots+ 心跳响应下发配额 +/api/admin/nodes/:node_id/quota+ 节点详情管理弹窗)。下文为设计原文,保留作实现参照;实际实现以crates/node/src/worker.rs、crates/server/src/api/admin.rs、dashboard/src/components/nodesTable.js为准。
1. 需求背景
目前 DCTS 的计算节点 (Worker) 在启动时通过本地配置确定最大计算槽位 (max_slots),并在心跳和注册时上报给服务端。如果在不重启 Worker 进程的情况下,因为设备资源被其他用户借用等原因,需要临时减少该节点分发的任务数(即缩减 CPU 核数/并发槽位)。
2. 现有架构分析
- Pull 模式:任务的领用是由 Worker 主动发起
POST /api/task/claim的。Worker 内部根据自身的max_slots控制并发领用的数量。 - 状态同步:Worker 定期发送
POST /api/node/heartbeat汇报当前的active_slots(正在运行的任务数)和资源负载。
因为是 Pull 模式,如果在服务端强行拒绝 claim(例如当节点的活跃任务超过管理员设定的配额时返回空),会导致 Worker 不断进行无效的轮询(Busy Loop),浪费网络和日志资源。因此,最优解是让 Worker 能够感知到服务端下发的配额上限,并动态调整其本地的并发控制器。
3. 设计方案:基于心跳响应的配额下发机制 (Heartbeat-based Quota Sync)
3.1 数据库与服务端改造
- DB 结构更新:
在服务端的
nodes表中新增一个可空字段admin_max_slots(INTEGER, Nullable),用于保存管理员强制指定的并发上限。 - 新增管理 API:
- 接口:
POST /api/admin/nodes/:node_id/quota - 权限:Admin 角色
- 请求体:
{"admin_max_slots": 4}(传null表示解除限制,恢复节点的物理槽位上限)
- 接口:
- 心跳响应扩展:
修改
POST /api/node/heartbeat的响应结构,将admin_max_slots的值透传给 Worker:注意:还需在{ "status": "ok", "admin_max_slots": 4 // 如果无限制,则为 null }crates/common/src/models.rs中为NodeInfo增加pub admin_max_slots: Option<i32>字段,确保/api/admin/nodes返回配额数据供 Dashboard 渲染。
3.2 Worker 节点改造
- 解析心跳响应:
目前 Worker 的心跳线程(
crates/node/src/worker.rs)忽略了响应体。需要在crates/common/src/models.rs中定义NodeHeartbeatResponse结构体,并在 Worker 中反序列化,提取admin_max_slots。 - 状态共享:
Worker 并非使用
Semaphore控制并发,而是依赖Arc<AtomicI32>(active_slots) 计数。我们需要在NodeWorker的状态中新增一个effective_max_slots: Arc<AtomicUsize>,以便心跳线程和领用线程共享配额信息。 - 动态调整并发控制逻辑:
- 心跳线程在收到心跳响应后,计算生效配额并更新:
let target_slots = match admin_max_slots { Some(admin_limit) => std::cmp::min(admin_limit as usize, physical_max_slots), None => physical_max_slots }; self.effective_max_slots.store(target_slots, Ordering::Release); - 任务领用(Claim)主循环的条件相应修改为:
let active = self.active_slots.load(Ordering::Acquire); if (active as usize) < self.effective_max_slots.load(Ordering::Acquire) { // ... 领用任务 ... } else { tokio::time::sleep(Duration::from_secs(2)).await; } - 平滑降级:如果缩减时当前正在运行的槽位大于
target_slots,上述if判断会直接失败,Worker 自然进入sleep分支待命。无需强行中断物理计算任务,等现有任务结束后,数量便会自动回落到新配额以下,实现安全平滑降级(即便配额设为 0,也会安全地暂停接收新任务)。
- 心跳线程在收到心跳响应后,计算生效配额并更新:
3.3 前端控制台 (Dashboard) 改造
- 保持表格原状:
外部节点列表的大表维持原状(“CPU 槽位”列仍然显示
占用 / 最大,不作修改,保持界面整洁)。 - 重构操作列交互(收敛至统一弹窗):
- 将“管理操作”列中原有的“启用/停用”、“重发 Token”等多个离散按钮,统一替换为一个三个点 (More Options) 的 SVG 图标按钮。
- 点击该按钮后,利用系统中现有的弹窗样式 (如
.modal-card) 弹出一个节点详情与管理 Modal。
- 节点详情管理弹窗内容:
- 基础信息展示:显示节点的详细状态、心跳时间、资源使用率等。
- 槽位占用情况展示:如果没有配额限制,显示格式为
16/16(占用/最大);如果有配额限制,显示格式为12/12/16(占用/配额/最大),其中“最大”代表节点.env中DCTS_MAX_SLOTS设置的运行使用上限。
- 槽位占用情况展示:如果没有配额限制,显示格式为
- 修改配额 (CPU 核数):提供表单供输入新的配额数字,调用
/api/admin/nodes/:node_id/quota保存;或提供“清除配额”恢复运行使用的最大核数上限。 - 节点调度控制:提供“暂停(停用)/启动(启用)节点”的操作按钮。
- 安全管理:提供“重新颁发 Token”的操作按钮。
- 基础信息展示:显示节点的详细状态、心跳时间、资源使用率等。
4. 方案优势
- 平滑降级:不中断正在运行的 TLUSTY 任务,实现优雅缩容。
- 网络高效:利用现有的高频心跳链路顺带下发配置,不增加额外的 RPC 接口,且避免了服务端阻断 claim 带来的无效轮询。
- 职责清晰:Server 依然是状态中心,Worker 仍旧掌控并发调度,逻辑解耦。