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: 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    /// Record cache metrics
301    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    /// Get cache metrics for a specific cache type
307    #[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    /// Get all cache metrics
314    #[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    /// Record query metrics
321    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    /// Get query metrics for a specific query type
327    #[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    /// Get all query metrics
334    #[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    /// Get comprehensive performance summary including cache and query metrics
341    #[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        // Calculate overall health score including cache performance
355        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    /// Calculate comprehensive health score including cache and query performance
372    #[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        // Operation performance (40% weight)
382        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        // Cache performance (35% weight)
401        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        // Query performance (25% weight)
416        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
455/// Timer for tracking operation duration
456pub struct OperationTimer {
457    monitor: PerformanceMonitor,
458    operation_name: String,
459    start_time: Instant,
460}
461
462impl OperationTimer {
463    /// Complete the operation successfully
464    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    /// Complete the operation with an error
477    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/// Performance summary
491#[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        // Record some operations
512        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        // Test successful operation
535        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        // Record a failed operation
551        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        // Record multiple operations
574        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        // Record operations for different types
602        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        // Record some operations
640        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        // Test failed operation by recording it directly
682        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        // Test that dropping the timer records the operation
704        {
705            let timer = monitor.start_operation("test_drop");
706            thread::sleep(Duration::from_millis(5));
707            // Explicitly call success before dropping
708            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        // Record an operation
724        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        // Clone the monitor
734        let monitor2 = monitor1.clone();
735
736        // Both should have the same stats
737        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        // Record multiple operations
750        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        // Check op1 stats
774        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        // Check op2 stats
780        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        // Record some operations
791        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        // Add successful operation
858        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        // Add failed operation
868        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        // Add another successful operation
878        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); // 2/3
891                                                                               // Average should be approximately 116-117ms due to rounding
892        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        // Simulate some work
970        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        // Record some operations
1008        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}