Skip to content

Commit bc78df9

Browse files
committed
fix(webapp): keep live metric timestamps monotonic
1 parent bd57e80 commit bc78df9

2 files changed

Lines changed: 19 additions & 4 deletions

File tree

apps/webapp/app/hooks/useMetricResourceQuery.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,12 @@ export function useIsMetricResponseFresh(
2929
return () => clearTimeout(timeout);
3030
}, [expiresAt]);
3131

32-
return expiresAt !== null && expiredAt !== expiresAt;
32+
return (
33+
expiresAt !== null &&
34+
responseReceivedAt !== null &&
35+
responseReceivedAt < expiresAt &&
36+
expiredAt !== expiresAt
37+
);
3338
}
3439

3540
export type MetricResourceQueryOptions = {
@@ -122,6 +127,7 @@ export function useMetricResourceQuery(query: string, opts: MetricResourceQueryO
122127
const [isLoading, setIsLoading] = useState(true);
123128
const [failed, setFailed] = useState(false);
124129
const [responseReceivedAt, setResponseReceivedAt] = useState<number | null>(null);
130+
const [lastSuccessfulResponseAt, setLastSuccessfulResponseAt] = useState<number | null>(null);
125131
const abortRef = useRef<AbortController | null>(null);
126132
const loadedKeyRef = useRef<string | null>(null);
127133

@@ -132,6 +138,7 @@ export function useMetricResourceQuery(query: string, opts: MetricResourceQueryO
132138
setRows(null);
133139
setFailed(false);
134140
setResponseReceivedAt(null);
141+
setLastSuccessfulResponseAt(null);
135142
setIsLoading(false);
136143
return;
137144
}
@@ -147,6 +154,7 @@ export function useMetricResourceQuery(query: string, opts: MetricResourceQueryO
147154
setRows(responseCache.get(cacheKey) ?? null);
148155
setFailed(false);
149156
setResponseReceivedAt(null);
157+
setLastSuccessfulResponseAt(null);
150158
}
151159
setIsLoading(true);
152160
fetch("/resources/metric", {
@@ -172,9 +180,11 @@ export function useMetricResourceQuery(query: string, opts: MetricResourceQueryO
172180
if (controller.signal.aborted) return;
173181
if (data.success) {
174182
cacheSet(cacheKey, data.data.rows);
183+
const receivedAt = Date.now();
175184
setRows(data.data.rows);
176185
setFailed(false);
177-
setResponseReceivedAt(Date.now());
186+
setResponseReceivedAt(receivedAt);
187+
setLastSuccessfulResponseAt(receivedAt);
178188
} else {
179189
setFailed(true);
180190
setResponseReceivedAt(null);
@@ -222,5 +232,6 @@ export function useMetricResourceQuery(query: string, opts: MetricResourceQueryO
222232
showLoading: isLoading && !rows,
223233
failed,
224234
responseReceivedAt,
235+
lastSuccessfulResponseAt,
225236
};
226237
}

apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,7 +1307,11 @@ function WaitingInQueueBlock({
13071307
}) {
13081308
// Latest gauges from ClickHouse (as on the queue page), polled so the blocks keep ticking. Trust
13091309
// the newest bucket only while fresh; otherwise fall back to the loader's live values.
1310-
const { rows: liveRows, responseReceivedAt } = useQueueMetric(
1310+
const {
1311+
rows: liveRows,
1312+
responseReceivedAt,
1313+
lastSuccessfulResponseAt,
1314+
} = useQueueMetric(
13111315
`SELECT timeBucket() AS t, max(max_running) AS running, max(max_queued) AS queued, max(max_limit) AS q_limit\nFROM queue_metrics\nGROUP BY t\nORDER BY t`,
13121316
{
13131317
ids: waiting.ids,
@@ -1319,7 +1323,7 @@ function WaitingInQueueBlock({
13191323
);
13201324
const latest = liveRows.length > 0 ? liveRows[liveRows.length - 1] : undefined;
13211325
const latestBucketMs = latest ? clickhouseTimeToMs(latest.t) : NaN;
1322-
const now = responseReceivedAt ?? loadedAt;
1326+
const now = Math.max(loadedAt, lastSuccessfulResponseAt ?? loadedAt);
13231327
const liveFresh = useIsMetricResponseFresh(
13241328
responseReceivedAt,
13251329
latestBucketMs,

0 commit comments

Comments
 (0)