feat(all): 源精度命名体系、工作流可观测台、节点停用管理与白名单归档
核心变更:
1. GridAxisValue 源精度命名
- 新增 GridAxisValue 类型,携带 f64 数值 + YAML 源书写文本(Deref 透明兼容算术)
- config.rs 绕过 serde_yaml 归一化,逐 token 捕获轴值原文(logg: 5.0 → g5.0)
- runner/executor/scheduler 全链路改用 DB TEXT 列权威 point_name,
修复 REAL 列回读丢精度导致的 model_name 错配
2. 工作流执行可观测台
- 新增 stats/progress/points 三组 API(进度时间序列、经验速率 ETA、
停滞预警、逐点明细分页、收敛性热力图数据)
- 新增 workflow_progress_snapshots 表 + tasks/grid_points 耗时列
- runner 携带 last_iter/worst_depth/n_depths 进 conv.json
- 前端新增 hash 路由、工作流详情页(概览/网格点/收敛分析三 Tab)、YAML 编辑器
3. 节点停用/启用管理
- 新增 disabled 状态 + disable/enable API;停用节点保持心跳但停止分发,
worker 空闲待命而非退出;移除 revoke API,token 失效统一走重发覆盖;
移除 host_name 字段
4. 白名单结果归档
- 新增 result_filter 模块,只归档有语义产物,丢弃 Tlusty 中间单元(~2MB/模型)
- executor 原子写入归档 + 200 点 LRU 上限
5. 历史数据导入
- sync_seeds 重写为 import_results:经 /admin/import_seed 标记 converged +
按新版命名迁移产物树
6. 部署与目录重规划
- data/results→seeds、data/archive→result + migrate_data_dirs.sh
- deploy.sh 增强(SSH 复用、Profile、远程 env);Dockerfile 瘦身
7. 文档同步更新 api/database/architecture/deployment
This commit is contained in:
+34
-12
@@ -7,6 +7,18 @@ import { renderWorkflows } from './components/workflows.js';
|
||||
let isPolling = false;
|
||||
let pollTimer = null;
|
||||
|
||||
// 监听浏览器 Tab 标签页切换,切入后台时暂停轮询,切回前台时恢复并立即刷新
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.hidden) {
|
||||
if (pollTimer) {
|
||||
clearTimeout(pollTimer);
|
||||
pollTimer = null;
|
||||
}
|
||||
} else if (isPolling) {
|
||||
scheduleFetchData();
|
||||
}
|
||||
});
|
||||
|
||||
export function isAppPolling() {
|
||||
return isPolling;
|
||||
}
|
||||
@@ -26,9 +38,14 @@ export function stopPolling() {
|
||||
}
|
||||
|
||||
export async function scheduleFetchData() {
|
||||
if (pollTimer) clearTimeout(pollTimer);
|
||||
if (pollTimer) {
|
||||
clearTimeout(pollTimer);
|
||||
pollTimer = null;
|
||||
}
|
||||
if (document.hidden) return;
|
||||
|
||||
await fetchAllData();
|
||||
if (isPolling) {
|
||||
if (isPolling && !document.hidden) {
|
||||
pollTimer = setTimeout(scheduleFetchData, 5000);
|
||||
}
|
||||
}
|
||||
@@ -68,11 +85,6 @@ export async function fetchNodes() {
|
||||
const json = await fetchNodesList();
|
||||
if (json.success && Array.isArray(json.data)) {
|
||||
renderNodesTable(json.data);
|
||||
return;
|
||||
}
|
||||
const data = await fetchClusterStatus();
|
||||
if (data.nodes) {
|
||||
renderNodesTable(data.nodes);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('获取计算节点列表失败:', err);
|
||||
@@ -101,13 +113,20 @@ export function updateUI(data) {
|
||||
|
||||
const valActiveNodes = document.getElementById('val-active-nodes');
|
||||
const valTotalSlots = document.getElementById('val-total-slots');
|
||||
const slotsProgressFill = document.getElementById('slots-progress-fill');
|
||||
if (valActiveNodes) valActiveNodes.textContent = activeNodesCount;
|
||||
if (valTotalSlots) {
|
||||
valTotalSlots.textContent = `${activeSlots} / ${maxSlots} CPU 槽位占用`;
|
||||
}
|
||||
if (slotsProgressFill) {
|
||||
const pct = maxSlots > 0 ? Math.min(100, Math.max(0, Math.round((activeSlots / maxSlots) * 100))) : 0;
|
||||
slotsProgressFill.style.width = `${pct}%`;
|
||||
}
|
||||
|
||||
const stats = data.grid_stats || {};
|
||||
const pending = stats.pending ?? data.pending_points ?? 0;
|
||||
// 后端已将 queued 从 pending 拆出(详情页需区分"未入队/排队中");
|
||||
// 首页"待计算网格点"保持传统合并口径 = pending + queued。
|
||||
const pending = (stats.pending ?? data.pending_points ?? 0) + (stats.queued ?? 0);
|
||||
const running = stats.running ?? data.running_points ?? 0;
|
||||
const converged = stats.converged ?? data.converged_points ?? 0;
|
||||
const failed = stats.failed ?? data.failed_points ?? 0;
|
||||
@@ -122,13 +141,16 @@ export function updateUI(data) {
|
||||
|
||||
const valCompletedTasks = document.getElementById('val-completed-tasks');
|
||||
const valCompletionRate = document.getElementById('val-completion-rate');
|
||||
const convergedProgressFill = document.getElementById('converged-progress-fill');
|
||||
if (valCompletedTasks) valCompletedTasks.textContent = completed.toLocaleString();
|
||||
if (valCompletionRate) {
|
||||
valCompletionRate.textContent = `${converged.toLocaleString()} 收敛 / ${failed.toLocaleString()} 未收敛`;
|
||||
}
|
||||
|
||||
const valTotalWorkflows = document.getElementById('val-total-workflows');
|
||||
if (valTotalWorkflows && data.total_workflows !== undefined) {
|
||||
valTotalWorkflows.textContent = data.total_workflows;
|
||||
if (convergedProgressFill) {
|
||||
const totalModels = pending + running + completed;
|
||||
const pct = totalModels > 0 ? Math.min(100, Math.max(0, Math.round((converged / totalModels) * 100))) : 0;
|
||||
convergedProgressFill.style.width = `${pct}%`;
|
||||
}
|
||||
|
||||
// 工作流总数由 renderWorkflows 从列表单独写入,避免与此处双源覆盖造成数字抖动
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user