From a24b89220ca0f5e80d7730d5eab61e8e44320237 Mon Sep 17 00:00:00 2001 From: DmitriyA Date: Fri, 4 Apr 2025 15:31:20 -0400 Subject: [PATCH] fixed bugs --- src/Charts/PrometheusChart.jsx | 178 +++++++++++++++------------------ 1 file changed, 81 insertions(+), 97 deletions(-) diff --git a/src/Charts/PrometheusChart.jsx b/src/Charts/PrometheusChart.jsx index 632767d..cefc315 100755 --- a/src/Charts/PrometheusChart.jsx +++ b/src/Charts/PrometheusChart.jsx @@ -43,9 +43,12 @@ const PrometheusChart = ({ metricName }) => { }, []); const setupWebSocket = useCallback(() => { - if (socketRef.current?.connected) return socketRef.current; - - if (socketRef.current) socketRef.current.disconnect(); + if (socketRef.current) { + // Если соединение уже существует, возвращаем его + if (socketRef.current.connected) return socketRef.current; + // Если соединение в процессе переподключения, тоже возвращаем + if (socketRef.current.reconnecting) return socketRef.current; + } const socket = io('http://192.168.2.39:3000/metrics-ws', { transports: ['websocket'], @@ -97,104 +100,73 @@ const PrometheusChart = ({ metricName }) => { }, []); const fetchData = useCallback(() => { - try { - const now = Math.floor(Date.now() / 1000); - let start = useCustomRange - ? Math.floor(startDate.getTime() / 1000) - : now - selectedRange.value; - let end = useCustomRange - ? Math.floor(endDate.getTime() / 1000) - : now; - - if (start >= end) { - console.error('Invalid time range: start >= end'); - return; - } - - const step = calculateStep(start, end); - console.log(`Fetching data for ${metricName}`, { start, end, step }); - - if (socketRef.current?.connected) { - socketRef.current.emit('get-metrics', { - metric: metricName, - start, - end, - step - }); - } else { - console.error('WebSocket is not connected'); - setupWebSocket(); - } - } catch (error) { - console.error('Error in fetchData:', error); - } - }, [metricName, selectedRange, useCustomRange, startDate, endDate, calculateStep, setupWebSocket]); - - const processMetricsData = useCallback((response) => { - const { metric, data } = response; - if (metric !== metricName) return; - - if (!Array.isArray(data)) { - console.error('Invalid data format:', data); - return; - } - - console.log('Processing metrics data:', data); - const now = Math.floor(Date.now() / 1000); - const rangeSeconds = useCustomRange - ? Math.floor(endDate.getTime() / 1000) - Math.floor(startDate.getTime() / 1000) - : selectedRange.value; + const start = now - selectedRange.value; + const end = now; + const step = calculateStep(start, end); - const instancesData = {}; - - data.forEach(item => { - const instance = item.instance || 'default'; - const timestamp = item.timestamp; - const value = parseFloat(item.value); - - if (!instancesData[instance]) { - instancesData[instance] = []; - } - - const formatted = formatTime(timestamp, rangeSeconds); - instancesData[instance].push({ - time: formatted.display, // для отображения - value: value, - timestamp: timestamp, // для сортировки - originalTime: formatted // для группировки + if (socketRef.current?.connected) { + socketRef.current.emit('get-metrics', { + metric: metricName, + start, + end, + step, + _t: Date.now() // Добавляем timestamp для уникальности }); + } + }, [metricName, selectedRange.value]); // Только необходимые зависимости + + const groupBySecond = (points) => { + const grouped = []; + const timeMap = {}; + + points.forEach(point => { + const timeKey = Math.floor(point.timestamp / 1000); + if (!timeMap[timeKey]) { + timeMap[timeKey] = { ...point, count: 1 }; + grouped.push(timeMap[timeKey]); + } else { + timeMap[timeKey].value = (timeMap[timeKey].value * timeMap[timeKey].count + point.value) / + (timeMap[timeKey].count + 1); + timeMap[timeKey].count += 1; + } }); - // Группируем точки с одинаковым временем (в пределах секунды) - Object.keys(instancesData).forEach(instance => { - const grouped = []; - const timeMap = {}; + return grouped; + }; - instancesData[instance].forEach(point => { - const timeKey = Math.floor(point.timestamp / 1000); // группируем по секундам - if (!timeMap[timeKey]) { - timeMap[timeKey] = { - ...point, - count: 1 - }; - grouped.push(timeMap[timeKey]); - } else { - // Усредняем значения для точек в одну секунду - timeMap[timeKey].value = (timeMap[timeKey].value * timeMap[timeKey].count + point.value) / - (timeMap[timeKey].count + 1); - timeMap[timeKey].count += 1; + const processMetricsData = useCallback((response) => { + if (response.metric !== metricName || !Array.isArray(response.data)) return; + + setChartData(prev => { + const newData = { ...(prev || {}) }; + + // Добавление новых точек + response.data.forEach(item => { + const instance = item.instance || 'default'; + if (!newData[instance]) newData[instance] = []; + + if (!newData[instance].some(p => p.timestamp === item.timestamp)) { + newData[instance].push({ + time: formatTime(item.timestamp, selectedRange.value).display, + value: parseFloat(item.value), + timestamp: item.timestamp + }); } }); - instancesData[instance] = grouped.sort((a, b) => a.timestamp - b.timestamp); - }); + // Группировка и ограничение + Object.keys(newData).forEach(instance => { + newData[instance] = groupBySecond(newData[instance]) + .sort((a, b) => a.timestamp - b.timestamp) + .slice(-1000); + }); + + return Object.keys(newData).length ? newData : prev; + }); + }, [metricName, selectedRange.value, formatTime]); + - console.log('Processed chart data:', instancesData); - setChartData(instancesData); - setSelectedGraphRange(null); - setFilteredData(null); - }, [metricName, formatTime, useCustomRange, startDate, endDate, selectedRange.value]); const handleRangeChange = useCallback((event) => { const selectedValue = event.target.value; const range = TIME_RANGES.find(r => r.value === parseInt(selectedValue, 10)); @@ -234,14 +206,26 @@ const PrometheusChart = ({ metricName }) => { useEffect(() => { if (!socketRef.current?.connected) return; - clearInterval(intervalRef.current); - fetchData(); + const fetchDataWrapper = () => { + try { + fetchData(); + } catch (error) { + console.error('Error in interval fetch:', error); + } + }; - intervalRef.current = setInterval(() => { - fetchData(); - }, selectedRange.interval); + // Сразу запросить данные + fetchDataWrapper(); - return () => clearInterval(intervalRef.current); + // Установить интервал + intervalRef.current = setInterval(fetchDataWrapper, selectedRange.interval); + + return () => { + if (intervalRef.current) { + clearInterval(intervalRef.current); + intervalRef.current = null; + } + }; }, [fetchData, selectedRange.interval]); useEffect(() => {