forked from wrenn/wrenn
Optimize frontend polling: visibility API, range-based intervals, skip redundant redraws
Adds Page Visibility API to StatsPanel, templates, and capsule detail pages so polling pauses when the browser tab is hidden. Capsule metrics now use range-appropriate poll intervals (10s for 5m/10m, up to 120s for 24h) instead of a flat 10s. Chart updates are skipped when the data fingerprint hasn't changed, avoiding unnecessary Canvas redraws.
This commit is contained in:
@ -21,5 +21,14 @@ export async function fetchSandboxMetrics(id: string, range: MetricRange): Promi
|
||||
|
||||
export const METRIC_RANGES: MetricRange[] = ['5m', '10m', '1h', '6h', '24h'];
|
||||
|
||||
// All ranges poll every 10 seconds.
|
||||
// Poll interval varies by range — shorter ranges need fresher data.
|
||||
export const METRIC_POLL_INTERVALS: Record<MetricRange, number> = {
|
||||
'5m': 10_000,
|
||||
'10m': 10_000,
|
||||
'1h': 30_000,
|
||||
'6h': 60_000,
|
||||
'24h': 120_000,
|
||||
};
|
||||
|
||||
/** @deprecated Use METRIC_POLL_INTERVALS instead */
|
||||
export const METRIC_POLL_INTERVAL = 10_000;
|
||||
|
||||
@ -26,6 +26,8 @@
|
||||
let chartRam: any = null;
|
||||
|
||||
let pollInterval: ReturnType<typeof setInterval> | null = null;
|
||||
let lastDataKey = ''; // cheap fingerprint to skip redundant chart redraws
|
||||
let visibilityHandler: (() => void) | null = null;
|
||||
|
||||
async function load() {
|
||||
const result = await fetchStats(range);
|
||||
@ -43,6 +45,10 @@
|
||||
|
||||
function updateCharts() {
|
||||
if (!stats) return;
|
||||
// Skip redraw if data hasn't changed (same length + same last label).
|
||||
const key = `${stats.series.labels.length}:${stats.series.labels.at(-1) ?? ''}`;
|
||||
if (key === lastDataKey) return;
|
||||
lastDataKey = key;
|
||||
// Use Array.from to pass plain JS arrays to Chart.js — Svelte 5 $state
|
||||
// wraps arrays in reactive proxies which Chart.js can't iterate reliably.
|
||||
const labels = formatLabels(Array.from(stats.series.labels), range);
|
||||
@ -77,14 +83,19 @@
|
||||
});
|
||||
}
|
||||
|
||||
function stopPolling() {
|
||||
if (pollInterval) { clearInterval(pollInterval); pollInterval = null; }
|
||||
}
|
||||
|
||||
function restartPolling() {
|
||||
if (pollInterval) clearInterval(pollInterval);
|
||||
stopPolling();
|
||||
load();
|
||||
pollInterval = setInterval(load, POLL_INTERVALS[range]);
|
||||
}
|
||||
|
||||
function setRange(r: TimeRange) {
|
||||
range = r;
|
||||
lastDataKey = ''; // force chart redraw on range switch
|
||||
goto(`?range=${r}`, { replaceState: true, noScroll: true, keepFocus: true });
|
||||
restartPolling();
|
||||
}
|
||||
@ -237,10 +248,21 @@
|
||||
updateCharts();
|
||||
|
||||
restartPolling();
|
||||
|
||||
// Pause polling when the browser tab is hidden to save bandwidth/CPU.
|
||||
visibilityHandler = () => {
|
||||
if (document.hidden) {
|
||||
stopPolling();
|
||||
} else {
|
||||
restartPolling();
|
||||
}
|
||||
};
|
||||
document.addEventListener('visibilitychange', visibilityHandler);
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (pollInterval) clearInterval(pollInterval);
|
||||
stopPolling();
|
||||
if (visibilityHandler) document.removeEventListener('visibilitychange', visibilityHandler);
|
||||
chartRunning?.destroy();
|
||||
chartCpu?.destroy();
|
||||
chartRam?.destroy();
|
||||
|
||||
Reference in New Issue
Block a user