Skip to main content

things3_core/
performance.rs

1//! Performance monitoring and metrics for Things 3 operations
2
3use 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/// Performance metrics for a single operation
13#[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/// Aggregated performance statistics
23#[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/// System resource metrics
88#[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/// Cache-specific performance metrics
98#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct CacheMetrics {
100    pub cache_type: String, // "l1", "l2", "l3", "query"
101    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/// Database query performance metrics
115#[derive(Debug, Clone, Serialize, Deserialize)]
116pub struct QueryMetrics {
117    pub query_type: String, // "tasks", "projects", "areas", "search"
118    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/// Comprehensive performance summary including all metrics
132#[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
142/// Performance monitor for tracking operations and system metrics
143pub struct PerformanceMonitor {
144    /// Individual operation metrics
145    metrics: Arc<RwLock<Vec<OperationMetrics>>>,
146    /// Aggregated statistics by operation name
147    stats: Arc<RwLock<HashMap<String, PerformanceStats>>>,
148    /// Cache-specific metrics by cache type
149    cache_metrics: Arc<RwLock<HashMap<String, CacheMetrics>>>,
150    /// Query performance metrics by query type
151    query_metrics: Arc<RwLock<HashMap<String, QueryMetrics>>>,
152    /// System information
153    system: Arc<RwLock<System>>,
154    /// Maximum number of metrics to keep in memory
155    max_metrics: usize,
156}
157
158impl PerformanceMonitor {
159    /// Create a new performance monitor
160    #[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    /// Create a new performance monitor with default settings
173    #[must_use]
174    pub fn new_default() -> Self {
175        Self::new(10000) // Keep last 10,000 metrics
176    }
177
178    /// Start timing an operation
179    #[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    /// Record a completed operation
189    pub fn record_operation(&self, metric: &OperationMetrics) {
190        // Add to metrics list
191        {
192            let mut metrics = self.metrics.write();
193            metrics.push(metric.clone());
194
195            // Trim if we exceed max_metrics
196            if metrics.len() > self.max_metrics {
197                let excess = metrics.len() - self.max_metrics;
198                metrics.drain(0..excess);
199            }
200        }
201
202        // Update aggregated stats
203        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    /// Get all operation metrics
213    #[must_use]
214    pub fn get_metrics(&self) -> Vec<OperationMetrics> {
215        self.metrics.read().clone()
216    }
217
218    /// Get aggregated statistics for all operations
219    #[must_use]
220    pub fn get_all_stats(&self) -> HashMap<String, PerformanceStats> {
221        self.stats.read().clone()
222    }
223
224    /// Get statistics for a specific operation
225    #[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    /// Get current system metrics
231    /// Get system metrics
232    ///
233    /// # Errors
234    ///
235    /// Returns an error if system information cannot be retrieved.
236    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    /// Clear all metrics and statistics
263    pub fn clear(&self) {
264        self.metrics.write().clear();
265        self.stats.write().clear();
266    }
267
268    /// Get performance summary
269    #[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    /// Record cache metrics
298    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    /// Get cache metrics for a specific cache type
304    #[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    /// Get all cache metrics
311    #[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    /// Record query metrics
318    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    /// Get query metrics for a specific query type
324    #[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    /// Get all query metrics
331    #[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    /// Get comprehensive performance summary including cache and query metrics
338    #[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        // Calculate overall health score including cache performance
352        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    /// Calculate comprehensive health score including cache and query performance
369    #[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        // Operation performance (40% weight)
379        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        // Cache performance (35% weight)
398        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        // Query performance (25% weight)
413        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
452/// Timer for tracking operation duration
453pub struct OperationTimer {
454    monitor: PerformanceMonitor,
455    operation_name: String,
456    start_time: Instant,
457}
458
459impl OperationTimer {
460    /// Complete the operation successfully
461    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    /// Complete the operation with an error
474    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/// Performance summary
488#[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        // Record some operations
509        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        // Test successful operation
532        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        // Record a failed operation
548        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        // Record multiple operations
571        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        // Record operations for different types
599        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        // Record some operations
637        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        // Test failed operation by recording it directly
679        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        // Test that dropping the timer records the operation
701        {
702            let timer = monitor.start_operation("test_drop");
703            thread::sleep(Duration::from_millis(5));
704            // Explicitly call success before dropping
705            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        // Record an operation
721        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        // Clone the monitor
731        let monitor2 = monitor1.clone();
732
733        // Both should have the same stats
734        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        // Record multiple operations
747        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        // Check op1 stats
771        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        // Check op2 stats
777        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        // Record some operations
788        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        // Add successful operation
855        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        // Add failed operation
865        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        // Add another successful operation
875        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); // 2/3
888                                                                               // Average should be approximately 116-117ms due to rounding
889        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        // Simulate some work
967        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        // Record some operations
1005        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}