From 73de07047851b148a4381adad2232e7555bd071f Mon Sep 17 00:00:00 2001 From: zhom <2717306+zhom@users.noreply.github.com> Date: Thu, 11 Dec 2025 22:40:57 +0400 Subject: [PATCH] refactor: dump client bandwidth data when not visible --- src-tauri/src/traffic_stats.rs | 52 +++++++++++++++++------ src/components/profile-data-table.tsx | 32 ++++++++++++-- src/components/traffic-details-dialog.tsx | 6 ++- 3 files changed, 73 insertions(+), 17 deletions(-) diff --git a/src-tauri/src/traffic_stats.rs b/src-tauri/src/traffic_stats.rs index c5a5847..9350246 100644 --- a/src-tauri/src/traffic_stats.rs +++ b/src-tauri/src/traffic_stats.rs @@ -82,6 +82,9 @@ pub struct TrafficStats { pub session_start: u64, /// Last update timestamp pub last_update: u64, + /// Timestamp of the last flush to disk (used to avoid double-counting session snapshots) + #[serde(default)] + pub last_flush_timestamp: u64, /// Total bytes sent across all time pub total_bytes_sent: u64, /// Total bytes received across all time @@ -110,6 +113,7 @@ impl TrafficStats { profile_id, session_start: now, last_update: now, + last_flush_timestamp: 0, total_bytes_sent: 0, total_bytes_received: 0, total_requests: 0, @@ -788,6 +792,12 @@ impl LiveTrafficTracker { let sent = self.bytes_sent.swap(0, Ordering::Relaxed); let received = self.bytes_received.swap(0, Ordering::Relaxed); + // Update flush timestamp to track when data was last flushed + // This prevents double-counting session snapshots written before this flush + let now = current_timestamp(); + stats.last_flush_timestamp = now; + stats.last_update = now; + // Update bandwidth history stats.record_bandwidth(sent, received); @@ -1045,18 +1055,36 @@ pub fn get_all_traffic_snapshots_realtime() -> Vec { if let Some(session) = load_session_snapshot(profile_id) { // Merge session data with disk snapshot if let Some(snapshot) = snapshots.get_mut(profile_id) { - // Session data contains in-memory counters not yet flushed to disk - // Disk snapshot contains cumulative totals already flushed - // We need to ADD them, not take the max, to get the true total - snapshot.total_bytes_sent = - snapshot.total_bytes_sent.saturating_add(session.bytes_sent); - snapshot.total_bytes_received = snapshot - .total_bytes_received - .saturating_add(session.bytes_received); - snapshot.total_requests = snapshot.total_requests.saturating_add(session.requests); - snapshot.current_bytes_sent = session.bytes_sent; - snapshot.current_bytes_received = session.bytes_received; - snapshot.last_update = session.timestamp; + // Only merge session data if it's newer than the last flush + // Session snapshots written before the last flush contain bytes already + // included in disk totals, so merging them would cause double-counting + let disk_stats = load_traffic_stats(profile_id); + let last_flush = disk_stats + .as_ref() + .map(|s| s.last_flush_timestamp) + .unwrap_or(0); + + if session.timestamp > last_flush { + // Session data contains in-memory counters not yet flushed to disk + // Disk snapshot contains cumulative totals already flushed + // We need to ADD them, not take the max, to get the true total + snapshot.total_bytes_sent = + snapshot.total_bytes_sent.saturating_add(session.bytes_sent); + snapshot.total_bytes_received = snapshot + .total_bytes_received + .saturating_add(session.bytes_received); + snapshot.total_requests = + snapshot.total_requests.saturating_add(session.requests); + snapshot.current_bytes_sent = session.bytes_sent; + snapshot.current_bytes_received = session.bytes_received; + snapshot.last_update = session.timestamp; + } else { + // Session snapshot is stale (written before last flush) + // Use current values from disk snapshot, but update timestamp if session is newer + if session.timestamp > snapshot.last_update { + snapshot.last_update = session.timestamp; + } + } } else { // Create new snapshot from session data snapshots.insert( diff --git a/src/components/profile-data-table.tsx b/src/components/profile-data-table.tsx index 51b5615..a1cadc1 100644 --- a/src/components/profile-data-table.tsx +++ b/src/components/profile-data-table.tsx @@ -886,9 +886,12 @@ export function ProfilesDataTable({ const newSnapshots: Record = {}; for (const snapshot of allSnapshots) { if (snapshot.profile_id) { - const existing = newSnapshots[snapshot.profile_id]; - if (!existing || snapshot.last_update > existing.last_update) { - newSnapshots[snapshot.profile_id] = snapshot; + // Only keep snapshots for profiles that are currently running + if (runningProfiles.has(snapshot.profile_id)) { + const existing = newSnapshots[snapshot.profile_id]; + if (!existing || snapshot.last_update > existing.last_update) { + newSnapshots[snapshot.profile_id] = snapshot; + } } } } @@ -901,7 +904,27 @@ export function ProfilesDataTable({ void fetchTrafficSnapshots(); const interval = setInterval(fetchTrafficSnapshots, 1000); return () => clearInterval(interval); - }, [browserState.isClient, runningCount]); + }, [browserState.isClient, runningCount, runningProfiles]); + + // Clean up snapshots for profiles that are no longer running + React.useEffect(() => { + if (!browserState.isClient) return; + + setTrafficSnapshots((prev) => { + const cleaned: Record = {}; + for (const [profileId, snapshot] of Object.entries(prev)) { + // Only keep snapshots for profiles that are currently running + if (runningProfiles.has(profileId)) { + cleaned[profileId] = snapshot; + } + } + // Only update if something was removed + if (Object.keys(cleaned).length !== Object.keys(prev).length) { + return cleaned; + } + return prev; + }); + }, [browserState.isClient, runningProfiles]); // Clear launching/stopping spinners when backend reports running status changes React.useEffect(() => { @@ -1692,6 +1715,7 @@ export function ProfilesDataTable({ if (isRunning && meta.trafficSnapshots) { // Find the traffic snapshot for this profile by matching profile_id const snapshot = meta.trafficSnapshots[profile.id]; + // Only use recent_bandwidth (last 60 seconds) - minimal data needed for mini chart // Create a new array reference to ensure React detects changes const bandwidthData = snapshot?.recent_bandwidth ? [...snapshot.recent_bandwidth] diff --git a/src/components/traffic-details-dialog.tsx b/src/components/traffic-details-dialog.tsx index 2a0adab..3af58b6 100644 --- a/src/components/traffic-details-dialog.tsx +++ b/src/components/traffic-details-dialog.tsx @@ -171,7 +171,11 @@ export function TrafficDetailsDialog({ void fetchStats(); const interval = setInterval(fetchStats, 2000); - return () => clearInterval(interval); + return () => { + clearInterval(interval); + // Clear stats from memory when dialog closes to free up memory + setStats(null); + }; }, [isOpen, profileId, timePeriod]); // Transform data for chart (already filtered by backend)