fix(hub): prevent monitor board stuck on slow aggregate polling
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -8,8 +8,10 @@
|
||||
let expandedExchangeId = sessionStorage.getItem("hub_expanded_ex") || "";
|
||||
const HUB_MONITOR_BOARD_CACHE_KEY = "hub_monitor_board_v1";
|
||||
const HUB_MONITOR_CACHE_MAX_AGE_MS = 6 * 60 * 60 * 1000;
|
||||
let monitorBoardFetchSeq = 0;
|
||||
const HUB_MONITOR_FETCH_TIMEOUT_MS = 55000;
|
||||
let lastMonitorBoardUpdatedAt = "";
|
||||
let monitorBoardInFlight = false;
|
||||
let monitorBoardSlowHintTimer = null;
|
||||
|
||||
async function apiFetch(url, opts) {
|
||||
const r = await fetch(url, opts);
|
||||
@@ -344,10 +346,33 @@
|
||||
}
|
||||
|
||||
function stopMonitorPoll() {
|
||||
clearTimeout(monitorTimer);
|
||||
clearInterval(monitorTimer);
|
||||
monitorTimer = null;
|
||||
}
|
||||
|
||||
function clearMonitorBoardSlowHint() {
|
||||
if (monitorBoardSlowHintTimer) {
|
||||
clearTimeout(monitorBoardSlowHintTimer);
|
||||
monitorBoardSlowHintTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleMonitorBoardSlowHint(box) {
|
||||
clearMonitorBoardSlowHint();
|
||||
if (!box) return;
|
||||
monitorBoardSlowHintTimer = setTimeout(() => {
|
||||
if (lastMonitorRows.length) return;
|
||||
const el = box.querySelector(".board-loading");
|
||||
if (!el) return;
|
||||
const sub = el.querySelector(".board-loading-sub");
|
||||
if (sub) {
|
||||
sub.textContent =
|
||||
"聚合较慢(四所子代理 + Flask)。可检查 PM2、或设 HUB_BOARD_KEY_PRICES=false 加速;下方超时后会提示错误。";
|
||||
}
|
||||
}, 12000);
|
||||
}
|
||||
|
||||
function saveMonitorBoardCache(rows, updatedAt) {
|
||||
try {
|
||||
sessionStorage.setItem(
|
||||
@@ -414,13 +439,22 @@
|
||||
renderMonitorGrid(rows || []);
|
||||
}
|
||||
|
||||
function scheduleNextMonitorPoll() {
|
||||
stopMonitorPoll();
|
||||
if (!document.getElementById("auto-monitor")?.checked) return;
|
||||
if (currentPage() !== "monitor") return;
|
||||
monitorTimer = setTimeout(async () => {
|
||||
await loadMonitorBoard({ background: true });
|
||||
scheduleNextMonitorPoll();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
function startMonitorPoll() {
|
||||
stopMonitorPoll();
|
||||
const hadCache = restoreMonitorBoardFromCache();
|
||||
loadMonitorBoard({ background: hadCache });
|
||||
if (document.getElementById("auto-monitor").checked) {
|
||||
monitorTimer = setInterval(() => loadMonitorBoard({ background: true }), 5000);
|
||||
}
|
||||
void loadMonitorBoard({ background: hadCache }).finally(() => {
|
||||
scheduleNextMonitorPoll();
|
||||
});
|
||||
}
|
||||
|
||||
async function loadSettings() {
|
||||
@@ -576,30 +610,44 @@
|
||||
async function loadMonitorBoard(opts) {
|
||||
const options = opts || {};
|
||||
const background = !!options.background;
|
||||
const force = !!options.force;
|
||||
if (monitorBoardInFlight && background && !force) return;
|
||||
const box = document.getElementById("monitor-grid");
|
||||
const seq = ++monitorBoardFetchSeq;
|
||||
const showLoading = !background && !lastMonitorRows.length;
|
||||
if (showLoading && box) {
|
||||
box.innerHTML =
|
||||
'<div class="board-loading"><span class="board-loading-spin" aria-hidden="true"></span>正在聚合四所数据…</div>';
|
||||
'<div class="board-loading"><span class="board-loading-spin" aria-hidden="true"></span>正在聚合四所数据…<p class="board-loading-sub"></p></div>';
|
||||
scheduleMonitorBoardSlowHint(box);
|
||||
} else if (background && lastMonitorRows.length) {
|
||||
applyMonitorBoardUi(lastMonitorRows, null, { stale: true });
|
||||
}
|
||||
monitorBoardInFlight = true;
|
||||
const ctrl = new AbortController();
|
||||
const fetchTimer = setTimeout(() => ctrl.abort(), HUB_MONITOR_FETCH_TIMEOUT_MS);
|
||||
try {
|
||||
const r = await apiFetch("/api/monitor/board");
|
||||
const r = await apiFetch("/api/monitor/board", { signal: ctrl.signal });
|
||||
const data = await r.json();
|
||||
if (seq !== monitorBoardFetchSeq) return;
|
||||
if (!r.ok) {
|
||||
throw new Error(data.msg || data.detail || `HTTP ${r.status}`);
|
||||
}
|
||||
lastMonitorRows = data.rows || [];
|
||||
saveMonitorBoardCache(lastMonitorRows, data.updated_at);
|
||||
applyMonitorBoardUi(lastMonitorRows, data.updated_at, { stale: false });
|
||||
} catch (e) {
|
||||
if (seq !== monitorBoardFetchSeq) return;
|
||||
const msg =
|
||||
e && e.name === "AbortError"
|
||||
? "聚合超时(约 55 秒)。请检查子代理/Flask 是否运行,或关闭 HUB_BOARD_KEY_PRICES 加速"
|
||||
: String(e);
|
||||
if (background && lastMonitorRows.length) {
|
||||
showToast("监控数据刷新失败,仍显示上次缓存", true);
|
||||
applyMonitorBoardUi(lastMonitorRows, null, { stale: false });
|
||||
return;
|
||||
}
|
||||
if (box) box.innerHTML = `<div class="err">${esc(e)}</div>`;
|
||||
if (box) box.innerHTML = `<div class="err">${esc(msg)}</div>`;
|
||||
} finally {
|
||||
clearTimeout(fetchTimer);
|
||||
clearMonitorBoardSlowHint();
|
||||
monitorBoardInFlight = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1870,7 +1918,8 @@
|
||||
location.href = "/login";
|
||||
};
|
||||
|
||||
document.getElementById("btn-monitor-refresh").onclick = loadMonitorBoard;
|
||||
document.getElementById("btn-monitor-refresh").onclick = () =>
|
||||
loadMonitorBoard({ force: true, background: !!lastMonitorRows.length });
|
||||
document.getElementById("auto-monitor").onchange = startMonitorPoll;
|
||||
document.getElementById("btn-close-all").onclick = closeAll;
|
||||
document.getElementById("btn-settings-save").onclick = saveSettings;
|
||||
|
||||
Reference in New Issue
Block a user