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 => {
157 self.l1_hits.fetch_add(1, Ordering::Relaxed);
158 }
159 CacheTier::L2 => {
160 self.l2_hits.fetch_add(1, Ordering::Relaxed);
161 }
162 CacheTier::L3 => {
163 self.l3_hits.fetch_add(1, Ordering::Relaxed);
164 }
165 }
166 }
167
168 pub fn record_miss(&self, tier: CacheTier) {
170 self.cache_misses.fetch_add(1, Ordering::Relaxed);
171 match tier {
172 CacheTier::L1 => {
173 self.l1_misses.fetch_add(1, Ordering::Relaxed);
174 }
175 CacheTier::L2 => {
176 self.l2_misses.fetch_add(1, Ordering::Relaxed);
177 }
178 CacheTier::L3 => {
179 self.l3_misses.fetch_add(1, Ordering::Relaxed);
180 }
181 }
182 }
183
184 pub fn record_put(&self) {
186 self.cache_puts.fetch_add(1, Ordering::Relaxed);
187 }
188
189 pub fn record_eviction(&self) {
191 self.cache_evictions.fetch_add(1, Ordering::Relaxed);
192 }
193
194 pub fn record_invalidation(&self, source: InvalidationSource) {
196 self.cache_invalidations.fetch_add(1, Ordering::Relaxed);
197 match source {
198 InvalidationSource::WAL => {
199 self.wal_invalidations.fetch_add(1, Ordering::Relaxed);
200 }
201 InvalidationSource::TTL => {
202 self.ttl_invalidations.fetch_add(1, Ordering::Relaxed);
203 }
204 InvalidationSource::Manual => {
205 self.manual_invalidations.fetch_add(1, Ordering::Relaxed);
206 }
207 }
208 }
209
210 pub fn record_latency(&self, duration: Duration) {
212 let us = duration.as_micros() as u64;
213 self.latency_total_us.fetch_add(us, Ordering::Relaxed);
214 self.latency_count.fetch_add(1, Ordering::Relaxed);
215
216 if us < 100 {
217 self.latency_under_100us.fetch_add(1, Ordering::Relaxed);
218 } else if us < 1000 {
219 self.latency_100us_1ms.fetch_add(1, Ordering::Relaxed);
220 } else if us < 10_000 {
221 self.latency_1ms_10ms.fetch_add(1, Ordering::Relaxed);
222 } else if us < 100_000 {
223 self.latency_10ms_100ms.fetch_add(1, Ordering::Relaxed);
224 } else {
225 self.latency_over_100ms.fetch_add(1, Ordering::Relaxed);
226 }
227 }
228
229 pub fn record_workload(&self, workload: WorkloadType) {
231 match workload {
232 WorkloadType::OLTP => {
233 self.oltp_queries.fetch_add(1, Ordering::Relaxed);
234 }
235 WorkloadType::OLAP => {
236 self.olap_queries.fetch_add(1, Ordering::Relaxed);
237 }
238 WorkloadType::Vector => {
239 self.vector_queries.fetch_add(1, Ordering::Relaxed);
240 }
241 WorkloadType::AIAgent => {
242 self.ai_agent_queries.fetch_add(1, Ordering::Relaxed);
243 }
244 WorkloadType::RAG => {
245 self.rag_queries.fetch_add(1, Ordering::Relaxed);
246 }
247 WorkloadType::Mixed => {
248 self.mixed_queries.fetch_add(1, Ordering::Relaxed);
249 }
250 }
251 }
252
253 pub fn update_tier_size(&self, tier: CacheTier, size_bytes: u64, entries: u64) {
255 match tier {
256 CacheTier::L1 => {
257 self.l1_size_bytes.store(size_bytes, Ordering::Relaxed);
258 self.l1_entries.store(entries, Ordering::Relaxed);
259 }
260 CacheTier::L2 => {
261 self.l2_size_bytes.store(size_bytes, Ordering::Relaxed);
262 self.l2_entries.store(entries, Ordering::Relaxed);
263 }
264 CacheTier::L3 => {
265 self.l3_size_bytes.store(size_bytes, Ordering::Relaxed);
266 self.l3_entries.store(entries, Ordering::Relaxed);
267 }
268 }
269 }
270
271 pub fn record_error(&self, error_type: ErrorType) {
273 self.cache_errors.fetch_add(1, Ordering::Relaxed);
274 match error_type {
275 ErrorType::Timeout => {
276 self.timeout_errors.fetch_add(1, Ordering::Relaxed);
277 }
278 ErrorType::Serialization => {
279 self.serialization_errors.fetch_add(1, Ordering::Relaxed);
280 }
281 ErrorType::Other => {}
282 }
283 }
284
285 pub fn uptime(&self) -> Duration {
287 self.start_time.elapsed()
288 }
289
290 pub fn hit_rate(&self) -> f64 {
292 let hits = self.cache_hits.load(Ordering::Relaxed);
293 let misses = self.cache_misses.load(Ordering::Relaxed);
294 let total = hits + misses;
295 if total > 0 {
296 hits as f64 / total as f64
297 } else {
298 0.0
299 }
300 }
301
302 pub fn avg_latency_us(&self) -> f64 {
304 let total = self.latency_total_us.load(Ordering::Relaxed);
305 let count = self.latency_count.load(Ordering::Relaxed);
306 if count > 0 {
307 total as f64 / count as f64
308 } else {
309 0.0
310 }
311 }
312
313 pub fn to_prometheus(&self) -> String {
315 let mut output = String::with_capacity(4096);
316
317 output.push_str(&format!(
319 "# HELP distribcache_uptime_seconds Cache uptime in seconds\n\
320 # TYPE distribcache_uptime_seconds gauge\n\
321 distribcache_uptime_seconds {}\n\n",
322 self.uptime().as_secs()
323 ));
324
325 output.push_str(&format!(
327 "# HELP distribcache_operations_total Total cache operations\n\
328 # TYPE distribcache_operations_total counter\n\
329 distribcache_operations_total{{operation=\"hit\"}} {}\n\
330 distribcache_operations_total{{operation=\"miss\"}} {}\n\
331 distribcache_operations_total{{operation=\"put\"}} {}\n\
332 distribcache_operations_total{{operation=\"eviction\"}} {}\n\
333 distribcache_operations_total{{operation=\"invalidation\"}} {}\n\n",
334 self.cache_hits.load(Ordering::Relaxed),
335 self.cache_misses.load(Ordering::Relaxed),
336 self.cache_puts.load(Ordering::Relaxed),
337 self.cache_evictions.load(Ordering::Relaxed),
338 self.cache_invalidations.load(Ordering::Relaxed),
339 ));
340
341 output.push_str(&format!(
343 "# HELP distribcache_hit_rate Cache hit rate\n\
344 # TYPE distribcache_hit_rate gauge\n\
345 distribcache_hit_rate {:.4}\n\n",
346 self.hit_rate()
347 ));
348
349 output.push_str(&format!(
351 "# HELP distribcache_tier_hits_total Hits per tier\n\
352 # TYPE distribcache_tier_hits_total counter\n\
353 distribcache_tier_hits_total{{tier=\"l1\"}} {}\n\
354 distribcache_tier_hits_total{{tier=\"l2\"}} {}\n\
355 distribcache_tier_hits_total{{tier=\"l3\"}} {}\n\n",
356 self.l1_hits.load(Ordering::Relaxed),
357 self.l2_hits.load(Ordering::Relaxed),
358 self.l3_hits.load(Ordering::Relaxed),
359 ));
360
361 output.push_str(&format!(
362 "# HELP distribcache_tier_size_bytes Size per tier in bytes\n\
363 # TYPE distribcache_tier_size_bytes gauge\n\
364 distribcache_tier_size_bytes{{tier=\"l1\"}} {}\n\
365 distribcache_tier_size_bytes{{tier=\"l2\"}} {}\n\
366 distribcache_tier_size_bytes{{tier=\"l3\"}} {}\n\n",
367 self.l1_size_bytes.load(Ordering::Relaxed),
368 self.l2_size_bytes.load(Ordering::Relaxed),
369 self.l3_size_bytes.load(Ordering::Relaxed),
370 ));
371
372 output.push_str(&format!(
373 "# HELP distribcache_tier_entries Entries per tier\n\
374 # TYPE distribcache_tier_entries gauge\n\
375 distribcache_tier_entries{{tier=\"l1\"}} {}\n\
376 distribcache_tier_entries{{tier=\"l2\"}} {}\n\
377 distribcache_tier_entries{{tier=\"l3\"}} {}\n\n",
378 self.l1_entries.load(Ordering::Relaxed),
379 self.l2_entries.load(Ordering::Relaxed),
380 self.l3_entries.load(Ordering::Relaxed),
381 ));
382
383 output.push_str(&format!(
385 "# HELP distribcache_latency_bucket Latency distribution\n\
386 # TYPE distribcache_latency_bucket histogram\n\
387 distribcache_latency_bucket{{le=\"0.0001\"}} {}\n\
388 distribcache_latency_bucket{{le=\"0.001\"}} {}\n\
389 distribcache_latency_bucket{{le=\"0.01\"}} {}\n\
390 distribcache_latency_bucket{{le=\"0.1\"}} {}\n\
391 distribcache_latency_bucket{{le=\"+Inf\"}} {}\n\n",
392 self.latency_under_100us.load(Ordering::Relaxed),
393 self.latency_under_100us.load(Ordering::Relaxed)
394 + self.latency_100us_1ms.load(Ordering::Relaxed),
395 self.latency_under_100us.load(Ordering::Relaxed)
396 + self.latency_100us_1ms.load(Ordering::Relaxed)
397 + self.latency_1ms_10ms.load(Ordering::Relaxed),
398 self.latency_under_100us.load(Ordering::Relaxed)
399 + self.latency_100us_1ms.load(Ordering::Relaxed)
400 + self.latency_1ms_10ms.load(Ordering::Relaxed)
401 + self.latency_10ms_100ms.load(Ordering::Relaxed),
402 self.latency_count.load(Ordering::Relaxed),
403 ));
404
405 output.push_str(&format!(
406 "# HELP distribcache_latency_avg_us Average latency in microseconds\n\
407 # TYPE distribcache_latency_avg_us gauge\n\
408 distribcache_latency_avg_us {:.2}\n\n",
409 self.avg_latency_us()
410 ));
411
412 output.push_str(&format!(
414 "# HELP distribcache_workload_total Queries by workload type\n\
415 # TYPE distribcache_workload_total counter\n\
416 distribcache_workload_total{{type=\"oltp\"}} {}\n\
417 distribcache_workload_total{{type=\"olap\"}} {}\n\
418 distribcache_workload_total{{type=\"vector\"}} {}\n\
419 distribcache_workload_total{{type=\"ai_agent\"}} {}\n\
420 distribcache_workload_total{{type=\"rag\"}} {}\n\
421 distribcache_workload_total{{type=\"mixed\"}} {}\n\n",
422 self.oltp_queries.load(Ordering::Relaxed),
423 self.olap_queries.load(Ordering::Relaxed),
424 self.vector_queries.load(Ordering::Relaxed),
425 self.ai_agent_queries.load(Ordering::Relaxed),
426 self.rag_queries.load(Ordering::Relaxed),
427 self.mixed_queries.load(Ordering::Relaxed),
428 ));
429
430 output.push_str(&format!(
432 "# HELP distribcache_ai_cache_hits AI cache hits\n\
433 # TYPE distribcache_ai_cache_hits counter\n\
434 distribcache_ai_cache_hits{{cache=\"conversation\"}} {}\n\
435 distribcache_ai_cache_hits{{cache=\"rag\"}} {}\n\
436 distribcache_ai_cache_hits{{cache=\"tool\"}} {}\n\
437 distribcache_ai_cache_hits{{cache=\"semantic\"}} {}\n\n",
438 self.conversation_cache_hits.load(Ordering::Relaxed),
439 self.rag_cache_hits.load(Ordering::Relaxed),
440 self.tool_cache_hits.load(Ordering::Relaxed),
441 self.semantic_cache_hits.load(Ordering::Relaxed),
442 ));
443
444 output.push_str(&format!(
446 "# HELP distribcache_invalidations_total Invalidations by source\n\
447 # TYPE distribcache_invalidations_total counter\n\
448 distribcache_invalidations_total{{source=\"wal\"}} {}\n\
449 distribcache_invalidations_total{{source=\"ttl\"}} {}\n\
450 distribcache_invalidations_total{{source=\"manual\"}} {}\n\n",
451 self.wal_invalidations.load(Ordering::Relaxed),
452 self.ttl_invalidations.load(Ordering::Relaxed),
453 self.manual_invalidations.load(Ordering::Relaxed),
454 ));
455
456 output.push_str(&format!(
458 "# HELP distribcache_errors_total Cache errors\n\
459 # TYPE distribcache_errors_total counter\n\
460 distribcache_errors_total{{type=\"timeout\"}} {}\n\
461 distribcache_errors_total{{type=\"serialization\"}} {}\n\
462 distribcache_errors_total{{type=\"total\"}} {}\n",
463 self.timeout_errors.load(Ordering::Relaxed),
464 self.serialization_errors.load(Ordering::Relaxed),
465 self.cache_errors.load(Ordering::Relaxed),
466 ));
467
468 output
469 }
470
471 pub fn to_json(&self) -> serde_json::Value {
473 serde_json::json!({
474 "uptime_secs": self.uptime().as_secs(),
475 "operations": {
476 "hits": self.cache_hits.load(Ordering::Relaxed),
477 "misses": self.cache_misses.load(Ordering::Relaxed),
478 "puts": self.cache_puts.load(Ordering::Relaxed),
479 "evictions": self.cache_evictions.load(Ordering::Relaxed),
480 "invalidations": self.cache_invalidations.load(Ordering::Relaxed),
481 },
482 "hit_rate": self.hit_rate(),
483 "tiers": {
484 "l1": {
485 "hits": self.l1_hits.load(Ordering::Relaxed),
486 "misses": self.l1_misses.load(Ordering::Relaxed),
487 "size_bytes": self.l1_size_bytes.load(Ordering::Relaxed),
488 "entries": self.l1_entries.load(Ordering::Relaxed),
489 },
490 "l2": {
491 "hits": self.l2_hits.load(Ordering::Relaxed),
492 "misses": self.l2_misses.load(Ordering::Relaxed),
493 "size_bytes": self.l2_size_bytes.load(Ordering::Relaxed),
494 "entries": self.l2_entries.load(Ordering::Relaxed),
495 },
496 "l3": {
497 "hits": self.l3_hits.load(Ordering::Relaxed),
498 "misses": self.l3_misses.load(Ordering::Relaxed),
499 "size_bytes": self.l3_size_bytes.load(Ordering::Relaxed),
500 "entries": self.l3_entries.load(Ordering::Relaxed),
501 },
502 },
503 "latency": {
504 "avg_us": self.avg_latency_us(),
505 "buckets": {
506 "under_100us": self.latency_under_100us.load(Ordering::Relaxed),
507 "100us_1ms": self.latency_100us_1ms.load(Ordering::Relaxed),
508 "1ms_10ms": self.latency_1ms_10ms.load(Ordering::Relaxed),
509 "10ms_100ms": self.latency_10ms_100ms.load(Ordering::Relaxed),
510 "over_100ms": self.latency_over_100ms.load(Ordering::Relaxed),
511 },
512 },
513 "workloads": {
514 "oltp": self.oltp_queries.load(Ordering::Relaxed),
515 "olap": self.olap_queries.load(Ordering::Relaxed),
516 "vector": self.vector_queries.load(Ordering::Relaxed),
517 "ai_agent": self.ai_agent_queries.load(Ordering::Relaxed),
518 "rag": self.rag_queries.load(Ordering::Relaxed),
519 "mixed": self.mixed_queries.load(Ordering::Relaxed),
520 },
521 "ai_caches": {
522 "conversation": {
523 "hits": self.conversation_cache_hits.load(Ordering::Relaxed),
524 "misses": self.conversation_cache_misses.load(Ordering::Relaxed),
525 },
526 "rag": {
527 "hits": self.rag_cache_hits.load(Ordering::Relaxed),
528 "misses": self.rag_cache_misses.load(Ordering::Relaxed),
529 },
530 "tool": {
531 "hits": self.tool_cache_hits.load(Ordering::Relaxed),
532 "misses": self.tool_cache_misses.load(Ordering::Relaxed),
533 },
534 "semantic": {
535 "hits": self.semantic_cache_hits.load(Ordering::Relaxed),
536 "misses": self.semantic_cache_misses.load(Ordering::Relaxed),
537 },
538 },
539 "errors": {
540 "total": self.cache_errors.load(Ordering::Relaxed),
541 "timeout": self.timeout_errors.load(Ordering::Relaxed),
542 "serialization": self.serialization_errors.load(Ordering::Relaxed),
543 },
544 })
545 }
546
547 pub fn reset(&self) {
549 self.cache_hits.store(0, Ordering::Relaxed);
550 self.cache_misses.store(0, Ordering::Relaxed);
551 self.cache_puts.store(0, Ordering::Relaxed);
552 self.cache_evictions.store(0, Ordering::Relaxed);
553 self.cache_invalidations.store(0, Ordering::Relaxed);
554
555 self.l1_hits.store(0, Ordering::Relaxed);
556 self.l1_misses.store(0, Ordering::Relaxed);
557 self.l2_hits.store(0, Ordering::Relaxed);
558 self.l2_misses.store(0, Ordering::Relaxed);
559 self.l3_hits.store(0, Ordering::Relaxed);
560 self.l3_misses.store(0, Ordering::Relaxed);
561
562 self.latency_under_100us.store(0, Ordering::Relaxed);
563 self.latency_100us_1ms.store(0, Ordering::Relaxed);
564 self.latency_1ms_10ms.store(0, Ordering::Relaxed);
565 self.latency_10ms_100ms.store(0, Ordering::Relaxed);
566 self.latency_over_100ms.store(0, Ordering::Relaxed);
567 self.latency_total_us.store(0, Ordering::Relaxed);
568 self.latency_count.store(0, Ordering::Relaxed);
569
570 self.oltp_queries.store(0, Ordering::Relaxed);
571 self.olap_queries.store(0, Ordering::Relaxed);
572 self.vector_queries.store(0, Ordering::Relaxed);
573 self.ai_agent_queries.store(0, Ordering::Relaxed);
574 self.rag_queries.store(0, Ordering::Relaxed);
575 self.mixed_queries.store(0, Ordering::Relaxed);
576
577 self.cache_errors.store(0, Ordering::Relaxed);
578 self.timeout_errors.store(0, Ordering::Relaxed);
579 self.serialization_errors.store(0, Ordering::Relaxed);
580 }
581}
582
583#[derive(Debug, Clone, Copy, PartialEq, Eq)]
587pub enum InvalidationSource {
588 WAL,
589 TTL,
590 Manual,
591}
592
593#[derive(Debug, Clone, Copy, PartialEq, Eq)]
597pub enum ErrorType {
598 Timeout,
599 Serialization,
600 Other,
601}
602
603#[cfg(test)]
604mod tests {
605 use super::*;
606
607 #[test]
608 fn test_metrics_creation() {
609 let metrics = DistribCacheMetrics::new();
610 assert_eq!(metrics.cache_hits.load(Ordering::Relaxed), 0);
611 assert_eq!(metrics.cache_misses.load(Ordering::Relaxed), 0);
612 }
613
614 #[test]
615 fn test_record_hit() {
616 let metrics = DistribCacheMetrics::new();
617
618 metrics.record_hit(CacheTier::L1);
619 metrics.record_hit(CacheTier::L2);
620 metrics.record_hit(CacheTier::L1);
621
622 assert_eq!(metrics.cache_hits.load(Ordering::Relaxed), 3);
623 assert_eq!(metrics.l1_hits.load(Ordering::Relaxed), 2);
624 assert_eq!(metrics.l2_hits.load(Ordering::Relaxed), 1);
625 }
626
627 #[test]
628 fn test_hit_rate() {
629 let metrics = DistribCacheMetrics::new();
630
631 metrics.record_hit(CacheTier::L1);
632 metrics.record_hit(CacheTier::L1);
633 metrics.record_miss(CacheTier::L1);
634 metrics.record_miss(CacheTier::L1);
635
636 assert!((metrics.hit_rate() - 0.5).abs() < 0.001);
637 }
638
639 #[test]
640 fn test_record_latency() {
641 let metrics = DistribCacheMetrics::new();
642
643 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);
648 assert_eq!(metrics.latency_100us_1ms.load(Ordering::Relaxed), 1);
649 assert_eq!(metrics.latency_1ms_10ms.load(Ordering::Relaxed), 1);
650 assert_eq!(metrics.latency_count.load(Ordering::Relaxed), 3);
651 }
652
653 #[test]
654 fn test_prometheus_export() {
655 let metrics = DistribCacheMetrics::new();
656
657 metrics.record_hit(CacheTier::L1);
658 metrics.record_miss(CacheTier::L2);
659 metrics.record_workload(WorkloadType::OLTP);
660
661 let prometheus = metrics.to_prometheus();
662
663 assert!(prometheus.contains("distribcache_operations_total"));
664 assert!(prometheus.contains("distribcache_hit_rate"));
665 assert!(prometheus.contains("distribcache_tier_hits_total"));
666 }
667
668 #[test]
669 fn test_json_export() {
670 let metrics = DistribCacheMetrics::new();
671
672 metrics.record_hit(CacheTier::L1);
673 metrics.record_put();
674
675 let json = metrics.to_json();
676
677 assert_eq!(json["operations"]["hits"], 1);
678 assert_eq!(json["operations"]["puts"], 1);
679 }
680
681 #[test]
682 fn test_reset() {
683 let metrics = DistribCacheMetrics::new();
684
685 metrics.record_hit(CacheTier::L1);
686 metrics.record_miss(CacheTier::L2);
687 metrics.record_put();
688
689 metrics.reset();
690
691 assert_eq!(metrics.cache_hits.load(Ordering::Relaxed), 0);
692 assert_eq!(metrics.cache_misses.load(Ordering::Relaxed), 0);
693 assert_eq!(metrics.cache_puts.load(Ordering::Relaxed), 0);
694 }
695
696 #[test]
697 fn test_workload_tracking() {
698 let metrics = DistribCacheMetrics::new();
699
700 metrics.record_workload(WorkloadType::OLTP);
701 metrics.record_workload(WorkloadType::OLTP);
702 metrics.record_workload(WorkloadType::OLAP);
703 metrics.record_workload(WorkloadType::AIAgent);
704
705 assert_eq!(metrics.oltp_queries.load(Ordering::Relaxed), 2);
706 assert_eq!(metrics.olap_queries.load(Ordering::Relaxed), 1);
707 assert_eq!(metrics.ai_agent_queries.load(Ordering::Relaxed), 1);
708 }
709
710 #[test]
711 fn test_error_tracking() {
712 let metrics = DistribCacheMetrics::new();
713
714 metrics.record_error(ErrorType::Timeout);
715 metrics.record_error(ErrorType::Timeout);
716 metrics.record_error(ErrorType::Serialization);
717
718 assert_eq!(metrics.cache_errors.load(Ordering::Relaxed), 3);
719 assert_eq!(metrics.timeout_errors.load(Ordering::Relaxed), 2);
720 assert_eq!(metrics.serialization_errors.load(Ordering::Relaxed), 1);
721 }
722}