Files
DCTS/docs/architecture.md
T
fmq c8fd24b120 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 入库
2026-08-01 17:01:40 +08:00

9.3 KiB
Raw Blame History

DCTS 系统架构 (System Architecture)

介绍 DCTS (Distributed Computing TLUSTY/SYNSPEC) 的整体设计架构、Master-Worker 拓扑结构、任务生命周期流转及容错机制。


1. 架构拓扑 (Topology)

DCTS 采用中心化控制、分布式离散执行 (Master-Worker) 的拓扑设计:

flowchart TB
    subgraph Master Node ["Master 服务端 (server)"]
        API["Axum HTTP REST API"]
        DB[(主数据库 dcts.db)]
        MQ[(任务队列 dcts_queue.db)]
        Scheduler["Grid Scheduler 网格调度器"]
        SeedsStorage["本地种子库 seeds/*.7"]
        
        API <--> DB
        API <--> MQ
        Scheduler --> MQ
        Scheduler --> DB
    end

    subgraph Compute Nodes ["分布式计算节点集群 (node)"]
        Worker1["Worker 节点 1 (Node Daemon)"]
        Worker2["Worker 节点 2 (Node Daemon)"]
        WorkerN["Worker 节点 N (Node Daemon)"]
    end

    Worker1 -- "1. 心跳/抢占任务 (Claim)" --> API
    Worker1 -- "2. 下载种子/基础数据" --> API
    Worker1 -- "3. 汇报结果/上传 .7 大气" --> API

    Worker2 -- "免凭据注册申请 / 审批后心跳" --> API
    WorkerN -- "免凭据注册申请 / 审批后心跳" --> API

    API --> SeedsStorage

2. 核心组件职责

2.1 Master 服务端 (server & Web dashboard)

  • 工作流与多任务隔离:支持多工作流并发隔离运行(grid_pointstask_queue 增加 workflow_name 复合唯一索引),调度与重置操作严格隔离在单工作流作用域内。支持旧版 SQLite 数据库启动时无缝平滑迁移。
  • 安全中间件与 RBAC:基于角色访问控制 (Admin / Node / Public) 划分 API 权限,内嵌 Bearer Token 校验、Governor / 漏桶算法限流中间件与 CORS 跨域控制,防御侧信道攻击与暴力破解。
  • 节点凭据生命周期管理:支持 Worker 节点注册申请、管理员审批授权、Token 重新颁发(旧 token 失效)与节点停用/启用全生命周期管理。
  • 任务调度与分配:调度器将 pending 网格点推入 mq 队列;Worker 以 pull 抢占式 领用任务(详见 §5 任务调度模型)。
  • 状态维护与心跳监测:后台离线检测线程定期标记超时未心跳的节点为 offline,并能将僵挂在超时节点上的任务自动回收到队列中(Requeue)。
  • ESM 模块化 Web 看板:前端采用 ESM 模块解耦设计(state.js, api.js, components/),支持节点凭据管理、工作流控制与全局 Toast 通知。

2.2 Worker 计算节点 (node)

  • 身份标识DCTS_NODE_ID 未指定或为空时,自动生成基于随机 UUID 的节点 ID(node-<uuid>)。Worker 是计算服务,每个物理设备部署一个 node 进程(多 slot 并行由 DCTS_MAX_SLOTS 控制),无需在同一设备运行多副本。
  • 环境自适应预热 (Bootstrap):启动时核对本地 ./runtime 运行依赖,缺失时自动向 Master 拉取可执行文件与二进制数据。
  • 任务抢占与执行 (Claim & Execute):根据并发配置轮询抢占任务,调用 common 启动子进程链(tlusty / synspec)。
  • 种子检索与回传 (Seed Sync):计算成功后将收敛的大气结构文件(.7)与状态 JSON 汇报回服务端。

3. 任务生命周期 (Task Lifecycle)

网格计算点从创建到完成的状态流转如下图所示:

stateDiagram-v2
    [*] --> Pending : 工作流注册生成网格点
    Pending --> Queued : 调度器推入 mq 队列
    Queued --> Running : Worker 成功 Claim 抢占

    state Running {
        [*] --> ExecutingChain
        ExecutingChain --> ColdStartChain : 默认冷启动 (lte->nc->nl)
        ColdStartChain --> Synspec : 物理收敛
        ColdStartChain --> SeedStepChain : 冷启动发散且有可邻近种子
        SeedStepChain --> Synspec : 热启动收敛
    }

    Running --> Converged : 计算成功 & 上传 .7 产物
    Running --> Pending : Worker 心跳超时/掉线 (Requeue, 直接回 Pending 等待重新派发)
    Running --> Failed : 无可邻近种子回退 / 彻底发散

    Converged --> [*]
    Failed --> [*]

状态值与 GridPointStatus 一一对应: pendingqueuedrunningconverged / failed。 Requeue 把 running 直接重置回 pending(不经过 queued),由调度器下一轮重新推入队列。


4. 容错与高可用设计 (Fault Tolerance)

  1. 分布式无状态 Worker & 上报阶梯退避:Worker 节点不保存持久运行控制状态,异常宕机不会损坏主数据集。计算结果向 Master 上报时,具备多达 8 次指数阶梯容灾回退(最大间隔 60 秒,覆盖超 2 分钟断断连长窗),稳健保障长时间高密物理算单不受瞬时组网闪断或服务端短时上线切换干预。
  2. 零文件扫描与连接并发缩流:底层任务分批取配、排队清洗与邻接优化(Seed-Stepping)全链线依赖常驻内存的 SQlite 主从精算并调优收敛连接池配置(主库=8,队列=4 减免本地排他写冲突并发挂断);去除了历史残存的高损及同步阻塞磁盘遍历 API,在确保零卡死响应的前提下提升查询搜索效力。
  3. 任务超时与流控平稳保护 (Stale & Requeue):服务端后台定期向已超时死挂的 Running (默认 >1800 秒)作业予以强退回转为 Pending;此外当操作维护员发起暂停或终止工作流行为时,将仅平滑洗退待调 Queued 项,悉心保育在途已投的 Worker 数值演算完整出计算归表,防假命题竞合。
  4. 多级退避与种子隔离:若某点冷启动发散,自动隔离失败现场,依靠数据库记录寻找欧氏空间距离最匹配的热启动合拢 .7 气象序列;即便遇到底层强硬大步发散也不产生干扰并留存物理运行根系以便溯源。

5. 任务调度模型 (Task Scheduling)

5.1 Pull 抢占式模型(非服务端负载分发)

DCTS 的任务分发是 Worker 主动拉取(pull 模型,不是服务端按节点负载推派(push):

  • 调度器schedule_pending_tasks)只负责把 pending 网格点推入 mq 队列(push_task),不参与"分给哪个 node"的决策。
  • Worker领用主循环)只要 active_slots < max_slots,就持续向服务端发起 claim 请求抢任务。
  • 服务端 pop_task 的出队决策只看任务自身属性,不看谁来领用:谁先抢到写锁、谁就拿到队首任务。

出队排序(难度优先 → 同难度先进先出):

SELECT task_id, payload FROM task_queue
WHERE status = 'pending'
ORDER BY wave ASC, created_at ASC
LIMIT 1

5.2 为什么不做服务端负载分发

关键观察:Worker 满载后会主动停止领用worker.rs):

let active = self.active_slots.load(Ordering::Acquire);
if (active as usize) < self.config.max_slots {
    match self.claim_task().await { /* 抢任务 */ }
} else {
    sleep(Duration::from_secs(2)).await;  // 满载:不 claim,等槽位释放
}

由此分两种工况:

工况 条件 是否会扎堆 负载分发是否需要
稳态 任务数 ≥ 所有 node 总槽位 否——各 node 抢满 max_slots 即停止 claim,空出槽位者再去抢,天然均衡 不需要
稀疏态 任务数 < 总槽位(如调试用 2 个点) 是——最快的 node 连续抢光少量任务 收益极小,省的仅是几个任务的串行墙钟时间

稳态(网格规模任务,如 sdB_cno 的 432 点)是实际生产场景,该场景下 pull 模型已自动达成负载均衡。稀疏态的扎堆是固有现象且代价可忽略(任务跑完即结束)。

引入服务端负载分发的代价反而更高:破坏 FIFO 可预测性、带来心跳负载数据滞后导致的退让抖动、增加队首饥饿风险。综合权衡,当前 pull 模型是刻意选择,无需改为服务端负载分发

稀疏态调试建议:若调试时确实希望任务分散到多个 node 观察,最省事的做法是把各 node 的 DCTS_MAX_SLOTS 设为 1——每个 node 抢 1 个即满载停止,自然分散,零代码改动、零风险。

5.3 出队优先级语义(wave + FIFO)

  • wave ASC(难度优先):网格点按 CNO 丰度之和(cno_sum)升序分组,低丰度(低难度、更易收敛)的点先出队。设计意图是"先易后难"——先跑出收敛点作为种子库,后续高难度点可借热启动种子收敛。
  • created_at ASC(同难度 FIFO:同一 wave 内严格按推入队列的时间先后出队。
  • 跨工作流公平:出队不按 workflow_name 分组,多个并发工作流的低难度点会先于任何工作流的高难度点出队,避免某工作流在高难度 wave 上饿死其他工作流的低难度点。