mockforge_chaos/
latency_metrics.rs

1//! Latency metrics tracking for real-time visualization
2
3use parking_lot::RwLock;
4use serde::{Deserialize, Serialize};
5use std::collections::VecDeque;
6use std::sync::Arc;
7use std::time::SystemTime;
8
9/// Single latency sample
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct LatencySample {
12    /// Timestamp in milliseconds since epoch
13    pub timestamp: u64,
14    /// Latency in milliseconds
15    pub latency_ms: u64,
16}
17
18/// Latency metrics tracker
19/// Tracks recent latency samples for real-time visualization
20#[derive(Debug, Clone)]
21pub struct LatencyMetricsTracker {
22    /// Recent latency samples (max 1000 samples or 5 minutes)
23    samples: Arc<RwLock<VecDeque<LatencySample>>>,
24    /// Maximum number of samples to keep
25    max_samples: usize,
26    /// Maximum age of samples in seconds (5 minutes)
27    max_age_seconds: u64,
28}
29
30impl LatencyMetricsTracker {
31    /// Create a new latency metrics tracker
32    pub fn new() -> Self {
33        Self {
34            samples: Arc::new(RwLock::new(VecDeque::new())),
35            max_samples: 1000,
36            max_age_seconds: 300, // 5 minutes
37        }
38    }
39
40    /// Record a latency sample
41    pub fn record_latency(&self, latency_ms: u64) {
42        let now = SystemTime::now()
43            .duration_since(std::time::UNIX_EPOCH)
44            .unwrap_or_default()
45            .as_millis() as u64;
46
47        let sample = LatencySample {
48            timestamp: now,
49            latency_ms,
50        };
51
52        let mut samples = self.samples.write();
53        samples.push_back(sample);
54
55        // Clean up old samples
56        self.cleanup_old_samples(&mut samples);
57    }
58
59    /// Get all latency samples within the time window
60    pub fn get_samples(&self) -> Vec<LatencySample> {
61        let mut samples = self.samples.write();
62        self.cleanup_old_samples(&mut samples);
63        samples.iter().cloned().collect()
64    }
65
66    /// Get samples within a time range
67    pub fn get_samples_in_range(&self, start_ms: u64, end_ms: u64) -> Vec<LatencySample> {
68        let samples = self.samples.read();
69        samples
70            .iter()
71            .filter(|s| s.timestamp >= start_ms && s.timestamp <= end_ms)
72            .cloned()
73            .collect()
74    }
75
76    /// Clean up old samples
77    fn cleanup_old_samples(&self, samples: &mut VecDeque<LatencySample>) {
78        let now = SystemTime::now()
79            .duration_since(std::time::UNIX_EPOCH)
80            .unwrap_or_default()
81            .as_millis() as u64;
82        let cutoff = now.saturating_sub(self.max_age_seconds * 1000);
83
84        // Remove samples older than cutoff
85        while samples.front().map(|s| s.timestamp < cutoff).unwrap_or(false) {
86            samples.pop_front();
87        }
88
89        // Limit to max_samples
90        while samples.len() > self.max_samples {
91            samples.pop_front();
92        }
93    }
94
95    /// Clear all samples
96    pub fn clear(&self) {
97        let mut samples = self.samples.write();
98        samples.clear();
99    }
100
101    /// Get statistics about current samples
102    pub fn get_stats(&self) -> LatencyStats {
103        let samples = self.get_samples();
104        if samples.is_empty() {
105            return LatencyStats {
106                count: 0,
107                min_ms: 0,
108                max_ms: 0,
109                avg_ms: 0.0,
110                p50_ms: 0,
111                p95_ms: 0,
112                p99_ms: 0,
113            };
114        }
115
116        let mut latencies: Vec<u64> = samples.iter().map(|s| s.latency_ms).collect();
117        latencies.sort();
118
119        let count = latencies.len();
120        let min_ms = latencies[0];
121        let max_ms = latencies[count - 1];
122        let sum: u64 = latencies.iter().sum();
123        let avg_ms = sum as f64 / count as f64;
124
125        let p50_ms = latencies[count / 2];
126        let p95_ms = latencies[(count * 95) / 100];
127        let p99_ms = latencies[(count * 99) / 100];
128
129        LatencyStats {
130            count,
131            min_ms,
132            max_ms,
133            avg_ms,
134            p50_ms,
135            p95_ms,
136            p99_ms,
137        }
138    }
139}
140
141impl Default for LatencyMetricsTracker {
142    fn default() -> Self {
143        Self::new()
144    }
145}
146
147/// Latency statistics
148#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct LatencyStats {
150    /// Number of samples
151    pub count: usize,
152    /// Minimum latency in ms
153    pub min_ms: u64,
154    /// Maximum latency in ms
155    pub max_ms: u64,
156    /// Average latency in ms
157    pub avg_ms: f64,
158    /// 50th percentile (median) latency in ms
159    pub p50_ms: u64,
160    /// 95th percentile latency in ms
161    pub p95_ms: u64,
162    /// 99th percentile latency in ms
163    pub p99_ms: u64,
164}