fix(runtime): bound Runs readback polling
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Failing after 47s
CD Pipeline / build-and-deploy (push) Has been skipped
CD Pipeline / post-deploy-checks (push) Has been skipped
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Failing after 47s
CD Pipeline / build-and-deploy (push) Has been skipped
CD Pipeline / post-deploy-checks (push) Has been skipped
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useLocale, useTranslations } from "next-intl";
|
||||
import {
|
||||
Activity,
|
||||
@@ -467,6 +467,7 @@ type AutomationWorkItem = {
|
||||
};
|
||||
|
||||
const API_BASE = getRuntimeApiBaseUrl();
|
||||
const AUTO_REFRESH_INTERVAL = 90_000;
|
||||
|
||||
function numberValue(value: unknown): string {
|
||||
const numeric = Number(value ?? 0);
|
||||
@@ -644,39 +645,55 @@ export function AutonomousRuntimeReceiptPanel({
|
||||
const [error, setError] = useState(false);
|
||||
const [updatedAt, setUpdatedAt] = useState<Date | null>(null);
|
||||
const [workFilter, setWorkFilter] = useState<WorkFilter>("all");
|
||||
const refreshInFlightRef = useRef(false);
|
||||
|
||||
const refresh = useCallback(async () => {
|
||||
if (refreshInFlightRef.current) return;
|
||||
refreshInFlightRef.current = true;
|
||||
setLoading(true);
|
||||
const next = await fetchRuntimeControl();
|
||||
setPayload(next);
|
||||
setError(next === null);
|
||||
setUpdatedAt(next ? new Date() : null);
|
||||
setLoading(false);
|
||||
try {
|
||||
const next = await fetchRuntimeControl();
|
||||
if (next) {
|
||||
setPayload(next);
|
||||
setUpdatedAt(new Date());
|
||||
}
|
||||
setError(next === null);
|
||||
|
||||
const [
|
||||
priority,
|
||||
consumer,
|
||||
telegramVerifier,
|
||||
telegramCoverage,
|
||||
telegramLiveReceiptApply,
|
||||
] = await Promise.all([
|
||||
fetchPriorityWorkOrder(),
|
||||
fetchLogConsumerReadback(),
|
||||
fetchTelegramPostApplyVerifier(),
|
||||
fetchTelegramMonitoringCoverage(),
|
||||
fetchTelegramMonitoringLiveReceiptApplyReadback(),
|
||||
]);
|
||||
setPriorityPayload(priority);
|
||||
setConsumerPayload(consumer);
|
||||
setTelegramVerifierPayload(telegramVerifier);
|
||||
setTelegramCoveragePayload(telegramCoverage);
|
||||
setTelegramLiveReceiptApplyPayload(telegramLiveReceiptApply);
|
||||
// These endpoints share the same bounded DB pool. Keep them serialized so
|
||||
// one browser cannot starve health probes or create overlapping readbacks.
|
||||
const priority = await fetchPriorityWorkOrder();
|
||||
if (priority) setPriorityPayload(priority);
|
||||
const consumer = await fetchLogConsumerReadback();
|
||||
if (consumer) setConsumerPayload(consumer);
|
||||
const telegramVerifier = await fetchTelegramPostApplyVerifier();
|
||||
if (telegramVerifier) setTelegramVerifierPayload(telegramVerifier);
|
||||
const telegramCoverage = await fetchTelegramMonitoringCoverage();
|
||||
if (telegramCoverage) setTelegramCoveragePayload(telegramCoverage);
|
||||
const telegramLiveReceiptApply =
|
||||
await fetchTelegramMonitoringLiveReceiptApplyReadback();
|
||||
if (telegramLiveReceiptApply) {
|
||||
setTelegramLiveReceiptApplyPayload(telegramLiveReceiptApply);
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
refreshInFlightRef.current = false;
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
refresh();
|
||||
const timer = window.setInterval(refresh, 30_000);
|
||||
return () => window.clearInterval(timer);
|
||||
let cancelled = false;
|
||||
let timer: number | null = null;
|
||||
const poll = async () => {
|
||||
await refresh();
|
||||
if (!cancelled) {
|
||||
timer = window.setTimeout(() => void poll(), AUTO_REFRESH_INTERVAL);
|
||||
}
|
||||
};
|
||||
void poll();
|
||||
return () => {
|
||||
cancelled = true;
|
||||
if (timer !== null) window.clearTimeout(timer);
|
||||
};
|
||||
}, [refresh]);
|
||||
|
||||
const hasRuntimeReadback = payload !== null;
|
||||
@@ -1605,7 +1622,7 @@ export function AutonomousRuntimeReceiptPanel({
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={refresh}
|
||||
onClick={() => void refresh()}
|
||||
disabled={loading}
|
||||
className="inline-flex items-center gap-2 border border-[#d8d3c7] bg-white px-3 py-2 text-xs font-semibold text-[#141413] hover:border-[#d97757] disabled:opacity-60"
|
||||
aria-label={t("refresh")}
|
||||
|
||||
Reference in New Issue
Block a user