mockforge_chaos/
latency_metrics.rs1use parking_lot::RwLock;
4use serde::{Deserialize, Serialize};
5use std::collections::VecDeque;
6use std::sync::Arc;
7use std::time::SystemTime;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct LatencySample {
12 pub timestamp: u64,
14 pub latency_ms: u64,
16}
17
18#[derive(Debug, Clone)]
21pub struct LatencyMetricsTracker {
22 samples: Arc<RwLock<VecDeque<LatencySample>>>,
24 max_samples: usize,
26 max_age_seconds: u64,
28}
29
30impl LatencyMetricsTracker {
31 pub fn new() -> Self {
33 Self {
34 samples: Arc::new(RwLock::new(VecDeque::new())),
35 max_samples: 1000,
36 max_age_seconds: 300, }
38 }
39
40 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 self.cleanup_old_samples(&mut samples);
57 }
58
59 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 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 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 while samples.front().map(|s| s.timestamp < cutoff).unwrap_or(false) {
86 samples.pop_front();
87 }
88
89 while samples.len() > self.max_samples {
91 samples.pop_front();
92 }
93 }
94
95 pub fn clear(&self) {
97 let mut samples = self.samples.write();
98 samples.clear();
99 }
100
101 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#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct LatencyStats {
150 pub count: usize,
152 pub min_ms: u64,
154 pub max_ms: u64,
156 pub avg_ms: f64,
158 pub p50_ms: u64,
160 pub p95_ms: u64,
162 pub p99_ms: u64,
164}