refactor: dump client bandwidth data when not visible

This commit is contained in:
zhom
2025-12-11 22:40:57 +04:00
parent 187d3414d8
commit 73de070478
3 changed files with 73 additions and 17 deletions
+40 -12
View File
@@ -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<TrafficSnapshot> {
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(
+28 -4
View File
@@ -886,9 +886,12 @@ export function ProfilesDataTable({
const newSnapshots: Record<string, TrafficSnapshot> = {};
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<string, TrafficSnapshot> = {};
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]
+5 -1
View File
@@ -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)