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 入库
This commit is contained in:
+20
-3
@@ -6,6 +6,11 @@ import { renderWorkflows } from './components/workflows.js';
|
||||
|
||||
let isPolling = false;
|
||||
let pollTimer = null;
|
||||
// 连续失败退避计数:服务端持续不可达时拉长轮询间隔(指数退避,上限 60s),
|
||||
// 避免空轮询刷请求;恢复正常即重置为基准 5s。
|
||||
let pollFailCount = 0;
|
||||
// 最近一次 cluster 状态拉取是否成功(由 updateServerStatus 维护,供 fetchAllData 判定退避)。
|
||||
let serverOnline = false;
|
||||
|
||||
// 监听浏览器 Tab 标签页切换,切入后台时暂停轮询,切回前台时恢复并立即刷新
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
@@ -44,18 +49,29 @@ export async function scheduleFetchData() {
|
||||
}
|
||||
if (document.hidden) return;
|
||||
|
||||
await fetchAllData();
|
||||
const ok = await fetchAllData();
|
||||
if (ok) {
|
||||
pollFailCount = 0;
|
||||
} else {
|
||||
pollFailCount = Math.min(pollFailCount + 1, 4);
|
||||
}
|
||||
// 指数退避:5s * 2^failCount,clamp 到 [5s, 60s]
|
||||
const delay = Math.min(5000 * Math.pow(2, pollFailCount), 60000);
|
||||
if (isPolling && !document.hidden) {
|
||||
pollTimer = setTimeout(scheduleFetchData, 5000);
|
||||
pollTimer = setTimeout(scheduleFetchData, delay);
|
||||
}
|
||||
}
|
||||
|
||||
// 返回主状态拉取是否成功(用于轮询退避计数)。三项并行拉取,
|
||||
// 以 cluster 状态能否成功获取作为整体健康判定。
|
||||
export async function fetchAllData() {
|
||||
await Promise.all([
|
||||
const results = await Promise.allSettled([
|
||||
fetchStatus(),
|
||||
fetchWorkflows(),
|
||||
fetchNodes(),
|
||||
]);
|
||||
// fetchStatus 内部已 try/catch,rejected 仅在极异常情况;以第一项(状态)成败为准。
|
||||
return results[0].status === 'fulfilled' && serverOnline;
|
||||
}
|
||||
|
||||
export async function fetchStatus() {
|
||||
@@ -92,6 +108,7 @@ export async function fetchNodes() {
|
||||
}
|
||||
|
||||
export function updateServerStatus(online) {
|
||||
serverOnline = !!online;
|
||||
const box = document.getElementById('status-indicator-box');
|
||||
const text = document.getElementById('server-status-text');
|
||||
if (!box || !text) return;
|
||||
|
||||
Reference in New Issue
Block a user