1use std::sync::atomic::{AtomicU64, Ordering};
6use std::time::{Duration, Instant};
7
8use super::classifier::WorkloadType;
9use super::tiers::CacheTier;
10
11#[derive(Debug)]
13pub struct DistribCacheMetrics {
14 start_time: Instant,
16
17 pub cache_hits: AtomicU64,
19 pub cache_misses: AtomicU64,
20 pub cache_puts: AtomicU64,
21 pub cache_evictions: AtomicU64,
22 pub cache_invalidations: AtomicU64,
23
24 pub l1_hits: AtomicU64,
26 pub l1_misses: AtomicU64,
27 pub l1_size_bytes: AtomicU64,
28 pub l1_entries: AtomicU64,
29
30 pub l2_hits: AtomicU64,
31 pub l2_misses: AtomicU64,
32 pub l2_size_bytes: AtomicU64,
33 pub l2_entries: AtomicU64,
34
35 pub l3_hits: AtomicU64,
36 pub l3_misses: AtomicU64,
37 pub l3_size_bytes: AtomicU64,
38 pub l3_entries: AtomicU64,
39
40 pub latency_under_100us: AtomicU64,
42 pub latency_100us_1ms: AtomicU64,
43 pub latency_1ms_10ms: AtomicU64,
44 pub latency_10ms_100ms: AtomicU64,
45 pub latency_over_100ms: AtomicU64,
46 pub latency_total_us: AtomicU64,
47 pub latency_count: AtomicU64,
48
49 pub oltp_queries: AtomicU64,
51 pub olap_queries: AtomicU64,
52 pub vector_queries: AtomicU64,
53 pub ai_agent_queries: AtomicU64,
54 pub rag_queries: AtomicU64,
55 pub mixed_queries: AtomicU64,
56
57 pub conversation_cache_hits: AtomicU64,
59 pub conversation_cache_misses: AtomicU64,
60 pub rag_cache_hits: AtomicU64,
61 pub rag_cache_misses: AtomicU64,
62 pub tool_cache_hits: AtomicU64,
63 pub tool_cache_misses: AtomicU64,
64 pub semantic_cache_hits: AtomicU64,
65 pub semantic_cache_misses: AtomicU64,
66
67 pub prefetch_hits: AtomicU64,
69 pub prefetch_misses: AtomicU64,
70 pub prefetch_predictions: AtomicU64,
71
72 pub wal_invalidations: AtomicU64,
74 pub ttl_invalidations: AtomicU64,
75 pub manual_invalidations: AtomicU64,
76
77 pub scheduled_queries: AtomicU64,
79 pub queued_queries: AtomicU64,
80 pub rejected_queries: AtomicU64,
81
82 pub cache_errors: AtomicU64,
84 pub timeout_errors: AtomicU64,
85 pub serialization_errors: AtomicU64,
86}
87
88impl Default for DistribCacheMetrics {
89 fn default() -> Self {
90 Self::new()
91 }
92}
93
94impl DistribCacheMetrics {
95 pub fn new() -> Self {
97 Self {
98 start_time: Instant::now(),
99 cache_hits: AtomicU64::new(0),
100 cache_misses: AtomicU64::new(0),
101 cache_puts: AtomicU64::new(0),
102 cache_evictions: AtomicU64::new(0),
103 cache_invalidations: AtomicU64::new(0),
104 l1_hits: AtomicU64::new(0),
105 l1_misses: AtomicU64::new(0),
106 l1_size_bytes: AtomicU64::new(0),
107 l1_entries: AtomicU64::new(0),
108 l2_hits: AtomicU64::new(0),
109 l2_misses: AtomicU64::new(0),
110 l2_size_bytes: AtomicU64::new(0),
111 l2_entries: AtomicU64::new(0),
112 l3_hits: AtomicU64::new(0),
113 l3_misses: AtomicU64::new(0),
114 l3_size_bytes: AtomicU64::new(0),
115 l3_entries: AtomicU64::new(0),
116 latency_under_100us: AtomicU64::new(0),
117 latency_100us_1ms: AtomicU64::new(0),
118 latency_1ms_10ms: AtomicU64::new(0),
119 latency_10ms_100ms: AtomicU64::new(0),
120 latency_over_100ms: AtomicU64::new(0),
121 latency_total_us: AtomicU64::new(0),
122 latency_count: AtomicU64::new(0),
123 oltp_queries: AtomicU64::new(0),
124 olap_queries: AtomicU64::new(0),
125 vector_queries: AtomicU64::new(0),
126 ai_agent_queries: AtomicU64::new(0),
127 rag_queries: AtomicU64::new(0),
128 mixed_queries: AtomicU64::new(0),
129 conversation_cache_hits: AtomicU64::new(0),
130 conversation_cache_misses: AtomicU64::new(0),
131 rag_cache_hits: AtomicU64::new(0),
132 rag_cache_misses: AtomicU64::new(0),
133 tool_cache_hits: AtomicU64::new(0),
134 tool_cache_misses: AtomicU64::new(0),
135 semantic_cache_hits: AtomicU64::new(0),
136 semantic_cache_misses: AtomicU64::new(0),
137 prefetch_hits: AtomicU64::new(0),
138 prefetch_misses: AtomicU64::new(0),
139 prefetch_predictions: AtomicU64::new(0),
140 wal_invalidations: AtomicU64::new(0),
141 ttl_invalidations: AtomicU64::new(0),
142 manual_invalidations: AtomicU64::new(0),
143 scheduled_queries: AtomicU64::new(0),
144 queued_queries: AtomicU64::new(0),
145 rejected_queries: AtomicU64::new(0),
146 cache_errors: AtomicU64::new(0),
147 timeout_errors: AtomicU64::new(0),
148 serialization_errors: AtomicU64::new(0),
149 }
150 }
151
152 pub fn record_hit(&self, tier: CacheTier) {
154 self.cache_hits.fetch_add(1, Ordering::Relaxed);
155 match tier {
156 CacheTier::L1 => { self.l1_hits.fetch_add(1, Ordering::Relaxed); }
157 CacheTier::L2 => { self.l2_hits.fetch_add(1, Ordering::Relaxed); }
158 CacheTier::L3 => { self.l3_hits.fetch_add(1, Ordering::Relaxed); }
159 }
160 }
161
162 pub fn record_miss(&self, tier: CacheTier) {
164 self.cache_misses.fetch_add(1, Ordering::Relaxed);
165 match tier {
166 CacheTier::L1 => { self.l1_misses.fetch_add(1, Ordering::Relaxed); }
167 CacheTier::L2 => { self.l2_misses.fetch_add(1, Ordering::Relaxed); }
168 CacheTier::L3 => { self.l3_misses.fetch_add(1, Ordering::Relaxed); }
169 }
170 }
171
172 pub fn record_put(&self) {
174 self.cache_puts.fetch_add(1, Ordering::Relaxed);
175 }
176
177 pub fn record_eviction(&self) {
179 self.cache_evictions.fetch_add(1, Ordering::Relaxed);
180 }
181
182 pub fn record_invalidation(&self, source: InvalidationSource) {
184 self.cache_invalidations.fetch_add(1, Ordering::Relaxed);
185 match source {
186 InvalidationSource::WAL => { self.wal_invalidations.fetch_add(1, Ordering::Relaxed); }
187 InvalidationSource::TTL => { self.ttl_invalidations.fetch_add(1, Ordering::Relaxed); }
188 InvalidationSource::Manual => { self.manual_invalidations.fetch_add(1, Ordering::Relaxed); }
189 }
190 }
191
192 pub fn record_latency(&self, duration: Duration) {
194 let us = duration.as_micros() as u64;
195 self.latency_total_us.fetch_add(us, Ordering::Relaxed);
196 self.latency_count.fetch_add(1, Ordering::Relaxed);
197
198 if us < 100 {
199 self.latency_under_100us.fetch_add(1, Ordering::Relaxed);
200 } else if us < 1000 {
201 self.latency_100us_1ms.fetch_add(1, Ordering::Relaxed);
202 } else if us < 10_000 {
203 self.latency_1ms_10ms.fetch_add(1, Ordering::Relaxed);
204 } else if us < 100_000 {
205 self.latency_10ms_100ms.fetch_add(1, Ordering::Relaxed);
206 } else {
207 self.latency_over_100ms.fetch_add(1, Ordering::Relaxed);
208 }
209 }
210
211 pub fn record_workload(&self, workload: WorkloadType) {
213 match workload {
214 WorkloadType::OLTP => { self.oltp_queries.fetch_add(1, Ordering::Relaxed); }
215 WorkloadType::OLAP => { self.olap_queries.fetch_add(1, Ordering::Relaxed); }
216 WorkloadType::Vector => { self.vector_queries.fetch_add(1, Ordering::Relaxed); }
217 WorkloadType::AIAgent => { self.ai_agent_queries.fetch_add(1, Ordering::Relaxed); }
218 WorkloadType::RAG => { self.rag_queries.fetch_add(1, Ordering::Relaxed); }
219 WorkloadType::Mixed => { self.mixed_queries.fetch_add(1, Ordering::Relaxed); }
220 }
221 }
222
223 pub fn update_tier_size(&self, tier: CacheTier, size_bytes: u64, entries: u64) {
225 match tier {
226 CacheTier::L1 => {
227 self.l1_size_bytes.store(size_bytes, Ordering::Relaxed);
228 self.l1_entries.store(entries, Ordering::Relaxed);
229 }
230 CacheTier::L2 => {
231 self.l2_size_bytes.store(size_bytes, Ordering::Relaxed);
232 self.l2_entries.store(entries, Ordering::Relaxed);
233 }
234 CacheTier::L3 => {
235 self.l3_size_bytes.store(size_bytes, Ordering::Relaxed);
236 self.l3_entries.store(entries, Ordering::Relaxed);
237 }
238 }
239 }
240
241 pub fn record_error(&self, error_type: ErrorType) {
243 self.cache_errors.fetch_add(1, Ordering::Relaxed);
244 match error_type {
245 ErrorType::Timeout => { self.timeout_errors.fetch_add(1, Ordering::Relaxed); }
246 ErrorType::Serialization => { self.serialization_errors.fetch_add(1, Ordering::Relaxed); }
247 ErrorType::Other => {}
248 }
249 }
250
251 pub fn uptime(&self) -> Duration {
253 self.start_time.elapsed()
254 }
255
256 pub fn hit_rate(&self) -> f64 {
258 let hits = self.cache_hits.load(Ordering::Relaxed);
259 let misses = self.cache_misses.load(Ordering::Relaxed);
260 let total = hits + misses;
261 if total > 0 {
262 hits as f64 / total as f64
263 } else {
264 0.0
265 }
266 }
267
268 pub fn avg_latency_us(&self) -> f64 {
270 let total = self.latency_total_us.load(Ordering::Relaxed);
271 let count = self.latency_count.load(Ordering::Relaxed);
272 if count > 0 {
273 total as f64 / count as f64
274 } else {
275 0.0
276 }
277 }
278
279 pub fn to_prometheus(&self) -> String {
281 let mut output = String::with_capacity(4096);
282
283 output.push_str(&format!(
285 "# HELP distribcache_uptime_seconds Cache uptime in seconds\n\
286 # TYPE distribcache_uptime_seconds gauge\n\
287 distribcache_uptime_seconds {}\n\n",
288 self.uptime().as_secs()
289 ));
290
291 output.push_str(&format!(
293 "# HELP distribcache_operations_total Total cache operations\n\
294 # TYPE distribcache_operations_total counter\n\
295 distribcache_operations_total{{operation=\"hit\"}} {}\n\
296 distribcache_operations_total{{operation=\"miss\"}} {}\n\
297 distribcache_operations_total{{operation=\"put\"}} {}\n\
298 distribcache_operations_total{{operation=\"eviction\"}} {}\n\
299 distribcache_operations_total{{operation=\"invalidation\"}} {}\n\n",
300 self.cache_hits.load(Ordering::Relaxed),
301 self.cache_misses.load(Ordering::Relaxed),
302 self.cache_puts.load(Ordering::Relaxed),
303 self.cache_evictions.load(Ordering::Relaxed),
304 self.cache_invalidations.load(Ordering::Relaxed),
305 ));
306
307 output.push_str(&format!(
309 "# HELP distribcache_hit_rate Cache hit rate\n\
310 # TYPE distribcache_hit_rate gauge\n\
311 distribcache_hit_rate {:.4}\n\n",
312 self.hit_rate()
313 ));
314
315 output.push_str(&format!(
317 "# HELP distribcache_tier_hits_total Hits per tier\n\
318 # TYPE distribcache_tier_hits_total counter\n\
319 distribcache_tier_hits_total{{tier=\"l1\"}} {}\n\
320 distribcache_tier_hits_total{{tier=\"l2\"}} {}\n\
321 distribcache_tier_hits_total{{tier=\"l3\"}} {}\n\n",
322 self.l1_hits.load(Ordering::Relaxed),
323 self.l2_hits.load(Ordering::Relaxed),
324 self.l3_hits.load(Ordering::Relaxed),
325 ));
326
327 output.push_str(&format!(
328 "# HELP distribcache_tier_size_bytes Size per tier in bytes\n\
329 # TYPE distribcache_tier_size_bytes gauge\n\
330 distribcache_tier_size_bytes{{tier=\"l1\"}} {}\n\
331 distribcache_tier_size_bytes{{tier=\"l2\"}} {}\n\
332 distribcache_tier_size_bytes{{tier=\"l3\"}} {}\n\n",
333 self.l1_size_bytes.load(Ordering::Relaxed),
334 self.l2_size_bytes.load(Ordering::Relaxed),
335 self.l3_size_bytes.load(Ordering::Relaxed),
336 ));
337
338 output.push_str(&format!(
339 "# HELP distribcache_tier_entries Entries per tier\n\
340 # TYPE distribcache_tier_entries gauge\n\
341 distribcache_tier_entries{{tier=\"l1\"}} {}\n\
342 distribcache_tier_entries{{tier=\"l2\"}} {}\n\
343 distribcache_tier_entries{{tier=\"l3\"}} {}\n\n",
344 self.l1_entries.load(Ordering::Relaxed),
345 self.l2_entries.load(Ordering::Relaxed),
346 self.l3_entries.load(Ordering::Relaxed),
347 ));
348
349 output.push_str(&format!(
351 "# HELP distribcache_latency_bucket Latency distribution\n\
352 # TYPE distribcache_latency_bucket histogram\n\
353 distribcache_latency_bucket{{le=\"0.0001\"}} {}\n\
354 distribcache_latency_bucket{{le=\"0.001\"}} {}\n\
355 distribcache_latency_bucket{{le=\"0.01\"}} {}\n\
356 distribcache_latency_bucket{{le=\"0.1\"}} {}\n\
357 distribcache_latency_bucket{{le=\"+Inf\"}} {}\n\n",
358 self.latency_under_100us.load(Ordering::Relaxed),
359 self.latency_under_100us.load(Ordering::Relaxed) +
360 self.latency_100us_1ms.load(Ordering::Relaxed),
361 self.latency_under_100us.load(Ordering::Relaxed) +
362 self.latency_100us_1ms.load(Ordering::Relaxed) +
363 self.latency_1ms_10ms.load(Ordering::Relaxed),
364 self.latency_under_100us.load(Ordering::Relaxed) +
365 self.latency_100us_1ms.load(Ordering::Relaxed) +
366 self.latency_1ms_10ms.load(Ordering::Relaxed) +
367 self.latency_10ms_100ms.load(Ordering::Relaxed),
368 self.latency_count.load(Ordering::Relaxed),
369 ));
370
371 output.push_str(&format!(
372 "# HELP distribcache_latency_avg_us Average latency in microseconds\n\
373 # TYPE distribcache_latency_avg_us gauge\n\
374 distribcache_latency_avg_us {:.2}\n\n",
375 self.avg_latency_us()
376 ));
377
378 output.push_str(&format!(
380 "# HELP distribcache_workload_total Queries by workload type\n\
381 # TYPE distribcache_workload_total counter\n\
382 distribcache_workload_total{{type=\"oltp\"}} {}\n\
383 distribcache_workload_total{{type=\"olap\"}} {}\n\
384 distribcache_workload_total{{type=\"vector\"}} {}\n\
385 distribcache_workload_total{{type=\"ai_agent\"}} {}\n\
386 distribcache_workload_total{{type=\"rag\"}} {}\n\
387 distribcache_workload_total{{type=\"mixed\"}} {}\n\n",
388 self.oltp_queries.load(Ordering::Relaxed),
389 self.olap_queries.load(Ordering::Relaxed),
390 self.vector_queries.load(Ordering::Relaxed),
391 self.ai_agent_queries.load(Ordering::Relaxed),
392 self.rag_queries.load(Ordering::Relaxed),
393 self.mixed_queries.load(Ordering::Relaxed),
394 ));
395
396 output.push_str(&format!(
398 "# HELP distribcache_ai_cache_hits AI cache hits\n\
399 # TYPE distribcache_ai_cache_hits counter\n\
400 distribcache_ai_cache_hits{{cache=\"conversation\"}} {}\n\
401 distribcache_ai_cache_hits{{cache=\"rag\"}} {}\n\
402 distribcache_ai_cache_hits{{cache=\"tool\"}} {}\n\
403 distribcache_ai_cache_hits{{cache=\"semantic\"}} {}\n\n",
404 self.conversation_cache_hits.load(Ordering::Relaxed),
405 self.rag_cache_hits.load(Ordering::Relaxed),
406 self.tool_cache_hits.load(Ordering::Relaxed),
407 self.semantic_cache_hits.load(Ordering::Relaxed),
408 ));
409
410 output.push_str(&format!(
412 "# HELP distribcache_invalidations_total Invalidations by source\n\
413 # TYPE distribcache_invalidations_total counter\n\
414 distribcache_invalidations_total{{source=\"wal\"}} {}\n\
415 distribcache_invalidations_total{{source=\"ttl\"}} {}\n\
416 distribcache_invalidations_total{{source=\"manual\"}} {}\n\n",
417 self.wal_invalidations.load(Ordering::Relaxed),
418 self.ttl_invalidations.load(Ordering::Relaxed),
419 self.manual_invalidations.load(Ordering::Relaxed),
420 ));
421
422 output.push_str(&format!(
424 "# HELP distribcache_errors_total Cache errors\n\
425 # TYPE distribcache_errors_total counter\n\
426 distribcache_errors_total{{type=\"timeout\"}} {}\n\
427 distribcache_errors_total{{type=\"serialization\"}} {}\n\
428 distribcache_errors_total{{type=\"total\"}} {}\n",
429 self.timeout_errors.load(Ordering::Relaxed),
430 self.serialization_errors.load(Ordering::Relaxed),
431 self.cache_errors.load(Ordering::Relaxed),
432 ));
433
434 output
435 }
436
437 pub fn to_json(&self) -> serde_json::Value {
439 serde_json::json!({
440 "uptime_secs": self.uptime().as_secs(),
441 "operations": {
442 "hits": self.cache_hits.load(Ordering::Relaxed),
443 "misses": self.cache_misses.load(Ordering::Relaxed),
444 "puts": self.cache_puts.load(Ordering::Relaxed),
445 "evictions": self.cache_evictions.load(Ordering::Relaxed),
446 "invalidations": self.cache_invalidations.load(Ordering::Relaxed),
447 },
448 "hit_rate": self.hit_rate(),
449 "tiers": {
450 "l1": {
451 "hits": self.l1_hits.load(Ordering::Relaxed),
452 "misses": self.l1_misses.load(Ordering::Relaxed),
453 "size_bytes": self.l1_size_bytes.load(Ordering::Relaxed),
454 "entries": self.l1_entries.load(Ordering::Relaxed),
455 },
456 "l2": {
457 "hits": self.l2_hits.load(Ordering::Relaxed),
458 "misses": self.l2_misses.load(Ordering::Relaxed),
459 "size_bytes": self.l2_size_bytes.load(Ordering::Relaxed),
460 "entries": self.l2_entries.load(Ordering::Relaxed),
461 },
462 "l3": {
463 "hits": self.l3_hits.load(Ordering::Relaxed),
464 "misses": self.l3_misses.load(Ordering::Relaxed),
465 "size_bytes": self.l3_size_bytes.load(Ordering::Relaxed),
466 "entries": self.l3_entries.load(Ordering::Relaxed),
467 },
468 },
469 "latency": {
470 "avg_us": self.avg_latency_us(),
471 "buckets": {
472 "under_100us": self.latency_under_100us.load(Ordering::Relaxed),
473 "100us_1ms": self.latency_100us_1ms.load(Ordering::Relaxed),
474 "1ms_10ms": self.latency_1ms_10ms.load(Ordering::Relaxed),
475 "10ms_100ms": self.latency_10ms_100ms.load(Ordering::Relaxed),
476 "over_100ms": self.latency_over_100ms.load(Ordering::Relaxed),
477 },
478 },
479 "workloads": {
480 "oltp": self.oltp_queries.load(Ordering::Relaxed),
481 "olap": self.olap_queries.load(Ordering::Relaxed),
482 "vector": self.vector_queries.load(Ordering::Relaxed),
483 "ai_agent": self.ai_agent_queries.load(Ordering::Relaxed),
484 "rag": self.rag_queries.load(Ordering::Relaxed),
485 "mixed": self.mixed_queries.load(Ordering::Relaxed),
486 },
487 "ai_caches": {
488 "conversation": {
489 "hits": self.conversation_cache_hits.load(Ordering::Relaxed),
490 "misses": self.conversation_cache_misses.load(Ordering::Relaxed),
491 },
492 "rag": {
493 "hits": self.rag_cache_hits.load(Ordering::Relaxed),
494 "misses": self.rag_cache_misses.load(Ordering::Relaxed),
495 },
496 "tool": {
497 "hits": self.tool_cache_hits.load(Ordering::Relaxed),
498 "misses": self.tool_cache_misses.load(Ordering::Relaxed),
499 },
500 "semantic": {
501 "hits": self.semantic_cache_hits.load(Ordering::Relaxed),
502 "misses": self.semantic_cache_misses.load(Ordering::Relaxed),
503 },
504 },
505 "errors": {
506 "total": self.cache_errors.load(Ordering::Relaxed),
507 "timeout": self.timeout_errors.load(Ordering::Relaxed),
508 "serialization": self.serialization_errors.load(Ordering::Relaxed),
509 },
510 })
511 }
512
513 pub fn reset(&self) {
515 self.cache_hits.store(0, Ordering::Relaxed);
516 self.cache_misses.store(0, Ordering::Relaxed);
517 self.cache_puts.store(0, Ordering::Relaxed);
518 self.cache_evictions.store(0, Ordering::Relaxed);
519 self.cache_invalidations.store(0, Ordering::Relaxed);
520
521 self.l1_hits.store(0, Ordering::Relaxed);
522 self.l1_misses.store(0, Ordering::Relaxed);
523 self.l2_hits.store(0, Ordering::Relaxed);
524 self.l2_misses.store(0, Ordering::Relaxed);
525 self.l3_hits.store(0, Ordering::Relaxed);
526 self.l3_misses.store(0, Ordering::Relaxed);
527
528 self.latency_under_100us.store(0, Ordering::Relaxed);
529 self.latency_100us_1ms.store(0, Ordering::Relaxed);
530 self.latency_1ms_10ms.store(0, Ordering::Relaxed);
531 self.latency_10ms_100ms.store(0, Ordering::Relaxed);
532 self.latency_over_100ms.store(0, Ordering::Relaxed);
533 self.latency_total_us.store(0, Ordering::Relaxed);
534 self.latency_count.store(0, Ordering::Relaxed);
535
536 self.oltp_queries.store(0, Ordering::Relaxed);
537 self.olap_queries.store(0, Ordering::Relaxed);
538 self.vector_queries.store(0, Ordering::Relaxed);
539 self.ai_agent_queries.store(0, Ordering::Relaxed);
540 self.rag_queries.store(0, Ordering::Relaxed);
541 self.mixed_queries.store(0, Ordering::Relaxed);
542
543 self.cache_errors.store(0, Ordering::Relaxed);
544 self.timeout_errors.store(0, Ordering::Relaxed);
545 self.serialization_errors.store(0, Ordering::Relaxed);
546 }
547}
548
549#[derive(Debug, Clone, Copy, PartialEq, Eq)]
553pub enum InvalidationSource {
554 WAL,
555 TTL,
556 Manual,
557}
558
559#[derive(Debug, Clone, Copy, PartialEq, Eq)]
563pub enum ErrorType {
564 Timeout,
565 Serialization,
566 Other,
567}
568
569#[cfg(test)]
570mod tests {
571 use super::*;
572
573 #[test]
574 fn test_metrics_creation() {
575 let metrics = DistribCacheMetrics::new();
576 assert_eq!(metrics.cache_hits.load(Ordering::Relaxed), 0);
577 assert_eq!(metrics.cache_misses.load(Ordering::Relaxed), 0);
578 }
579
580 #[test]
581 fn test_record_hit() {
582 let metrics = DistribCacheMetrics::new();
583
584 metrics.record_hit(CacheTier::L1);
585 metrics.record_hit(CacheTier::L2);
586 metrics.record_hit(CacheTier::L1);
587
588 assert_eq!(metrics.cache_hits.load(Ordering::Relaxed), 3);
589 assert_eq!(metrics.l1_hits.load(Ordering::Relaxed), 2);
590 assert_eq!(metrics.l2_hits.load(Ordering::Relaxed), 1);
591 }
592
593 #[test]
594 fn test_hit_rate() {
595 let metrics = DistribCacheMetrics::new();
596
597 metrics.record_hit(CacheTier::L1);
598 metrics.record_hit(CacheTier::L1);
599 metrics.record_miss(CacheTier::L1);
600 metrics.record_miss(CacheTier::L1);
601
602 assert!((metrics.hit_rate() - 0.5).abs() < 0.001);
603 }
604
605 #[test]
606 fn test_record_latency() {
607 let metrics = DistribCacheMetrics::new();
608
609 metrics.record_latency(Duration::from_micros(50)); metrics.record_latency(Duration::from_micros(500)); metrics.record_latency(Duration::from_millis(5)); assert_eq!(metrics.latency_under_100us.load(Ordering::Relaxed), 1);
614 assert_eq!(metrics.latency_100us_1ms.load(Ordering::Relaxed), 1);
615 assert_eq!(metrics.latency_1ms_10ms.load(Ordering::Relaxed), 1);
616 assert_eq!(metrics.latency_count.load(Ordering::Relaxed), 3);
617 }
618
619 #[test]
620 fn test_prometheus_export() {
621 let metrics = DistribCacheMetrics::new();
622
623 metrics.record_hit(CacheTier::L1);
624 metrics.record_miss(CacheTier::L2);
625 metrics.record_workload(WorkloadType::OLTP);
626
627 let prometheus = metrics.to_prometheus();
628
629 assert!(prometheus.contains("distribcache_operations_total"));
630 assert!(prometheus.contains("distribcache_hit_rate"));
631 assert!(prometheus.contains("distribcache_tier_hits_total"));
632 }
633
634 #[test]
635 fn test_json_export() {
636 let metrics = DistribCacheMetrics::new();
637
638 metrics.record_hit(CacheTier::L1);
639 metrics.record_put();
640
641 let json = metrics.to_json();
642
643 assert_eq!(json["operations"]["hits"], 1);
644 assert_eq!(json["operations"]["puts"], 1);
645 }
646
647 #[test]
648 fn test_reset() {
649 let metrics = DistribCacheMetrics::new();
650
651 metrics.record_hit(CacheTier::L1);
652 metrics.record_miss(CacheTier::L2);
653 metrics.record_put();
654
655 metrics.reset();
656
657 assert_eq!(metrics.cache_hits.load(Ordering::Relaxed), 0);
658 assert_eq!(metrics.cache_misses.load(Ordering::Relaxed), 0);
659 assert_eq!(metrics.cache_puts.load(Ordering::Relaxed), 0);
660 }
661
662 #[test]
663 fn test_workload_tracking() {
664 let metrics = DistribCacheMetrics::new();
665
666 metrics.record_workload(WorkloadType::OLTP);
667 metrics.record_workload(WorkloadType::OLTP);
668 metrics.record_workload(WorkloadType::OLAP);
669 metrics.record_workload(WorkloadType::AIAgent);
670
671 assert_eq!(metrics.oltp_queries.load(Ordering::Relaxed), 2);
672 assert_eq!(metrics.olap_queries.load(Ordering::Relaxed), 1);
673 assert_eq!(metrics.ai_agent_queries.load(Ordering::Relaxed), 1);
674 }
675
676 #[test]
677 fn test_error_tracking() {
678 let metrics = DistribCacheMetrics::new();
679
680 metrics.record_error(ErrorType::Timeout);
681 metrics.record_error(ErrorType::Timeout);
682 metrics.record_error(ErrorType::Serialization);
683
684 assert_eq!(metrics.cache_errors.load(Ordering::Relaxed), 3);
685 assert_eq!(metrics.timeout_errors.load(Ordering::Relaxed), 2);
686 assert_eq!(metrics.serialization_errors.load(Ordering::Relaxed), 1);
687 }
688}