From 3655428da44503e30d25d0f8b55d78a00a06a3af Mon Sep 17 00:00:00 2001 From: shats Date: Tue, 24 Feb 2026 12:54:51 +0700 Subject: [PATCH] feat(uptime-monitor): enhance uptime checks with request cancellation and logging on page leave for improved resource management --- .cursor/debug-378b5f.log | 9 +++++- frontend/src/UptimeMonitorPage.jsx | 50 ++++++++++++++++++++++++++++-- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/.cursor/debug-378b5f.log b/.cursor/debug-378b5f.log index 7fe9fcb..701d0f7 100644 --- a/.cursor/debug-378b5f.log +++ b/.cursor/debug-378b5f.log @@ -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} diff --git a/frontend/src/UptimeMonitorPage.jsx b/frontend/src/UptimeMonitorPage.jsx index 4268243..4cc2b54 100644 --- a/frontend/src/UptimeMonitorPage.jsx +++ b/frontend/src/UptimeMonitorPage.jsx @@ -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);