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: system.available_memory() as f64 / 1024.0 / 1024.0,
257 #[allow(clippy::cast_precision_loss)]
258 total_memory_mb: system.total_memory() as f64 / 1024.0 / 1024.0,
259 })
260 }
261
262 pub fn clear(&self) {
264 self.metrics.write().clear();
265 self.stats.write().clear();
266 }
267
268 #[must_use]
270 pub fn get_summary(&self) -> PerformanceSummary {
271 let stats = self.get_all_stats();
272 let total_operations: u64 = stats.values().map(|s| s.total_calls).sum();
273 let total_successful: u64 = stats.values().map(|s| s.successful_calls).sum();
274 let total_duration: Duration = stats.values().map(|s| s.total_duration).sum();
275
276 PerformanceSummary {
277 total_operations,
278 total_successful,
279 total_failed: total_operations - total_successful,
280 overall_success_rate: if total_operations > 0 {
281 #[allow(clippy::cast_precision_loss)]
282 {
283 total_successful as f64 / total_operations as f64
284 }
285 } else {
286 0.0
287 },
288 total_duration,
289 average_operation_duration: u64::try_from(total_duration.as_nanos())
290 .unwrap_or(0)
291 .checked_div(total_operations)
292 .map_or(Duration::ZERO, Duration::from_nanos),
293 operation_count: stats.len(),
294 }
295 }
296
297 pub fn record_cache_metrics(&self, cache_type: &str, metrics: CacheMetrics) {
299 let mut cache_metrics = self.cache_metrics.write();
300 cache_metrics.insert(cache_type.to_string(), metrics);
301 }
302
303 #[must_use]
305 pub fn get_cache_metrics(&self, cache_type: &str) -> Option<CacheMetrics> {
306 let cache_metrics = self.cache_metrics.read();
307 cache_metrics.get(cache_type).cloned()
308 }
309
310 #[must_use]
312 pub fn get_all_cache_metrics(&self) -> HashMap<String, CacheMetrics> {
313 let cache_metrics = self.cache_metrics.read();
314 cache_metrics.clone()
315 }
316
317 pub fn record_query_metrics(&self, query_type: &str, metrics: QueryMetrics) {
319 let mut query_metrics = self.query_metrics.write();
320 query_metrics.insert(query_type.to_string(), metrics);
321 }
322
323 #[must_use]
325 pub fn get_query_metrics(&self, query_type: &str) -> Option<QueryMetrics> {
326 let query_metrics = self.query_metrics.read();
327 query_metrics.get(query_type).cloned()
328 }
329
330 #[must_use]
332 pub fn get_all_query_metrics(&self) -> HashMap<String, QueryMetrics> {
333 let query_metrics = self.query_metrics.read();
334 query_metrics.clone()
335 }
336
337 #[must_use]
339 pub fn get_comprehensive_summary(&self) -> ComprehensivePerformanceSummary {
340 let operation_stats = self.get_all_stats();
341 let system_metrics = self.get_system_metrics().unwrap_or_else(|_| SystemMetrics {
342 timestamp: Utc::now(),
343 memory_usage_mb: 0.0,
344 cpu_usage_percent: 0.0,
345 available_memory_mb: 0.0,
346 total_memory_mb: 0.0,
347 });
348 let cache_metrics = self.get_all_cache_metrics();
349 let query_metrics = self.get_all_query_metrics();
350
351 let health_score = Self::calculate_comprehensive_health_score(
353 &operation_stats,
354 &cache_metrics,
355 &query_metrics,
356 );
357
358 ComprehensivePerformanceSummary {
359 timestamp: Utc::now(),
360 operation_stats,
361 system_metrics,
362 cache_metrics,
363 query_metrics,
364 overall_health_score: health_score,
365 }
366 }
367
368 #[allow(clippy::cast_precision_loss)]
370 fn calculate_comprehensive_health_score(
371 operation_stats: &HashMap<String, PerformanceStats>,
372 cache_metrics: &HashMap<String, CacheMetrics>,
373 query_metrics: &HashMap<String, QueryMetrics>,
374 ) -> f64 {
375 let mut total_score = 0.0;
376 let mut weight_sum = 0.0;
377
378 if !operation_stats.is_empty() {
380 let avg_success_rate = operation_stats
381 .values()
382 .map(|s| s.success_rate)
383 .sum::<f64>()
384 / operation_stats.len() as f64;
385 let avg_response_time = operation_stats
386 .values()
387 .map(|s| s.average_duration.as_millis() as f64)
388 .sum::<f64>()
389 / operation_stats.len() as f64;
390
391 let operation_score =
392 (avg_success_rate * 70.0) + ((1000.0 - avg_response_time.min(1000.0)) * 0.3);
393 total_score += operation_score * 0.4;
394 weight_sum += 0.4;
395 }
396
397 if !cache_metrics.is_empty() {
399 let avg_hit_rate = cache_metrics.values().map(|c| c.hit_rate).sum::<f64>()
400 / cache_metrics.len() as f64;
401 let avg_access_time = cache_metrics
402 .values()
403 .map(|c| c.average_access_time_ms)
404 .sum::<f64>()
405 / cache_metrics.len() as f64;
406
407 let cache_score = (avg_hit_rate * 60.0) + ((100.0 - avg_access_time.min(100.0)) * 0.4);
408 total_score += cache_score * 0.35;
409 weight_sum += 0.35;
410 }
411
412 if !query_metrics.is_empty() {
414 let avg_query_hit_rate = query_metrics
415 .values()
416 .map(|q| q.cache_hit_rate)
417 .sum::<f64>()
418 / query_metrics.len() as f64;
419 let avg_query_time = query_metrics
420 .values()
421 .map(|q| q.average_query_time_ms)
422 .sum::<f64>()
423 / query_metrics.len() as f64;
424
425 let query_score =
426 (avg_query_hit_rate * 50.0) + ((1000.0 - avg_query_time.min(1000.0)) * 0.5);
427 total_score += query_score * 0.25;
428 weight_sum += 0.25;
429 }
430
431 if weight_sum > 0.0 {
432 (total_score / weight_sum).clamp(0.0, 100.0)
433 } else {
434 100.0
435 }
436 }
437}
438
439impl Clone for PerformanceMonitor {
440 fn clone(&self) -> Self {
441 Self {
442 metrics: Arc::clone(&self.metrics),
443 stats: Arc::clone(&self.stats),
444 cache_metrics: Arc::clone(&self.cache_metrics),
445 query_metrics: Arc::clone(&self.query_metrics),
446 system: Arc::clone(&self.system),
447 max_metrics: self.max_metrics,
448 }
449 }
450}
451
452pub struct OperationTimer {
454 monitor: PerformanceMonitor,
455 operation_name: String,
456 start_time: Instant,
457}
458
459impl OperationTimer {
460 pub fn success(self) {
462 let duration = self.start_time.elapsed();
463 let metric = OperationMetrics {
464 operation_name: self.operation_name,
465 duration,
466 timestamp: Utc::now(),
467 success: true,
468 error_message: None,
469 };
470 self.monitor.record_operation(&metric);
471 }
472
473 pub fn error(self, error_message: String) {
475 let duration = self.start_time.elapsed();
476 let metric = OperationMetrics {
477 operation_name: self.operation_name,
478 duration,
479 timestamp: Utc::now(),
480 success: false,
481 error_message: Some(error_message),
482 };
483 self.monitor.record_operation(&metric);
484 }
485}
486
487#[derive(Debug, Clone, Serialize, Deserialize)]
489pub struct PerformanceSummary {
490 pub total_operations: u64,
491 pub total_successful: u64,
492 pub total_failed: u64,
493 pub overall_success_rate: f64,
494 pub total_duration: Duration,
495 pub average_operation_duration: Duration,
496 pub operation_count: usize,
497}
498
499#[cfg(test)]
500mod tests {
501 use super::*;
502 use std::thread;
503
504 #[test]
505 fn test_performance_monitor() {
506 let monitor = PerformanceMonitor::new_default();
507
508 let metric1 = OperationMetrics {
510 operation_name: "test_op".to_string(),
511 duration: Duration::from_millis(100),
512 timestamp: Utc::now(),
513 success: true,
514 error_message: None,
515 };
516
517 monitor.record_operation(&metric1);
518
519 let stats = monitor.get_operation_stats("test_op");
520 assert!(stats.is_some());
521 let stats = stats.unwrap();
522 assert_eq!(stats.total_calls, 1);
523 assert_eq!(stats.successful_calls, 1);
524 assert_eq!(stats.failed_calls, 0);
525 }
526
527 #[test]
528 fn test_operation_timer() {
529 let monitor = PerformanceMonitor::new_default();
530
531 let timer = monitor.start_operation("test_timer");
533 thread::sleep(Duration::from_millis(10));
534 timer.success();
535
536 let stats = monitor.get_operation_stats("test_timer");
537 assert!(stats.is_some());
538 let stats = stats.unwrap();
539 assert_eq!(stats.total_calls, 1);
540 assert!(stats.successful_calls > 0);
541 }
542
543 #[test]
544 fn test_performance_monitor_failed_operation() {
545 let monitor = PerformanceMonitor::new_default();
546
547 let metric = OperationMetrics {
549 operation_name: "failed_op".to_string(),
550 duration: Duration::from_millis(50),
551 timestamp: Utc::now(),
552 success: false,
553 error_message: Some("Test error".to_string()),
554 };
555
556 monitor.record_operation(&metric);
557
558 let stats = monitor.get_operation_stats("failed_op");
559 assert!(stats.is_some());
560 let stats = stats.unwrap();
561 assert_eq!(stats.total_calls, 1);
562 assert_eq!(stats.successful_calls, 0);
563 assert_eq!(stats.failed_calls, 1);
564 }
565
566 #[test]
567 fn test_performance_monitor_multiple_operations() {
568 let monitor = PerformanceMonitor::new_default();
569
570 for i in 0..5 {
572 let metric = OperationMetrics {
573 operation_name: "multi_op".to_string(),
574 duration: Duration::from_millis(i * 10),
575 timestamp: Utc::now(),
576 success: i % 2 == 0,
577 error_message: if i % 2 == 0 {
578 None
579 } else {
580 Some("Error".to_string())
581 },
582 };
583 monitor.record_operation(&metric);
584 }
585
586 let stats = monitor.get_operation_stats("multi_op");
587 assert!(stats.is_some());
588 let stats = stats.unwrap();
589 assert_eq!(stats.total_calls, 5);
590 assert_eq!(stats.successful_calls, 3);
591 assert_eq!(stats.failed_calls, 2);
592 }
593
594 #[test]
595 fn test_performance_monitor_get_all_stats() {
596 let monitor = PerformanceMonitor::new_default();
597
598 let operations = vec![("op1", true), ("op1", false), ("op2", true), ("op2", true)];
600
601 for (name, success) in operations {
602 let metric = OperationMetrics {
603 operation_name: name.to_string(),
604 duration: Duration::from_millis(100),
605 timestamp: Utc::now(),
606 success,
607 error_message: if success {
608 None
609 } else {
610 Some("Error".to_string())
611 },
612 };
613 monitor.record_operation(&metric);
614 }
615
616 let all_stats = monitor.get_all_stats();
617 assert_eq!(all_stats.len(), 2);
618 assert!(all_stats.contains_key("op1"));
619 assert!(all_stats.contains_key("op2"));
620
621 let op1_stats = &all_stats["op1"];
622 assert_eq!(op1_stats.total_calls, 2);
623 assert_eq!(op1_stats.successful_calls, 1);
624 assert_eq!(op1_stats.failed_calls, 1);
625
626 let op2_stats = &all_stats["op2"];
627 assert_eq!(op2_stats.total_calls, 2);
628 assert_eq!(op2_stats.successful_calls, 2);
629 assert_eq!(op2_stats.failed_calls, 0);
630 }
631
632 #[test]
633 fn test_performance_monitor_get_summary() {
634 let monitor = PerformanceMonitor::new_default();
635
636 let operations = vec![("op1", true, 100), ("op1", false, 200), ("op2", true, 150)];
638
639 for (name, success, duration_ms) in operations {
640 let metric = OperationMetrics {
641 operation_name: name.to_string(),
642 duration: Duration::from_millis(duration_ms),
643 timestamp: Utc::now(),
644 success,
645 error_message: if success {
646 None
647 } else {
648 Some("Error".to_string())
649 },
650 };
651 monitor.record_operation(&metric);
652 }
653
654 let summary = monitor.get_summary();
655 assert_eq!(summary.total_operations, 3);
656 assert_eq!(summary.total_successful, 2);
657 assert_eq!(summary.total_failed, 1);
658 assert!((summary.overall_success_rate - 2.0 / 3.0).abs() < 0.001);
659 assert_eq!(summary.operation_count, 2);
660 }
661
662 #[test]
663 fn test_performance_monitor_get_summary_empty() {
664 let monitor = PerformanceMonitor::new_default();
665 let summary = monitor.get_summary();
666
667 assert_eq!(summary.total_operations, 0);
668 assert_eq!(summary.total_successful, 0);
669 assert_eq!(summary.total_failed, 0);
670 assert!((summary.overall_success_rate - 0.0).abs() < f64::EPSILON);
671 assert_eq!(summary.operation_count, 0);
672 }
673
674 #[test]
675 fn test_operation_timer_failure() {
676 let monitor = PerformanceMonitor::new_default();
677
678 let metric = OperationMetrics {
680 operation_name: "test_failure".to_string(),
681 duration: Duration::from_millis(5),
682 timestamp: Utc::now(),
683 success: false,
684 error_message: Some("Test failure".to_string()),
685 };
686 monitor.record_operation(&metric);
687
688 let stats = monitor.get_operation_stats("test_failure");
689 assert!(stats.is_some());
690 let stats = stats.unwrap();
691 assert_eq!(stats.total_calls, 1);
692 assert_eq!(stats.successful_calls, 0);
693 assert_eq!(stats.failed_calls, 1);
694 }
695
696 #[test]
697 fn test_operation_timer_drop() {
698 let monitor = PerformanceMonitor::new_default();
699
700 {
702 let timer = monitor.start_operation("test_drop");
703 thread::sleep(Duration::from_millis(5));
704 timer.success();
706 }
707
708 let stats = monitor.get_operation_stats("test_drop");
709 assert!(stats.is_some());
710 let stats = stats.unwrap();
711 assert_eq!(stats.total_calls, 1);
712 assert_eq!(stats.successful_calls, 1);
713 assert_eq!(stats.failed_calls, 0);
714 }
715
716 #[test]
717 fn test_performance_monitor_clone() {
718 let monitor1 = PerformanceMonitor::new_default();
719
720 let metric = OperationMetrics {
722 operation_name: "clone_test".to_string(),
723 duration: Duration::from_millis(100),
724 timestamp: Utc::now(),
725 success: true,
726 error_message: None,
727 };
728 monitor1.record_operation(&metric);
729
730 let monitor2 = monitor1.clone();
732
733 let stats1 = monitor1.get_operation_stats("clone_test");
735 let stats2 = monitor2.get_operation_stats("clone_test");
736
737 assert!(stats1.is_some());
738 assert!(stats2.is_some());
739 assert_eq!(stats1.unwrap().total_calls, stats2.unwrap().total_calls);
740 }
741
742 #[test]
743 fn test_performance_monitor_additional_operations() {
744 let monitor = PerformanceMonitor::new_default();
745
746 let operations = vec![
748 ("op1", Duration::from_millis(100), true),
749 ("op1", Duration::from_millis(150), true),
750 ("op1", Duration::from_millis(200), false),
751 ("op2", Duration::from_millis(50), true),
752 ("op2", Duration::from_millis(75), true),
753 ];
754
755 for (op_name, duration, success) in operations {
756 let metric = OperationMetrics {
757 operation_name: op_name.to_string(),
758 duration,
759 timestamp: Utc::now(),
760 success,
761 error_message: if success {
762 None
763 } else {
764 Some("Test error".to_string())
765 },
766 };
767 monitor.record_operation(&metric);
768 }
769
770 let op1_stats = monitor.get_operation_stats("op1").unwrap();
772 assert_eq!(op1_stats.total_calls, 3);
773 assert_eq!(op1_stats.successful_calls, 2);
774 assert_eq!(op1_stats.failed_calls, 1);
775
776 let op2_stats = monitor.get_operation_stats("op2").unwrap();
778 assert_eq!(op2_stats.total_calls, 2);
779 assert_eq!(op2_stats.successful_calls, 2);
780 assert_eq!(op2_stats.failed_calls, 0);
781 }
782
783 #[test]
784 fn test_performance_monitor_get_metrics() {
785 let monitor = PerformanceMonitor::new_default();
786
787 let metric1 = OperationMetrics {
789 operation_name: "test_op".to_string(),
790 duration: Duration::from_millis(100),
791 timestamp: Utc::now(),
792 success: true,
793 error_message: None,
794 };
795 monitor.record_operation(&metric1);
796
797 let metric2 = OperationMetrics {
798 operation_name: "test_op2".to_string(),
799 duration: Duration::from_millis(200),
800 timestamp: Utc::now(),
801 success: false,
802 error_message: Some("Test error".to_string()),
803 };
804 monitor.record_operation(&metric2);
805
806 let all_metrics = monitor.get_metrics();
807 assert_eq!(all_metrics.len(), 2);
808 assert!(all_metrics.iter().any(|m| m.operation_name == "test_op"));
809 assert!(all_metrics.iter().any(|m| m.operation_name == "test_op2"));
810 }
811
812 #[test]
813 fn test_performance_stats_new_initialization() {
814 let stats = PerformanceStats::new("test_operation".to_string());
815 assert_eq!(stats.operation_name, "test_operation");
816 assert_eq!(stats.total_calls, 0);
817 assert_eq!(stats.successful_calls, 0);
818 assert_eq!(stats.failed_calls, 0);
819 assert!((stats.success_rate - 0.0).abs() < f64::EPSILON);
820 assert_eq!(stats.average_duration, Duration::from_millis(0));
821 assert_eq!(stats.min_duration, Duration::MAX);
822 assert_eq!(stats.max_duration, Duration::ZERO);
823 assert!(stats.last_called.is_none());
824 }
825
826 #[test]
827 fn test_performance_stats_update_single_success() {
828 let mut stats = PerformanceStats::new("test_op".to_string());
829
830 let metric = OperationMetrics {
831 operation_name: "test_op".to_string(),
832 duration: Duration::from_millis(100),
833 timestamp: Utc::now(),
834 success: true,
835 error_message: None,
836 };
837
838 stats.add_metric(&metric);
839
840 assert_eq!(stats.total_calls, 1);
841 assert_eq!(stats.successful_calls, 1);
842 assert_eq!(stats.failed_calls, 0);
843 assert!((stats.success_rate - 1.0).abs() < f64::EPSILON);
844 assert_eq!(stats.average_duration, Duration::from_millis(100));
845 assert_eq!(stats.min_duration, Duration::from_millis(100));
846 assert_eq!(stats.max_duration, Duration::from_millis(100));
847 assert!(stats.last_called.is_some());
848 }
849
850 #[test]
851 fn test_performance_stats_update_multiple_operations() {
852 let mut stats = PerformanceStats::new("test_op".to_string());
853
854 let metric1 = OperationMetrics {
856 operation_name: "test_op".to_string(),
857 duration: Duration::from_millis(100),
858 timestamp: Utc::now(),
859 success: true,
860 error_message: None,
861 };
862 stats.add_metric(&metric1);
863
864 let metric2 = OperationMetrics {
866 operation_name: "test_op".to_string(),
867 duration: Duration::from_millis(200),
868 timestamp: Utc::now(),
869 success: false,
870 error_message: Some("Error".to_string()),
871 };
872 stats.add_metric(&metric2);
873
874 let metric3 = OperationMetrics {
876 operation_name: "test_op".to_string(),
877 duration: Duration::from_millis(50),
878 timestamp: Utc::now(),
879 success: true,
880 error_message: None,
881 };
882 stats.add_metric(&metric3);
883
884 assert_eq!(stats.total_calls, 3);
885 assert_eq!(stats.successful_calls, 2);
886 assert_eq!(stats.failed_calls, 1);
887 assert!((stats.success_rate - 0.666_666_666_666_666_6).abs() < 0.001); assert!(
890 stats.average_duration >= Duration::from_millis(116)
891 && stats.average_duration <= Duration::from_millis(117)
892 );
893 assert_eq!(stats.min_duration, Duration::from_millis(50));
894 assert_eq!(stats.max_duration, Duration::from_millis(200));
895 }
896
897 #[test]
898 fn test_system_metrics_creation() {
899 let metrics = SystemMetrics {
900 timestamp: Utc::now(),
901 memory_usage_mb: 512.0,
902 cpu_usage_percent: 25.5,
903 available_memory_mb: 1024.0,
904 total_memory_mb: 2048.0,
905 };
906
907 assert!((metrics.cpu_usage_percent - 25.5).abs() < f64::EPSILON);
908 assert!((metrics.memory_usage_mb - 512.0).abs() < f64::EPSILON);
909 assert!((metrics.total_memory_mb - 2048.0).abs() < f64::EPSILON);
910 }
911
912 #[test]
913 fn test_cache_metrics_creation() {
914 let metrics = CacheMetrics {
915 cache_type: "l1".to_string(),
916 hits: 100,
917 misses: 25,
918 hit_rate: 0.8,
919 total_entries: 125,
920 memory_usage_bytes: 1024 * 1024,
921 evictions: 5,
922 insertions: 130,
923 invalidations: 2,
924 warming_entries: 0,
925 average_access_time_ms: 1.5,
926 last_accessed: Some(Utc::now()),
927 };
928
929 assert_eq!(metrics.hits, 100);
930 assert_eq!(metrics.misses, 25);
931 assert!((metrics.hit_rate - 0.8).abs() < f64::EPSILON);
932 assert_eq!(metrics.total_entries, 125);
933 assert_eq!(metrics.evictions, 5);
934 }
935
936 #[test]
937 fn test_query_metrics_creation() {
938 let metrics = QueryMetrics {
939 query_type: "tasks".to_string(),
940 total_queries: 1000,
941 cached_queries: 950,
942 database_queries: 50,
943 cache_hit_rate: 0.95,
944 average_query_time_ms: 150.0,
945 average_cache_time_ms: 5.0,
946 average_database_time_ms: 200.0,
947 slowest_query_ms: 2000,
948 fastest_query_ms: 10,
949 query_size_bytes: 1024,
950 compression_ratio: 0.8,
951 };
952
953 assert_eq!(metrics.total_queries, 1000);
954 assert_eq!(metrics.cached_queries, 950);
955 assert_eq!(metrics.database_queries, 50);
956 assert!((metrics.average_query_time_ms - 150.0).abs() < f64::EPSILON);
957 assert_eq!(metrics.slowest_query_ms, 2000);
958 assert_eq!(metrics.fastest_query_ms, 10);
959 }
960
961 #[test]
962 fn test_operation_timer_success() {
963 let monitor = PerformanceMonitor::new_default();
964 let timer = monitor.start_operation("test_operation");
965
966 std::thread::sleep(Duration::from_millis(10));
968
969 timer.success();
970
971 let stats = monitor.get_operation_stats("test_operation").unwrap();
972 assert_eq!(stats.operation_name, "test_operation");
973 assert_eq!(stats.total_calls, 1);
974 assert_eq!(stats.successful_calls, 1);
975 assert!(stats.average_duration >= Duration::from_millis(10));
976 }
977
978 #[test]
979 fn test_operation_timer_error() {
980 let monitor = PerformanceMonitor::new_default();
981 let timer = monitor.start_operation("test_operation");
982
983 timer.error("Test error occurred".to_string());
984
985 let stats = monitor.get_operation_stats("test_operation").unwrap();
986 assert_eq!(stats.operation_name, "test_operation");
987 assert_eq!(stats.total_calls, 1);
988 assert_eq!(stats.failed_calls, 1);
989 }
990
991 #[test]
992 fn test_performance_monitor_get_summary_empty_comprehensive() {
993 let monitor = PerformanceMonitor::new_default();
994 let summary = monitor.get_summary();
995
996 assert_eq!(summary.total_operations, 0);
997 assert_eq!(summary.operation_count, 0);
998 }
999
1000 #[test]
1001 fn test_performance_monitor_record_and_get_summary() {
1002 let monitor = PerformanceMonitor::new_default();
1003
1004 let metric1 = OperationMetrics {
1006 operation_name: "op1".to_string(),
1007 duration: Duration::from_millis(100),
1008 timestamp: Utc::now(),
1009 success: true,
1010 error_message: None,
1011 };
1012 monitor.record_operation(&metric1);
1013
1014 let metric2 = OperationMetrics {
1015 operation_name: "op2".to_string(),
1016 duration: Duration::from_millis(200),
1017 timestamp: Utc::now(),
1018 success: false,
1019 error_message: Some("Error".to_string()),
1020 };
1021 monitor.record_operation(&metric2);
1022
1023 let summary = monitor.get_summary();
1024 assert_eq!(summary.total_operations, 2);
1025 assert_eq!(summary.operation_count, 2);
1026
1027 let op1_stats = monitor.get_operation_stats("op1").unwrap();
1028 assert_eq!(op1_stats.total_calls, 1);
1029 assert_eq!(op1_stats.successful_calls, 1);
1030
1031 let op2_stats = monitor.get_operation_stats("op2").unwrap();
1032 assert_eq!(op2_stats.total_calls, 1);
1033 assert_eq!(op2_stats.failed_calls, 1);
1034 }
1035}