Skip to main content

tenflowers_dataset/cache/telemetry/
types.rs

1//! Cache telemetry types: structs and enums used throughout the telemetry subsystem.
2
3use std::collections::HashMap;
4use std::time::{Duration, Instant, SystemTime};
5
6#[cfg(feature = "serialize")]
7use serde::{Deserialize, Serialize};
8
9/// Comprehensive cache telemetry metrics
10#[derive(Debug, Clone)]
11pub struct CacheTelemetryMetrics {
12    /// Basic hit/miss counters
13    pub hits: u64,
14    pub misses: u64,
15    pub evictions: u64,
16    pub insertions: u64,
17
18    /// Latency metrics (in microseconds)
19    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    /// Memory metrics
26    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    /// Throughput metrics
32    pub requests_per_second: f64,
33    pub bytes_per_second: f64,
34
35    /// Time window for metrics
36    pub window_start: Instant,
37    pub window_duration: Duration,
38
39    /// Cache effectiveness
40    pub hit_ratio: f64,
41    pub byte_hit_ratio: f64,
42    pub eviction_rate: f64,
43}
44
45impl CacheTelemetryMetrics {
46    /// Create new empty metrics
47    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    /// Calculate derived metrics
73    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/// Individual cache operation event
106#[derive(Debug, Clone)]
107pub struct CacheEvent {
108    /// Type of cache operation
109    pub event_type: CacheEventType,
110    /// Timestamp of the event
111    pub timestamp: Instant,
112    /// Latency of the operation
113    pub latency: Duration,
114    /// Size of data involved (bytes)
115    pub size_bytes: Option<usize>,
116    /// Cache key identifier
117    pub key_hash: u64,
118}
119
120/// Types of cache events
121#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub enum CacheEventType {
123    /// Cache hit
124    Hit,
125    /// Cache miss
126    Miss,
127    /// Item eviction
128    Eviction,
129    /// Item insertion
130    Insertion,
131    /// Cache clear
132    Clear,
133}
134
135/// Time-series data point for tracking metrics over time
136#[derive(Debug, Clone)]
137pub struct MetricsSnapshot {
138    /// Timestamp of the snapshot
139    pub timestamp: Instant,
140    /// Metrics at this point in time
141    pub metrics: CacheTelemetryMetrics,
142}
143
144/// Configuration for telemetry collection
145#[derive(Debug, Clone)]
146pub struct TelemetryConfig {
147    /// Maximum number of events to keep in buffer
148    pub max_events: usize,
149    /// Maximum number of snapshots to keep
150    pub max_snapshots: usize,
151    /// Snapshot interval
152    pub snapshot_interval: Duration,
153    /// Enable detailed latency tracking
154    pub track_latency_histogram: bool,
155    /// Enable per-key statistics
156    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/// Alert types for cache performance issues
172#[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/// Alert severity levels
183#[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/// Performance alert
192#[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/// Alert configuration thresholds
204#[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/// Performance baselines for comparison
227#[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/// Aggregated statistics with moving averages
250#[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
276/// Calculate percentiles from a latency histogram (bucket → count map).
277///
278/// Buckets are in microseconds. Returns (p50, p95, p99) as `f64` microseconds.
279pub(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}