tenflowers_dataset/cache/telemetry/
types.rs1use std::collections::HashMap;
4use std::time::{Duration, Instant, SystemTime};
5
6#[cfg(feature = "serialize")]
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone)]
11pub struct CacheTelemetryMetrics {
12 pub hits: u64,
14 pub misses: u64,
15 pub evictions: u64,
16 pub insertions: u64,
17
18 pub avg_hit_latency_us: f64,
20 pub avg_miss_latency_us: f64,
21 pub p50_latency_us: f64,
22 pub p95_latency_us: f64,
23 pub p99_latency_us: f64,
24
25 pub current_size_bytes: usize,
27 pub peak_size_bytes: usize,
28 pub total_allocated_bytes: u64,
29 pub total_freed_bytes: u64,
30
31 pub requests_per_second: f64,
33 pub bytes_per_second: f64,
34
35 pub window_start: Instant,
37 pub window_duration: Duration,
38
39 pub hit_ratio: f64,
41 pub byte_hit_ratio: f64,
42 pub eviction_rate: f64,
43}
44
45impl CacheTelemetryMetrics {
46 pub fn new() -> Self {
48 Self {
49 hits: 0,
50 misses: 0,
51 evictions: 0,
52 insertions: 0,
53 avg_hit_latency_us: 0.0,
54 avg_miss_latency_us: 0.0,
55 p50_latency_us: 0.0,
56 p95_latency_us: 0.0,
57 p99_latency_us: 0.0,
58 current_size_bytes: 0,
59 peak_size_bytes: 0,
60 total_allocated_bytes: 0,
61 total_freed_bytes: 0,
62 requests_per_second: 0.0,
63 bytes_per_second: 0.0,
64 window_start: Instant::now(),
65 window_duration: Duration::from_secs(0),
66 hit_ratio: 0.0,
67 byte_hit_ratio: 0.0,
68 eviction_rate: 0.0,
69 }
70 }
71
72 pub fn calculate_derived(&mut self) {
74 let total_requests = self.hits + self.misses;
75 self.hit_ratio = if total_requests > 0 {
76 self.hits as f64 / total_requests as f64
77 } else {
78 0.0
79 };
80
81 let total_bytes = self.total_allocated_bytes;
82 let hit_bytes =
83 (self.hits as f64 / total_requests.max(1) as f64 * total_bytes as f64) as u64;
84 self.byte_hit_ratio = if total_bytes > 0 {
85 hit_bytes as f64 / total_bytes as f64
86 } else {
87 0.0
88 };
89
90 let duration_secs = self.window_duration.as_secs_f64();
91 if duration_secs > 0.0 {
92 self.requests_per_second = total_requests as f64 / duration_secs;
93 self.bytes_per_second = total_bytes as f64 / duration_secs;
94 self.eviction_rate = self.evictions as f64 / duration_secs;
95 }
96 }
97}
98
99impl Default for CacheTelemetryMetrics {
100 fn default() -> Self {
101 Self::new()
102 }
103}
104
105#[derive(Debug, Clone)]
107pub struct CacheEvent {
108 pub event_type: CacheEventType,
110 pub timestamp: Instant,
112 pub latency: Duration,
114 pub size_bytes: Option<usize>,
116 pub key_hash: u64,
118}
119
120#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub enum CacheEventType {
123 Hit,
125 Miss,
127 Eviction,
129 Insertion,
131 Clear,
133}
134
135#[derive(Debug, Clone)]
137pub struct MetricsSnapshot {
138 pub timestamp: Instant,
140 pub metrics: CacheTelemetryMetrics,
142}
143
144#[derive(Debug, Clone)]
146pub struct TelemetryConfig {
147 pub max_events: usize,
149 pub max_snapshots: usize,
151 pub snapshot_interval: Duration,
153 pub track_latency_histogram: bool,
155 pub track_per_key_stats: bool,
157}
158
159impl Default for TelemetryConfig {
160 fn default() -> Self {
161 Self {
162 max_events: 10000,
163 max_snapshots: 1000,
164 snapshot_interval: Duration::from_secs(60),
165 track_latency_histogram: true,
166 track_per_key_stats: false,
167 }
168 }
169}
170
171#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
173#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
174pub enum AlertType {
175 LowHitRate,
176 HighLatency,
177 HighEvictionRate,
178 MemoryPressure,
179 AnomalyDetected,
180}
181
182#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
184#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
185pub enum AlertSeverity {
186 Info,
187 Warning,
188 Critical,
189}
190
191#[derive(Debug, Clone)]
193#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
194pub struct PerformanceAlert {
195 pub alert_type: AlertType,
196 pub severity: AlertSeverity,
197 pub description: String,
198 pub metric_value: f64,
199 pub threshold_value: f64,
200 pub timestamp: SystemTime,
201}
202
203#[derive(Debug, Clone)]
205#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
206pub struct AlertThresholds {
207 pub min_hit_rate: f64,
208 pub max_latency_us: f64,
209 pub max_eviction_rate: f64,
210 pub max_memory_overhead: f64,
211 pub anomaly_stddev_multiplier: f64,
212}
213
214impl Default for AlertThresholds {
215 fn default() -> Self {
216 Self {
217 min_hit_rate: 0.7,
218 max_latency_us: 10_000.0,
219 max_eviction_rate: 100.0,
220 max_memory_overhead: 2.0,
221 anomaly_stddev_multiplier: 3.0,
222 }
223 }
224}
225
226#[derive(Debug, Clone)]
228#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
229pub struct PerformanceBaselines {
230 pub baseline_hit_rate: f64,
231 pub baseline_latency_us: f64,
232 pub baseline_throughput: f64,
233 pub established_at: SystemTime,
234 pub sample_count: usize,
235}
236
237impl Default for PerformanceBaselines {
238 fn default() -> Self {
239 Self {
240 baseline_hit_rate: 0.0,
241 baseline_latency_us: 0.0,
242 baseline_throughput: 0.0,
243 established_at: SystemTime::now(),
244 sample_count: 0,
245 }
246 }
247}
248
249#[derive(Debug, Clone)]
251#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
252pub struct AggregatedStats {
253 pub moving_avg_hit_rate: f64,
254 pub hit_rate_stddev: f64,
255 pub moving_avg_latency_us: f64,
256 pub latency_stddev: f64,
257 pub peak_hit_rate: f64,
258 pub lowest_hit_rate: f64,
259 pub total_requests: u64,
260}
261
262impl Default for AggregatedStats {
263 fn default() -> Self {
264 Self {
265 moving_avg_hit_rate: 0.0,
266 hit_rate_stddev: 0.0,
267 moving_avg_latency_us: 0.0,
268 latency_stddev: 0.0,
269 peak_hit_rate: 0.0,
270 lowest_hit_rate: 1.0,
271 total_requests: 0,
272 }
273 }
274}
275
276pub(crate) fn calculate_percentiles(histogram: &HashMap<u64, u64>) -> (f64, f64, f64) {
280 if histogram.is_empty() {
281 return (0.0, 0.0, 0.0);
282 }
283
284 let total_count: u64 = histogram.values().sum();
285 let mut sorted_buckets: Vec<_> = histogram.iter().collect();
286 sorted_buckets.sort_by_key(|(bucket, _)| *bucket);
287
288 let find_percentile = |target_pct: f64| -> f64 {
289 let target_count = (total_count as f64 * target_pct) as u64;
290 let mut cumulative = 0u64;
291
292 for (bucket, count) in &sorted_buckets {
293 cumulative += *count;
294 if cumulative >= target_count {
295 return **bucket as f64;
296 }
297 }
298
299 sorted_buckets
300 .last()
301 .map(|(b, _)| **b as f64)
302 .unwrap_or(0.0)
303 };
304
305 (
306 find_percentile(0.50),
307 find_percentile(0.95),
308 find_percentile(0.99),
309 )
310}