mockforge_chaos/
latency_metrics.rs1use 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#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct LatencySample {
13 pub timestamp: u64,
15 pub latency_ms: u64,
17}
18
19#[derive(Debug, Clone)]
22pub struct LatencyMetricsTracker {
23 samples: Arc<RwLock<VecDeque<LatencySample>>>,
25 max_samples: usize,
27 max_age_seconds: u64,
29}
30
31impl LatencyMetricsTracker {
32 pub fn new() -> Self {
34 Self {
35 samples: Arc::new(RwLock::new(VecDeque::new())),
36 max_samples: 1000,
37 max_age_seconds: 300, }
39 }
40
41 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 self.cleanup_old_samples(&mut samples);
58 }
59
60 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 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 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 while samples.front().map(|s| s.timestamp < cutoff).unwrap_or(false) {
87 samples.pop_front();
88 }
89
90 while samples.len() > self.max_samples {
92 samples.pop_front();
93 }
94 }
95
96 pub fn clear(&self) {
98 let mut samples = self.samples.write();
99 samples.clear();
100 }
101
102 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#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct LatencyStats {
151 pub count: usize,
153 pub min_ms: u64,
155 pub max_ms: u64,
157 pub avg_ms: f64,
159 pub p50_ms: u64,
161 pub p95_ms: u64,
163 pub p99_ms: u64,
165}