feat(uptime-monitor): enhance uptime checks with request cancellation and logging on page leave for improved resource management
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m6s

This commit is contained in:
2026-02-24 12:54:51 +07:00
parent 3840a91ba5
commit 3655428da4
2 changed files with 55 additions and 4 deletions
+8 -1
View File
@@ -1 +1,8 @@
{"sessionId":"378b5f","runId":"baseline","hypothesisId":"H1_H2","location":"frontend/src/lib/api.js:request","message":"API request started","data":{"method":"GET","url":"/alerts"},"timestamp":1771911955281}
{"sessionId":"378b5f","runId":"baseline","hypothesisId":"H1_H2","location":"frontend/src/lib/api.js:response","message":"API response received","data":{"method":"POST","url":"/uptime/check","status":200},"timestamp":1771912295663}
{"sessionId":"378b5f","runId":"baseline","hypothesisId":"H1_H2","location":"frontend/src/lib/api.js:response","message":"API response received","data":{"method":"POST","url":"/uptime/check","status":200},"timestamp":1771912297719}
{"sessionId":"378b5f","runId":"baseline","hypothesisId":"H1_H2","location":"frontend/src/lib/api.js:request","message":"API request started","data":{"method":"GET","url":"/alerts"},"timestamp":1771912335274}
{"sessionId":"378b5f","runId":"baseline","hypothesisId":"H1_H2","location":"frontend/src/lib/api.js:request","message":"API request started","data":{"method":"GET","url":"/alerts"},"timestamp":1771912395275}
{"sessionId":"378b5f","runId":"baseline","hypothesisId":"H1_H2","location":"frontend/src/lib/api.js:request","message":"API request started","data":{"method":"GET","url":"/alerts"},"timestamp":1771912395275}
{"sessionId":"378b5f","runId":"baseline","hypothesisId":"H1_H2","location":"frontend/src/lib/api.js:response","message":"API response received","data":{"method":"GET","url":"/alerts","status":200},"timestamp":1771912434898}
{"sessionId":"378b5f","runId":"baseline","hypothesisId":"H1_H2","location":"frontend/src/lib/api.js:response","message":"API response received","data":{"method":"GET","url":"/alerts","status":200},"timestamp":1771912439040}
{"sessionId":"378b5f","runId":"baseline","hypothesisId":"H1_H2","location":"frontend/src/lib/api.js:request","message":"API request started","data":{"method":"GET","url":"/alerts"},"timestamp":1771912455273}
+47 -3
View File
@@ -1,4 +1,5 @@
import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
import { useLocation } from 'react-router-dom';
import api from './lib/api.js';
import {
IconClock,
@@ -81,6 +82,7 @@ function historyToResponseTimeData(history) {
}
export default function UptimeMonitorPage() {
const location = useLocation();
const [servers, setServers] = useState([]);
const [loading, setLoading] = useState(true);
const [checking, setChecking] = useState(false);
@@ -94,6 +96,8 @@ export default function UptimeMonitorPage() {
const [currentCheckingServerIds, setCurrentCheckingServerIds] = useState([]);
const intervalRef = useRef(null);
const checkingRef = useRef(false);
const activeRunIdRef = useRef(0);
const checkControllersRef = useRef(new Set());
const jumphosts = useMemo(
() =>
@@ -160,6 +164,7 @@ export default function UptimeMonitorPage() {
const runChecks = useCallback(async () => {
if (jumphosts.length === 0) return;
if (checkingRef.current) return;
const runId = ++activeRunIdRef.current;
checkingRef.current = true;
setChecking(true);
setCurrentCheckingServerIds([]);
@@ -171,18 +176,31 @@ export default function UptimeMonitorPage() {
const checkOne = async () => {
let server;
while ((server = getNext()) != null) {
if (activeRunIdRef.current !== runId) break;
const serverId = server.id || server.dns || server.ip;
if (!serverId) continue;
setCurrentCheckingServerIds((prev) => [...prev, serverId]);
const t0 = Date.now();
let ok = false;
let ms = null;
const controller = new AbortController();
checkControllersRef.current.add(controller);
try {
const { data: checkData } = await api.post('/uptime/check', { serverId });
const { data: checkData } = await api.post('/uptime/check', { serverId }, { signal: controller.signal });
ok = checkData?.ok === true;
ms = typeof checkData?.ms === 'number' ? checkData.ms : Date.now() - t0;
} catch (_) {
} catch (e) {
if (e?.code === 'ERR_CANCELED' || e?.name === 'CanceledError' || e?.name === 'AbortError') {
// #region agent log
fetch('http://192.168.10.2:7343/ingest/aa002dd6-6968-4a35-b09d-f05302d83266',{method:'POST',headers:{'Content-Type':'application/json','X-Debug-Session-Id':'378b5f'},body:JSON.stringify({sessionId:'378b5f',runId:'post-fix',hypothesisId:'H7',location:'frontend/src/UptimeMonitorPage.jsx:uptime-check-canceled',message:'Canceled uptime check request',data:{serverId:String(serverId||''),path:String(location?.pathname||'')},timestamp:Date.now()})}).catch(()=>{});
// #endregion
setCurrentCheckingServerIds((prev) => prev.filter((id) => id !== serverId));
checkControllersRef.current.delete(controller);
continue;
}
ms = Date.now() - t0;
} finally {
checkControllersRef.current.delete(controller);
}
const lastCheckTs = Date.now();
const entry = { ts: lastCheckTs, ok, ms };
@@ -196,10 +214,11 @@ export default function UptimeMonitorPage() {
}
};
await Promise.all(Array.from({ length: PARALLEL_CHECKS }, checkOne));
if (activeRunIdRef.current !== runId) return;
setCurrentCheckingServerIds([]);
checkingRef.current = false;
setChecking(false);
}, [jumphosts]);
}, [jumphosts, location.pathname]);
// Первый запуск проверки через несколько секунд после загрузки (после перезапуска контейнера кеш может быть пустым или устаревшим)
useEffect(() => {
@@ -217,6 +236,31 @@ export default function UptimeMonitorPage() {
};
}, [jumphosts, runChecks, checkIntervalMs]);
// Остановка фоновых uptime-check при уходе со страницы
useEffect(() => {
if (location.pathname === '/uptime-monitor') return;
activeRunIdRef.current += 1;
checkingRef.current = false;
if (intervalRef.current) clearInterval(intervalRef.current);
checkControllersRef.current.forEach((controller) => controller.abort());
checkControllersRef.current.clear();
setCurrentCheckingServerIds([]);
setChecking(false);
// #region agent log
fetch('http://192.168.10.2:7343/ingest/aa002dd6-6968-4a35-b09d-f05302d83266',{method:'POST',headers:{'Content-Type':'application/json','X-Debug-Session-Id':'378b5f'},body:JSON.stringify({sessionId:'378b5f',runId:'post-fix',hypothesisId:'H7',location:'frontend/src/UptimeMonitorPage.jsx:leave-page',message:'Stopped uptime background checks on page leave',data:{pathname:String(location?.pathname||'')},timestamp:Date.now()})}).catch(()=>{});
// #endregion
}, [location.pathname]);
useEffect(() => {
return () => {
activeRunIdRef.current += 1;
checkingRef.current = false;
if (intervalRef.current) clearInterval(intervalRef.current);
checkControllersRef.current.forEach((controller) => controller.abort());
checkControllersRef.current.clear();
};
}, []);
// Сохраняем историю в localStorage при изменении
useEffect(() => {
saveHistoryToStorage(historyMap);