1use anyhow::Result;
4use chrono::{DateTime, Utc};
5use parking_lot::RwLock;
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::sync::Arc;
9use std::time::{Duration, Instant};
10use sysinfo::System;
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct OperationMetrics {
15 pub operation_name: String,
16 pub duration: Duration,
17 pub timestamp: DateTime<Utc>,
18 pub success: bool,
19 pub error_message: Option<String>,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct PerformanceStats {
25 pub operation_name: String,
26 pub total_calls: u64,
27 pub successful_calls: u64,
28 pub failed_calls: u64,
29 pub total_duration: Duration,
30 pub average_duration: Duration,
31 pub min_duration: Duration,
32 pub max_duration: Duration,
33 pub success_rate: f64,
34 pub last_called: Option<DateTime<Utc>>,
35}
36
37impl PerformanceStats {
38 #[must_use]
39 pub const fn new(operation_name: String) -> Self {
40 Self {
41 operation_name,
42 total_calls: 0,
43 successful_calls: 0,
44 failed_calls: 0,
45 total_duration: Duration::ZERO,
46 average_duration: Duration::ZERO,
47 min_duration: Duration::MAX,
48 max_duration: Duration::ZERO,
49 success_rate: 0.0,
50 last_called: None,
51 }
52 }
53
54 pub fn add_metric(&mut self, metric: &OperationMetrics) {
55 self.total_calls += 1;
56 self.total_duration += metric.duration;
57 self.last_called = Some(metric.timestamp);
58
59 if metric.success {
60 self.successful_calls += 1;
61 } else {
62 self.failed_calls += 1;
63 }
64
65 if metric.duration < self.min_duration {
66 self.min_duration = metric.duration;
67 }
68 if metric.duration > self.max_duration {
69 self.max_duration = metric.duration;
70 }
71
72 self.average_duration = Duration::from_nanos(
73 u64::try_from(self.total_duration.as_nanos()).unwrap_or(u64::MAX) / self.total_calls,
74 );
75
76 self.success_rate = if self.total_calls > 0 {
77 #[allow(clippy::cast_precision_loss)]
78 {
79 self.successful_calls as f64 / self.total_calls as f64
80 }
81 } else {
82 0.0
83 };
84 }
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct SystemMetrics {
90 pub timestamp: DateTime<Utc>,
91 pub memory_usage_mb: f64,
92 pub cpu_usage_percent: f64,
93 pub available_memory_mb: f64,
94 pub total_memory_mb: f64,
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct CacheMetrics {
100 pub cache_type: String, pub hits: u64,
102 pub misses: u64,
103 pub hit_rate: f64,
104 pub total_entries: u64,
105 pub memory_usage_bytes: u64,
106 pub evictions: u64,
107 pub insertions: u64,
108 pub invalidations: u64,
109 pub warming_entries: u64,
110 pub average_access_time_ms: f64,
111 pub last_accessed: Option<DateTime<Utc>>,
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize)]
116pub struct QueryMetrics {
117 pub query_type: String, pub total_queries: u64,
119 pub cached_queries: u64,
120 pub database_queries: u64,
121 pub cache_hit_rate: f64,
122 pub average_query_time_ms: f64,
123 pub average_cache_time_ms: f64,
124 pub average_database_time_ms: f64,
125 pub slowest_query_ms: u64,
126 pub fastest_query_ms: u64,
127 pub query_size_bytes: u64,
128 pub compression_ratio: f64,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct ComprehensivePerformanceSummary {
134 pub timestamp: DateTime<Utc>,
135 pub operation_stats: HashMap<String, PerformanceStats>,
136 pub system_metrics: SystemMetrics,
137 pub cache_metrics: HashMap<String, CacheMetrics>,
138 pub query_metrics: HashMap<String, QueryMetrics>,
139 pub overall_health_score: f64,
140}
141
142pub struct PerformanceMonitor {
144 metrics: Arc<RwLock<Vec<OperationMetrics>>>,
146 stats: Arc<RwLock<HashMap<String, PerformanceStats>>>,
148 cache_metrics: Arc<RwLock<HashMap<String, CacheMetrics>>>,
150 query_metrics: Arc<RwLock<HashMap<String, QueryMetrics>>>,
152 system: Arc<RwLock<System>>,
154 max_metrics: usize,
156}
157
158impl PerformanceMonitor {
159 #[must_use]
161 pub fn new(max_metrics: usize) -> Self {
162 Self {
163 metrics: Arc::new(RwLock::new(Vec::new())),
164 stats: Arc::new(RwLock::new(HashMap::new())),
165 cache_metrics: Arc::new(RwLock::new(HashMap::new())),
166 query_metrics: Arc::new(RwLock::new(HashMap::new())),
167 system: Arc::new(RwLock::new(System::new_all())),
168 max_metrics,
169 }
170 }
171
172 #[must_use]
174 pub fn new_default() -> Self {
175 Self::new(10000) }
177
178 #[must_use]
180 pub fn start_operation(&self, operation_name: &str) -> OperationTimer {
181 OperationTimer {
182 monitor: self.clone(),
183 operation_name: operation_name.to_string(),
184 start_time: Instant::now(),
185 }
186 }
187
188 pub fn record_operation(&self, metric: &OperationMetrics) {
190 {
192 let mut metrics = self.metrics.write();
193 metrics.push(metric.clone());
194
195 if metrics.len() > self.max_metrics {
197 let excess = metrics.len() - self.max_metrics;
198 metrics.drain(0..excess);
199 }
200 }
201
202 let operation_name = metric.operation_name.clone();
204 let mut stats = self.stats.write();
205 let operation_stats = stats
206 .entry(operation_name)
207 .or_insert_with(|| PerformanceStats::new(metric.operation_name.clone()));
208 operation_stats.add_metric(metric);
209 drop(stats);
210 }
211
212 #[must_use]
214 pub fn get_metrics(&self) -> Vec<OperationMetrics> {
215 self.metrics.read().clone()
216 }
217
218 #[must_use]
220 pub fn get_all_stats(&self) -> HashMap<String, PerformanceStats> {
221 self.stats.read().clone()
222 }
223
224 #[must_use]
226 pub fn get_operation_stats(&self, operation_name: &str) -> Option<PerformanceStats> {
227 self.stats.read().get(operation_name).cloned()
228 }
229
230 pub fn get_system_metrics(&self) -> Result<SystemMetrics> {
237 let mut system = self.system.write();
238 system.refresh_all();
239
240 Ok(SystemMetrics {
241 timestamp: Utc::now(),
242 #[allow(clippy::cast_precision_loss)]
243 memory_usage_mb: system.used_memory() as f64 / 1024.0 / 1024.0,
244 cpu_usage_percent: {
245 let cpu_count = system.cpus().len();
246 #[allow(clippy::cast_precision_loss)]
247 let cpu_usage: f64 = system
248 .cpus()
249 .iter()
250 .map(|cpu| f64::from(cpu.cpu_usage()))
251 .sum::<f64>()
252 / cpu_count as f64;
253 cpu_usage
254 },
255 #[allow(clippy::cast_precision_loss)]
256 available_memory_mb: {
257 let avail = system.available_memory();
260 let bytes = if avail > 0 {
261 avail
262 } else {
263 system.total_memory().saturating_sub(system.used_memory())
264 };
265 bytes as f64 / 1024.0 / 1024.0
266 },
267 #[allow(clippy::cast_precision_loss)]
268 total_memory_mb: system.total_memory() as f64 / 1024.0 / 1024.0,
269 })
270 }
271
272 pub fn clear(&self) {
274 self.metrics.write().clear();
275 self.stats.write().clear();
276 }
277
278 #[must_use]
280 pub fn get_summary(&self) -> PerformanceSummary {
281 let stats = self.get_all_stats();
282 let total_operations: u64 = stats.values().map(|s| s.total_calls).sum();
283 let total_successful: u64 = stats.values().map(|s| s.successful_calls).sum();
284 let total_duration: Duration = stats.values().map(|s| s.total_duration).sum();
285
286 PerformanceSummary {
287 total_operations,
288 total_successful,
289 total_failed: total_operations - total_successful,
290 overall_success_rate: if total_operations > 0 {
291 #[allow(clippy::cast_precision_loss)]
292 {
293 total_successful as f64 / total_operations as f64
294 }
295 } else {
296 0.0
297 },
298 total_duration,
299 average_operation_duration: u64::try_from(total_duration.as_nanos())
300 .unwrap_or(0)
301 .checked_div(total_operations)
302 .map_or(Duration::ZERO, Duration::from_nanos),
303 operation_count: stats.len(),
304 }
305 }
306
307 pub fn record_cache_metrics(&self, cache_type: &str, metrics: CacheMetrics) {
309 let mut cache_metrics = self.cache_metrics.write();
310 cache_metrics.insert(cache_type.to_string(), metrics);
311 }
312
313 #[must_use]
315 pub fn get_cache_metrics(&self, cache_type: &str) -> Option<CacheMetrics> {
316 let cache_metrics = self.cache_metrics.read();
317 cache_metrics.get(cache_type).cloned()
318 }
319
320 #[must_use]
322 pub fn get_all_cache_metrics(&self) -> HashMap<String, CacheMetrics> {
323 let cache_metrics = self.cache_metrics.read();
324 cache_metrics.clone()
325 }
326
327 pub fn record_query_metrics(&self, query_type: &str, metrics: QueryMetrics) {
329 let mut query_metrics = self.query_metrics.write();
330 query_metrics.insert(query_type.to_string(), metrics);
331 }
332
333 #[must_use]
335 pub fn get_query_metrics(&self, query_type: &str) -> Option<QueryMetrics> {
336 let query_metrics = self.query_metrics.read();
337 query_metrics.get(query_type).cloned()
338 }
339
340 #[must_use]
342 pub fn get_all_query_metrics(&self) -> HashMap<String, QueryMetrics> {
343 let query_metrics = self.query_metrics.read();
344 query_metrics.clone()
345 }
346
347 #[must_use]
349 pub fn get_comprehensive_summary(&self) -> ComprehensivePerformanceSummary {
350 let operation_stats = self.get_all_stats();
351 let system_metrics = self.get_system_metrics().unwrap_or_else(|_| SystemMetrics {
352 timestamp: Utc::now(),
353 memory_usage_mb: 0.0,
354 cpu_usage_percent: 0.0,
355 available_memory_mb: 0.0,
356 total_memory_mb: 0.0,
357 });
358 let cache_metrics = self.get_all_cache_metrics();
359 let query_metrics = self.get_all_query_metrics();
360
361 let health_score = Self::calculate_comprehensive_health_score(
363 &operation_stats,
364 &cache_metrics,
365 &query_metrics,
366 );
367
368 ComprehensivePerformanceSummary {
369 timestamp: Utc::now(),
370 operation_stats,
371 system_metrics,
372 cache_metrics,
373 query_metrics,
374 overall_health_score: health_score,
375 }
376 }
377
378 #[allow(clippy::cast_precision_loss)]
380 fn calculate_comprehensive_health_score(
381 operation_stats: &HashMap<String, PerformanceStats>,
382 cache_metrics: &HashMap<String, CacheMetrics>,
383 query_metrics: &HashMap<String, QueryMetrics>,
384 ) -> f64 {
385 let mut total_score = 0.0;
386 let mut weight_sum = 0.0;
387
388 if !operation_stats.is_empty() {
390 let avg_success_rate = operation_stats
391 .values()
392 .map(|s| s.success_rate)
393 .sum::<f64>()
394 / operation_stats.len() as f64;
395 let avg_response_time = operation_stats
396 .values()
397 .map(|s| s.average_duration.as_millis() as f64)
398 .sum::<f64>()
399 / operation_stats.len() as f64;
400
401 let operation_score =
402 (avg_success_rate * 70.0) + ((1000.0 - avg_response_time.min(1000.0)) * 0.3);
403 total_score += operation_score * 0.4;
404 weight_sum += 0.4;
405 }
406
407 if !cache_metrics.is_empty() {
409 let avg_hit_rate = cache_metrics.values().map(|c| c.hit_rate).sum::<f64>()
410 / cache_metrics.len() as f64;
411 let avg_access_time = cache_metrics
412 .values()
413 .map(|c| c.average_access_time_ms)
414 .sum::<f64>()
415 / cache_metrics.len() as f64;
416
417 let cache_score = (avg_hit_rate * 60.0) + ((100.0 - avg_access_time.min(100.0)) * 0.4);
418 total_score += cache_score * 0.35;
419 weight_sum += 0.35;
420 }
421
422 if !query_metrics.is_empty() {
424 let avg_query_hit_rate = query_metrics
425 .values()
426 .map(|q| q.cache_hit_rate)
427 .sum::<f64>()
428 / query_metrics.len() as f64;
429 let avg_query_time = query_metrics
430 .values()
431 .map(|q| q.average_query_time_ms)
432 .sum::<f64>()
433 / query_metrics.len() as f64;
434
435 let query_score =
436 (avg_query_hit_rate * 50.0) + ((1000.0 - avg_query_time.min(1000.0)) * 0.5);
437 total_score += query_score * 0.25;
438 weight_sum += 0.25;
439 }
440
441 if weight_sum > 0.0 {
442 (total_score / weight_sum).clamp(0.0, 100.0)
443 } else {
444 100.0
445 }
446 }
447}
448
449impl Clone for PerformanceMonitor {
450 fn clone(&self) -> Self {
451 Self {
452 metrics: Arc::clone(&self.metrics),
453 stats: Arc::clone(&self.stats),
454 cache_metrics: Arc::clone(&self.cache_metrics),
455 query_metrics: Arc::clone(&self.query_metrics),
456 system: Arc::clone(&self.system),
457 max_metrics: self.max_metrics,
458 }
459 }
460}
461
462pub struct OperationTimer {
464 monitor: PerformanceMonitor,
465 operation_name: String,
466 start_time: Instant,
467}
468
469impl OperationTimer {
470 pub fn success(self) {
472 let duration = self.start_time.elapsed();
473 let metric = OperationMetrics {
474 operation_name: self.operation_name,
475 duration,
476 timestamp: Utc::now(),
477 success: true,
478 error_message: None,
479 };
480 self.monitor.record_operation(&metric);
481 }
482
483 pub fn error(self, error_message: String) {
485 let duration = self.start_time.elapsed();
486 let metric = OperationMetrics {
487 operation_name: self.operation_name,
488 duration,
489 timestamp: Utc::now(),
490 success: false,
491 error_message: Some(error_message),
492 };
493 self.monitor.record_operation(&metric);
494 }
495}
496
497#[derive(Debug, Clone, Serialize, Deserialize)]
499pub struct PerformanceSummary {
500 pub total_operations: u64,
501 pub total_successful: u64,
502 pub total_failed: u64,
503 pub overall_success_rate: f64,
504 pub total_duration: Duration,
505 pub average_operation_duration: Duration,
506 pub operation_count: usize,
507}
508
509#[cfg(test)]
510mod tests {
511 use super::*;
512 use std::thread;
513
514 #[test]
515 fn test_performance_monitor() {
516 let monitor = PerformanceMonitor::new_default();
517
518 let metric1 = OperationMetrics {
520 operation_name: "test_op".to_string(),
521 duration: Duration::from_millis(100),
522 timestamp: Utc::now(),
523 success: true,
524 error_message: None,
525 };
526
527 monitor.record_operation(&metric1);
528
529 let stats = monitor.get_operation_stats("test_op");
530 assert!(stats.is_some());
531 let stats = stats.unwrap();
532 assert_eq!(stats.total_calls, 1);
533 assert_eq!(stats.successful_calls, 1);
534 assert_eq!(stats.failed_calls, 0);
535 }
536
537 #[test]
538 fn test_operation_timer() {
539 let monitor = PerformanceMonitor::new_default();
540
541 let timer = monitor.start_operation("test_timer");
543 thread::sleep(Duration::from_millis(10));
544 timer.success();
545
546 let stats = monitor.get_operation_stats("test_timer");
547 assert!(stats.is_some());
548 let stats = stats.unwrap();
549 assert_eq!(stats.total_calls, 1);
550 assert!(stats.successful_calls > 0);
551 }
552
553 #[test]
554 fn test_performance_monitor_failed_operation() {
555 let monitor = PerformanceMonitor::new_default();
556
557 let metric = OperationMetrics {
559 operation_name: "failed_op".to_string(),
560 duration: Duration::from_millis(50),
561 timestamp: Utc::now(),
562 success: false,
563 error_message: Some("Test error".to_string()),
564 };
565
566 monitor.record_operation(&metric);
567
568 let stats = monitor.get_operation_stats("failed_op");
569 assert!(stats.is_some());
570 let stats = stats.unwrap();
571 assert_eq!(stats.total_calls, 1);
572 assert_eq!(stats.successful_calls, 0);
573 assert_eq!(stats.failed_calls, 1);
574 }
575
576 #[test]
577 fn test_performance_monitor_multiple_operations() {
578 let monitor = PerformanceMonitor::new_default();
579
580 for i in 0..5 {
582 let metric = OperationMetrics {
583 operation_name: "multi_op".to_string(),
584 duration: Duration::from_millis(i * 10),
585 timestamp: Utc::now(),
586 success: i % 2 == 0,
587 error_message: if i % 2 == 0 {
588 None
589 } else {
590 Some("Error".to_string())
591 },
592 };
593 monitor.record_operation(&metric);
594 }
595
596 let stats = monitor.get_operation_stats("multi_op");
597 assert!(stats.is_some());
598 let stats = stats.unwrap();
599 assert_eq!(stats.total_calls, 5);
600 assert_eq!(stats.successful_calls, 3);
601 assert_eq!(stats.failed_calls, 2);
602 }
603
604 #[test]
605 fn test_performance_monitor_get_all_stats() {
606 let monitor = PerformanceMonitor::new_default();
607
608 let operations = vec![("op1", true), ("op1", false), ("op2", true), ("op2", true)];
610
611 for (name, success) in operations {
612 let metric = OperationMetrics {
613 operation_name: name.to_string(),
614 duration: Duration::from_millis(100),
615 timestamp: Utc::now(),
616 success,
617 error_message: if success {
618 None
619 } else {
620 Some("Error".to_string())
621 },
622 };
623 monitor.record_operation(&metric);
624 }
625
626 let all_stats = monitor.get_all_stats();
627 assert_eq!(all_stats.len(), 2);
628 assert!(all_stats.contains_key("op1"));
629 assert!(all_stats.contains_key("op2"));
630
631 let op1_stats = &all_stats["op1"];
632 assert_eq!(op1_stats.total_calls, 2);
633 assert_eq!(op1_stats.successful_calls, 1);
634 assert_eq!(op1_stats.failed_calls, 1);
635
636 let op2_stats = &all_stats["op2"];
637 assert_eq!(op2_stats.total_calls, 2);
638 assert_eq!(op2_stats.successful_calls, 2);
639 assert_eq!(op2_stats.failed_calls, 0);
640 }
641
642 #[test]
643 fn test_performance_monitor_get_summary() {
644 let monitor = PerformanceMonitor::new_default();
645
646 let operations = vec![("op1", true, 100), ("op1", false, 200), ("op2", true, 150)];
648
649 for (name, success, duration_ms) in operations {
650 let metric = OperationMetrics {
651 operation_name: name.to_string(),
652 duration: Duration::from_millis(duration_ms),
653 timestamp: Utc::now(),
654 success,
655 error_message: if success {
656 None
657 } else {
658 Some("Error".to_string())
659 },
660 };
661 monitor.record_operation(&metric);
662 }
663
664 let summary = monitor.get_summary();
665 assert_eq!(summary.total_operations, 3);
666 assert_eq!(summary.total_successful, 2);
667 assert_eq!(summary.total_failed, 1);
668 assert!((summary.overall_success_rate - 2.0 / 3.0).abs() < 0.001);
669 assert_eq!(summary.operation_count, 2);
670 }
671
672 #[test]
673 fn test_performance_monitor_get_summary_empty() {
674 let monitor = PerformanceMonitor::new_default();
675 let summary = monitor.get_summary();
676
677 assert_eq!(summary.total_operations, 0);
678 assert_eq!(summary.total_successful, 0);
679 assert_eq!(summary.total_failed, 0);
680 assert!((summary.overall_success_rate - 0.0).abs() < f64::EPSILON);
681 assert_eq!(summary.operation_count, 0);
682 }
683
684 #[test]
685 fn test_operation_timer_failure() {
686 let monitor = PerformanceMonitor::new_default();
687
688 let metric = OperationMetrics {
690 operation_name: "test_failure".to_string(),
691 duration: Duration::from_millis(5),
692 timestamp: Utc::now(),
693 success: false,
694 error_message: Some("Test failure".to_string()),
695 };
696 monitor.record_operation(&metric);
697
698 let stats = monitor.get_operation_stats("test_failure");
699 assert!(stats.is_some());
700 let stats = stats.unwrap();
701 assert_eq!(stats.total_calls, 1);
702 assert_eq!(stats.successful_calls, 0);
703 assert_eq!(stats.failed_calls, 1);
704 }
705
706 #[test]
707 fn test_operation_timer_drop() {
708 let monitor = PerformanceMonitor::new_default();
709
710 {
712 let timer = monitor.start_operation("test_drop");
713 thread::sleep(Duration::from_millis(5));
714 timer.success();
716 }
717
718 let stats = monitor.get_operation_stats("test_drop");
719 assert!(stats.is_some());
720 let stats = stats.unwrap();
721 assert_eq!(stats.total_calls, 1);
722 assert_eq!(stats.successful_calls, 1);
723 assert_eq!(stats.failed_calls, 0);
724 }
725
726 #[test]
727 fn test_performance_monitor_clone() {
728 let monitor1 = PerformanceMonitor::new_default();
729
730 let metric = OperationMetrics {
732 operation_name: "clone_test".to_string(),
733 duration: Duration::from_millis(100),
734 timestamp: Utc::now(),
735 success: true,
736 error_message: None,
737 };
738 monitor1.record_operation(&metric);
739
740 let monitor2 = monitor1.clone();
742
743 let stats1 = monitor1.get_operation_stats("clone_test");
745 let stats2 = monitor2.get_operation_stats("clone_test");
746
747 assert!(stats1.is_some());
748 assert!(stats2.is_some());
749 assert_eq!(stats1.unwrap().total_calls, stats2.unwrap().total_calls);
750 }
751
752 #[test]
753 fn test_performance_monitor_additional_operations() {
754 let monitor = PerformanceMonitor::new_default();
755
756 let operations = vec![
758 ("op1", Duration::from_millis(100), true),
759 ("op1", Duration::from_millis(150), true),
760 ("op1", Duration::from_millis(200), false),
761 ("op2", Duration::from_millis(50), true),
762 ("op2", Duration::from_millis(75), true),
763 ];
764
765 for (op_name, duration, success) in operations {
766 let metric = OperationMetrics {
767 operation_name: op_name.to_string(),
768 duration,
769 timestamp: Utc::now(),
770 success,
771 error_message: if success {
772 None
773 } else {
774 Some("Test error".to_string())
775 },
776 };
777 monitor.record_operation(&metric);
778 }
779
780 let op1_stats = monitor.get_operation_stats("op1").unwrap();
782 assert_eq!(op1_stats.total_calls, 3);
783 assert_eq!(op1_stats.successful_calls, 2);
784 assert_eq!(op1_stats.failed_calls, 1);
785
786 let op2_stats = monitor.get_operation_stats("op2").unwrap();
788 assert_eq!(op2_stats.total_calls, 2);
789 assert_eq!(op2_stats.successful_calls, 2);
790 assert_eq!(op2_stats.failed_calls, 0);
791 }
792
793 #[test]
794 fn test_performance_monitor_get_metrics() {
795 let monitor = PerformanceMonitor::new_default();
796
797 let metric1 = OperationMetrics {
799 operation_name: "test_op".to_string(),
800 duration: Duration::from_millis(100),
801 timestamp: Utc::now(),
802 success: true,
803 error_message: None,
804 };
805 monitor.record_operation(&metric1);
806
807 let metric2 = OperationMetrics {
808 operation_name: "test_op2".to_string(),
809 duration: Duration::from_millis(200),
810 timestamp: Utc::now(),
811 success: false,
812 error_message: Some("Test error".to_string()),
813 };
814 monitor.record_operation(&metric2);
815
816 let all_metrics = monitor.get_metrics();
817 assert_eq!(all_metrics.len(), 2);
818 assert!(all_metrics.iter().any(|m| m.operation_name == "test_op"));
819 assert!(all_metrics.iter().any(|m| m.operation_name == "test_op2"));
820 }
821
822 #[test]
823 fn test_performance_stats_new_initialization() {
824 let stats = PerformanceStats::new("test_operation".to_string());
825 assert_eq!(stats.operation_name, "test_operation");
826 assert_eq!(stats.total_calls, 0);
827 assert_eq!(stats.successful_calls, 0);
828 assert_eq!(stats.failed_calls, 0);
829 assert!((stats.success_rate - 0.0).abs() < f64::EPSILON);
830 assert_eq!(stats.average_duration, Duration::from_millis(0));
831 assert_eq!(stats.min_duration, Duration::MAX);
832 assert_eq!(stats.max_duration, Duration::ZERO);
833 assert!(stats.last_called.is_none());
834 }
835
836 #[test]
837 fn test_performance_stats_update_single_success() {
838 let mut stats = PerformanceStats::new("test_op".to_string());
839
840 let metric = OperationMetrics {
841 operation_name: "test_op".to_string(),
842 duration: Duration::from_millis(100),
843 timestamp: Utc::now(),
844 success: true,
845 error_message: None,
846 };
847
848 stats.add_metric(&metric);
849
850 assert_eq!(stats.total_calls, 1);
851 assert_eq!(stats.successful_calls, 1);
852 assert_eq!(stats.failed_calls, 0);
853 assert!((stats.success_rate - 1.0).abs() < f64::EPSILON);
854 assert_eq!(stats.average_duration, Duration::from_millis(100));
855 assert_eq!(stats.min_duration, Duration::from_millis(100));
856 assert_eq!(stats.max_duration, Duration::from_millis(100));
857 assert!(stats.last_called.is_some());
858 }
859
860 #[test]
861 fn test_performance_stats_update_multiple_operations() {
862 let mut stats = PerformanceStats::new("test_op".to_string());
863
864 let metric1 = OperationMetrics {
866 operation_name: "test_op".to_string(),
867 duration: Duration::from_millis(100),
868 timestamp: Utc::now(),
869 success: true,
870 error_message: None,
871 };
872 stats.add_metric(&metric1);
873
874 let metric2 = OperationMetrics {
876 operation_name: "test_op".to_string(),
877 duration: Duration::from_millis(200),
878 timestamp: Utc::now(),
879 success: false,
880 error_message: Some("Error".to_string()),
881 };
882 stats.add_metric(&metric2);
883
884 let metric3 = OperationMetrics {
886 operation_name: "test_op".to_string(),
887 duration: Duration::from_millis(50),
888 timestamp: Utc::now(),
889 success: true,
890 error_message: None,
891 };
892 stats.add_metric(&metric3);
893
894 assert_eq!(stats.total_calls, 3);
895 assert_eq!(stats.successful_calls, 2);
896 assert_eq!(stats.failed_calls, 1);
897 assert!((stats.success_rate - 0.666_666_666_666_666_6).abs() < 0.001); assert!(
900 stats.average_duration >= Duration::from_millis(116)
901 && stats.average_duration <= Duration::from_millis(117)
902 );
903 assert_eq!(stats.min_duration, Duration::from_millis(50));
904 assert_eq!(stats.max_duration, Duration::from_millis(200));
905 }
906
907 #[test]
908 fn test_system_metrics_creation() {
909 let metrics = SystemMetrics {
910 timestamp: Utc::now(),
911 memory_usage_mb: 512.0,
912 cpu_usage_percent: 25.5,
913 available_memory_mb: 1024.0,
914 total_memory_mb: 2048.0,
915 };
916
917 assert!((metrics.cpu_usage_percent - 25.5).abs() < f64::EPSILON);
918 assert!((metrics.memory_usage_mb - 512.0).abs() < f64::EPSILON);
919 assert!((metrics.total_memory_mb - 2048.0).abs() < f64::EPSILON);
920 }
921
922 #[test]
923 fn test_cache_metrics_creation() {
924 let metrics = CacheMetrics {
925 cache_type: "l1".to_string(),
926 hits: 100,
927 misses: 25,
928 hit_rate: 0.8,
929 total_entries: 125,
930 memory_usage_bytes: 1024 * 1024,
931 evictions: 5,
932 insertions: 130,
933 invalidations: 2,
934 warming_entries: 0,
935 average_access_time_ms: 1.5,
936 last_accessed: Some(Utc::now()),
937 };
938
939 assert_eq!(metrics.hits, 100);
940 assert_eq!(metrics.misses, 25);
941 assert!((metrics.hit_rate - 0.8).abs() < f64::EPSILON);
942 assert_eq!(metrics.total_entries, 125);
943 assert_eq!(metrics.evictions, 5);
944 }
945
946 #[test]
947 fn test_query_metrics_creation() {
948 let metrics = QueryMetrics {
949 query_type: "tasks".to_string(),
950 total_queries: 1000,
951 cached_queries: 950,
952 database_queries: 50,
953 cache_hit_rate: 0.95,
954 average_query_time_ms: 150.0,
955 average_cache_time_ms: 5.0,
956 average_database_time_ms: 200.0,
957 slowest_query_ms: 2000,
958 fastest_query_ms: 10,
959 query_size_bytes: 1024,
960 compression_ratio: 0.8,
961 };
962
963 assert_eq!(metrics.total_queries, 1000);
964 assert_eq!(metrics.cached_queries, 950);
965 assert_eq!(metrics.database_queries, 50);
966 assert!((metrics.average_query_time_ms - 150.0).abs() < f64::EPSILON);
967 assert_eq!(metrics.slowest_query_ms, 2000);
968 assert_eq!(metrics.fastest_query_ms, 10);
969 }
970
971 #[test]
972 fn test_operation_timer_success() {
973 let monitor = PerformanceMonitor::new_default();
974 let timer = monitor.start_operation("test_operation");
975
976 std::thread::sleep(Duration::from_millis(10));
978
979 timer.success();
980
981 let stats = monitor.get_operation_stats("test_operation").unwrap();
982 assert_eq!(stats.operation_name, "test_operation");
983 assert_eq!(stats.total_calls, 1);
984 assert_eq!(stats.successful_calls, 1);
985 assert!(stats.average_duration >= Duration::from_millis(10));
986 }
987
988 #[test]
989 fn test_operation_timer_error() {
990 let monitor = PerformanceMonitor::new_default();
991 let timer = monitor.start_operation("test_operation");
992
993 timer.error("Test error occurred".to_string());
994
995 let stats = monitor.get_operation_stats("test_operation").unwrap();
996 assert_eq!(stats.operation_name, "test_operation");
997 assert_eq!(stats.total_calls, 1);
998 assert_eq!(stats.failed_calls, 1);
999 }
1000
1001 #[test]
1002 fn test_performance_monitor_get_summary_empty_comprehensive() {
1003 let monitor = PerformanceMonitor::new_default();
1004 let summary = monitor.get_summary();
1005
1006 assert_eq!(summary.total_operations, 0);
1007 assert_eq!(summary.operation_count, 0);
1008 }
1009
1010 #[test]
1011 fn test_performance_monitor_record_and_get_summary() {
1012 let monitor = PerformanceMonitor::new_default();
1013
1014 let metric1 = OperationMetrics {
1016 operation_name: "op1".to_string(),
1017 duration: Duration::from_millis(100),
1018 timestamp: Utc::now(),
1019 success: true,
1020 error_message: None,
1021 };
1022 monitor.record_operation(&metric1);
1023
1024 let metric2 = OperationMetrics {
1025 operation_name: "op2".to_string(),
1026 duration: Duration::from_millis(200),
1027 timestamp: Utc::now(),
1028 success: false,
1029 error_message: Some("Error".to_string()),
1030 };
1031 monitor.record_operation(&metric2);
1032
1033 let summary = monitor.get_summary();
1034 assert_eq!(summary.total_operations, 2);
1035 assert_eq!(summary.operation_count, 2);
1036
1037 let op1_stats = monitor.get_operation_stats("op1").unwrap();
1038 assert_eq!(op1_stats.total_calls, 1);
1039 assert_eq!(op1_stats.successful_calls, 1);
1040
1041 let op2_stats = monitor.get_operation_stats("op2").unwrap();
1042 assert_eq!(op2_stats.total_calls, 1);
1043 assert_eq!(op2_stats.failed_calls, 1);
1044 }
1045}