huge optimizations
This commit is contained in:
@@ -4,6 +4,8 @@ import { digestData } from "./utils.js";
|
||||
const POLL_INTERVAL_MS = 5000;
|
||||
const FALLBACK_POLL_MS = 30000;
|
||||
const PREF_REFRESH_KEY = "overlord_refresh_interval_seconds";
|
||||
const REALTIME_MIN_LOAD_MS = 1500;
|
||||
const REALTIME_CHURN_WINDOW_MS = 5000;
|
||||
let pollTimer = null;
|
||||
let render = () => {};
|
||||
let lastClientData = null;
|
||||
@@ -12,6 +14,12 @@ const PAGE_CACHE_LIMIT = 8;
|
||||
let dashboardWs = null;
|
||||
let dashboardWsConnected = false;
|
||||
let wsReconnectTimer = null;
|
||||
let realtimeLoadTimer = null;
|
||||
let lastRealtimeLoadAt = 0;
|
||||
let lastRealtimeEventAt = 0;
|
||||
let realtimeEventCount = 0;
|
||||
let realtimeForce = false;
|
||||
let realtimeReorder = false;
|
||||
const manuallyDisconnecting = new Set();
|
||||
|
||||
function stopDashboardRealtime() {
|
||||
@@ -19,6 +27,8 @@ function stopDashboardRealtime() {
|
||||
pollTimer = null;
|
||||
clearTimeout(wsReconnectTimer);
|
||||
wsReconnectTimer = null;
|
||||
clearTimeout(realtimeLoadTimer);
|
||||
realtimeLoadTimer = null;
|
||||
if (dashboardWs) {
|
||||
try {
|
||||
dashboardWs.onopen = null;
|
||||
@@ -32,6 +42,38 @@ function stopDashboardRealtime() {
|
||||
dashboardWsConnected = false;
|
||||
}
|
||||
|
||||
function noteRealtimeEvent() {
|
||||
const now = Date.now();
|
||||
if (now - lastRealtimeEventAt > REALTIME_CHURN_WINDOW_MS) {
|
||||
realtimeEventCount = 0;
|
||||
}
|
||||
lastRealtimeEventAt = now;
|
||||
realtimeEventCount += 1;
|
||||
}
|
||||
|
||||
function isRealtimeChurnActive() {
|
||||
return Date.now() - lastRealtimeEventAt < REALTIME_CHURN_WINDOW_MS && realtimeEventCount > 5;
|
||||
}
|
||||
|
||||
function scheduleRealtimeLoad(options = {}) {
|
||||
noteRealtimeEvent();
|
||||
realtimeForce = realtimeForce || !!options.force;
|
||||
realtimeReorder = realtimeReorder || options.reorder !== false;
|
||||
if (realtimeLoadTimer) return;
|
||||
|
||||
const elapsed = Date.now() - lastRealtimeLoadAt;
|
||||
const delay = Math.max(0, REALTIME_MIN_LOAD_MS - elapsed);
|
||||
realtimeLoadTimer = setTimeout(() => {
|
||||
realtimeLoadTimer = null;
|
||||
lastRealtimeLoadAt = Date.now();
|
||||
const force = realtimeForce;
|
||||
const reorder = realtimeReorder;
|
||||
realtimeForce = false;
|
||||
realtimeReorder = false;
|
||||
loadWithOptions({ force, reorder });
|
||||
}, delay);
|
||||
}
|
||||
|
||||
function bindDashboardPagehideCleanup() {
|
||||
window.removeEventListener("pagehide", stopDashboardRealtime);
|
||||
window.addEventListener("pagehide", stopDashboardRealtime);
|
||||
@@ -101,6 +143,7 @@ async function prefetchClientPage(page) {
|
||||
}
|
||||
|
||||
function prefetchAdjacentPages(data) {
|
||||
if (isRealtimeChurnActive()) return;
|
||||
const totalPages = Math.max(1, Math.ceil((Number(data.total) || 0) / (Number(data.pageSize) || state.pageSize || 1)));
|
||||
const current = Number(data.page) || state.page;
|
||||
if (current < totalPages) prefetchClientPage(current + 1);
|
||||
@@ -219,16 +262,16 @@ function connectDashboardWs() {
|
||||
try {
|
||||
const msg = typeof event.data === "string" ? JSON.parse(event.data) : null;
|
||||
if (msg && msg.type === "clients_changed") {
|
||||
loadWithOptions({ force: false, reorder: true });
|
||||
scheduleRealtimeLoad({ force: false, reorder: true });
|
||||
return;
|
||||
}
|
||||
if (msg && msg.type === "client_event") {
|
||||
if (msg.event === "client_offline" && manuallyDisconnecting.has(msg.clientId)) {
|
||||
manuallyDisconnecting.delete(msg.clientId);
|
||||
loadWithOptions({ force: true });
|
||||
scheduleRealtimeLoad({ force: true, reorder: true });
|
||||
} else {
|
||||
moveClientCardImmediately(msg);
|
||||
loadWithOptions({ force: false, reorder: true });
|
||||
if (!isRealtimeChurnActive()) moveClientCardImmediately(msg);
|
||||
scheduleRealtimeLoad({ force: false, reorder: true });
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
|
||||
@@ -780,6 +780,67 @@ function updateMetrics(data, debug) {
|
||||
}
|
||||
}
|
||||
|
||||
const internalTasksList = document.getElementById("internal-tasks");
|
||||
if (internalTasksList) {
|
||||
const tasks = Array.isArray(data.internal?.tasks) ? data.internal.tasks : [];
|
||||
if (tasks.length > 0) {
|
||||
internalTasksList.innerHTML = tasks
|
||||
.map((task) => `
|
||||
<div class="bg-slate-800/50 rounded p-3 min-w-0">
|
||||
<div class="text-xs text-slate-400 mb-2 truncate" title="${escapeHtml(task.task)}">
|
||||
${escapeHtml(task.task)}
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-x-3 gap-y-1 text-xs">
|
||||
<span class="text-slate-500">p95</span>
|
||||
<span class="font-semibold text-right">${formatMs(task.durationP95 || 0)}</span>
|
||||
<span class="text-slate-500">avg</span>
|
||||
<span class="font-semibold text-right">${formatMs(task.durationAvg || 0)}</span>
|
||||
<span class="text-slate-500">max</span>
|
||||
<span class="font-semibold text-right">${formatMs(task.durationMax || 0)}</span>
|
||||
<span class="text-slate-500">runs/min</span>
|
||||
<span class="font-semibold text-right">${Number(task.countLastMinute || 0).toLocaleString()}</span>
|
||||
</div>
|
||||
</div>
|
||||
`)
|
||||
.join("");
|
||||
} else {
|
||||
internalTasksList.innerHTML =
|
||||
'<div class="text-slate-500 col-span-full text-center py-4">No internal job samples yet</div>';
|
||||
}
|
||||
}
|
||||
|
||||
const retainedStateList = document.getElementById("retained-state");
|
||||
if (retainedStateList) {
|
||||
const retained = data.diagnostics?.retained || {};
|
||||
const entries = Object.entries(retained)
|
||||
.filter(([, value]) => typeof value === "number" || typeof value === "boolean")
|
||||
.sort(([a], [b]) => a.localeCompare(b));
|
||||
if (entries.length > 0) {
|
||||
retainedStateList.innerHTML = entries
|
||||
.map(([key, value]) => {
|
||||
const displayValue = key.toLowerCase().includes("bytes")
|
||||
? formatBytes(Number(value || 0))
|
||||
: typeof value === "boolean"
|
||||
? value ? "yes" : "no"
|
||||
: Number(value || 0).toLocaleString();
|
||||
return `
|
||||
<div class="bg-slate-800/50 rounded p-3 min-w-0">
|
||||
<div class="text-xs text-slate-500 mb-1 truncate" title="${escapeHtml(key)}">
|
||||
${escapeHtml(key)}
|
||||
</div>
|
||||
<div class="text-sm font-semibold truncate" title="${escapeHtml(displayValue)}">
|
||||
${escapeHtml(displayValue)}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
})
|
||||
.join("");
|
||||
} else {
|
||||
retainedStateList.innerHTML =
|
||||
'<div class="text-slate-500 col-span-full text-center py-4">No retained state counters yet</div>';
|
||||
}
|
||||
}
|
||||
|
||||
const commandTypesList = document.getElementById("command-types");
|
||||
if (Object.keys(data.commands.byType).length > 0) {
|
||||
const topCommands = Object.entries(data.commands.byType)
|
||||
|
||||
@@ -36,6 +36,7 @@ async function triggerAgentThumbnail(clientId) {
|
||||
const res = await fetch(`/api/clients/${encodeURIComponent(clientId)}/thumbnail`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
headers: { "X-Overlord-Thumbnail-Source": "dashboard" },
|
||||
});
|
||||
if (!res.ok) return null;
|
||||
return await res.json().catch(() => null);
|
||||
|
||||
Reference in New Issue
Block a user