diff --git a/frontend/src/pages/TargetDetailPage.jsx b/frontend/src/pages/TargetDetailPage.jsx
index a42cd2a..76031cb 100644
--- a/frontend/src/pages/TargetDetailPage.jsx
+++ b/frontend/src/pages/TargetDetailPage.jsx
@@ -54,6 +54,15 @@ function MetricsTooltip({ active, payload, label }) {
);
}
+function didMetricSeriesChange(prev = [], next = []) {
+ if (!Array.isArray(prev) || !Array.isArray(next)) return true;
+ if (prev.length !== next.length) return true;
+ if (prev.length === 0 && next.length === 0) return false;
+ const prevLast = prev[prev.length - 1];
+ const nextLast = next[next.length - 1];
+ return prevLast?.ts !== nextLast?.ts || Number(prevLast?.value) !== Number(nextLast?.value);
+}
+
async function loadMetric(targetId, metric, range, tokens, refresh) {
const { from, to } = toQueryRange(range);
return apiFetch(
@@ -129,11 +138,18 @@ export function TargetDetailPage() {
loadMetric(id, "cache_hit_ratio", "15m", tokens, refreshRef.current),
]);
if (!active) return;
- setSeries({ connections, xacts, cache });
+ const nextSeries = { connections, xacts, cache };
+ setSeries((prev) => {
+ const changed =
+ didMetricSeriesChange(prev.connections, nextSeries.connections) ||
+ didMetricSeriesChange(prev.xacts, nextSeries.xacts) ||
+ didMetricSeriesChange(prev.cache, nextSeries.cache);
+ return changed ? nextSeries : prev;
+ });
} catch {
// Keep previous chart values if a live tick fails.
}
- }, 1000);
+ }, 3000);
return () => {
active = false;
@@ -429,9 +445,9 @@ export function TargetDetailPage() {
} />
-
-
-
+
+
+