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