mockforge_chaos/
latency_metrics.rs

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