diff --git a/panel/frontend/src/components/ChartLine.vue b/panel/frontend/src/components/ChartLine.vue index 47fe11a..d89dad3 100644 --- a/panel/frontend/src/components/ChartLine.vue +++ b/panel/frontend/src/components/ChartLine.vue @@ -40,7 +40,9 @@ for (const ds of list) { const color = colorOf(ds) if (ds.band) { - // нижняя граница — невидимая линия, верхняя заливается к ней (fill: '-1') + // нижняя граница — невидимая линия, верхняя заливается к ней (fill: '-1'). + // Обе без сглаживания: с tension полоса выгибается за пределы min/max и + // показывает крайности, которых в дайджесте не было. datasets.push({ label: `${ds.label} min`, data: ds.band.min, @@ -49,7 +51,7 @@ pointRadius: 0, pointHoverRadius: 0, fill: false, - tension: 0.25, + tension: 0, }) meta.push({ hideLegend: true, skipTooltip: true }) datasets.push({ @@ -60,7 +62,7 @@ pointHoverRadius: 0, backgroundColor: color + '1f', fill: '-1', - tension: 0.25, + tension: 0, }) meta.push({ hideLegend: true, band: ds.band, label: ds.label }) } @@ -149,6 +151,9 @@ chart.data.labels = props.labels // серии пересобираем целиком: у полос число датасетов не совпадает с числом серий chart.data.datasets = buildDatasets(props.datasets) + // display легенды — не константа: при создании график может быть пустым + // (история ещё не пришла), так что пересчитываем его на каждом обновлении + chart.options.plugins.legend.display = props.datasets.length > 1 chart.update('none') }, { deep: true }, diff --git a/panel/frontend/src/pages/ServerPage.vue b/panel/frontend/src/pages/ServerPage.vue index 9cbd6d2..d3e7745 100644 --- a/panel/frontend/src/pages/ServerPage.vue +++ b/panel/frontend/src/pages/ServerPage.vue @@ -26,14 +26,19 @@ // --- Период и автообновление ------------------------------------------------- +// Точки и темп опроса — свои на каждый период: за неделю ответ в ~1500 точек +// весит сотни КБ, а обновлять его раз в 10 с незачем (на графике шириной в +// несколько сот пикселей лишние точки всё равно не различить). Плотность +// держим около точки на пиксель, длинные периоды опрашиваем реже. const PERIODS = [ - { id: '1h', label: '1 h', hours: 1 }, - { id: '6h', label: '6 h', hours: 6 }, - { id: '24h', label: '24 h', hours: 24 }, - { id: '7d', label: '7 d', hours: 24 * 7 }, + { id: '1h', label: '1 h', hours: 1, points: 400, refreshMs: 10_000 }, + { id: '6h', label: '6 h', hours: 6, points: 500, refreshMs: 15_000 }, + { id: '24h', label: '24 h', hours: 24, points: 600, refreshMs: 30_000 }, + { id: '7d', label: '7 d', hours: 24 * 7, points: 600, refreshMs: 60_000 }, ] const period = ref('1h') -const periodHours = computed(() => PERIODS.find((p) => p.id === period.value).hours) +const currentPeriod = computed(() => PERIODS.find((p) => p.id === period.value)) +const periodHours = computed(() => currentPeriod.value.hours) let serverTimer = null let historyTimer = null @@ -52,7 +57,7 @@ async function loadHistory() { try { const since = new Date(Date.now() - periodHours.value * 3600 * 1000) - history.value = await api.metrics(props.id, since, null, 1500) + history.value = await api.metrics(props.id, since, null, currentPeriod.value.points) } catch (e) { if (e.status !== 401) toast.warning({ title: t('server.historyFailed'), text: e.message }) } @@ -63,7 +68,7 @@ loadServer() loadHistory() serverTimer = setInterval(loadServer, 5000) - historyTimer = setInterval(loadHistory, 10000) + historyTimer = setInterval(loadHistory, currentPeriod.value.refreshMs) } function stopTimers() { @@ -72,7 +77,8 @@ } watch(() => props.id, startTimers, { immediate: true }) -watch(period, loadHistory) +// смена периода меняет и темп опроса — таймеры перезапускаем +watch(currentPeriod, startTimers) onUnmounted(stopTimers) // --- Данные для графиков ------------------------------------------------------ @@ -147,11 +153,14 @@ ]) const diskMounts = computed(() => { - const mounts = new Map() + const mounts = new Set() for (const m of history.value) { - for (const d of m.disks || []) mounts.set(d.mount, true) + for (const d of m.disks || []) mounts.add(d.mount) + // диск, смонтированный на короткое время, мог не дожить до выжившей точки + // схлопнутого бакета — его крайности остались только в дайджесте + for (const mount of Object.keys(m.agg?.disks || {})) mounts.add(mount) } - return [...mounts.keys()] + return [...mounts] }) const diskDatasets = computed(() =>