oxirs_vec/
performance_insights.rs

1//! Advanced Performance Insights and Monitoring for OxiRS Vector Search
2//!
3//! This module provides comprehensive performance analysis, optimization recommendations,
4//! and real-time monitoring capabilities for the vector search engine.
5
6use crate::{Vector, VectorId};
7use anyhow::Result;
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use std::time::{Duration, Instant, SystemTime};
11use tracing::{debug, info, warn};
12
13/// Advanced performance insights analyzer
14#[derive(Debug, Clone)]
15pub struct PerformanceInsightsAnalyzer {
16    /// Query execution statistics
17    query_stats: QueryStatistics,
18    /// Vector distribution analysis
19    vector_stats: VectorStatistics,
20    /// Performance trends over time
21    trends: PerformanceTrends,
22    /// Optimization recommendations
23    recommendations: OptimizationRecommendations,
24    /// Real-time metrics collection
25    metrics_collector: MetricsCollector,
26    /// Performance alerting system
27    alerting_system: AlertingSystem,
28}
29
30/// Comprehensive query performance statistics
31#[derive(Debug, Clone, Default, Serialize, Deserialize)]
32pub struct QueryStatistics {
33    pub total_queries: u64,
34    pub average_latency_ms: f64,
35    pub p50_latency_ms: f64,
36    pub p95_latency_ms: f64,
37    pub p99_latency_ms: f64,
38    pub throughput_qps: f64,
39    pub error_rate: f64,
40    pub cache_hit_rate: f64,
41    pub index_efficiency: f64,
42    pub memory_usage_mb: f64,
43    pub cpu_utilization: f64,
44    pub latency_distribution: Vec<LatencyBucket>,
45    pub query_complexity_distribution: HashMap<QueryComplexity, u64>,
46    pub top_slow_queries: Vec<SlowQueryEntry>,
47}
48
49/// Vector dataset quality and distribution statistics
50#[derive(Debug, Clone, Default, Serialize, Deserialize)]
51pub struct VectorStatistics {
52    pub total_vectors: u64,
53    pub average_dimension: u32,
54    pub dimension_distribution: HashMap<u32, u64>,
55    pub vector_density: f64,
56    pub sparsity_ratio: f64,
57    pub clustering_coefficient: f64,
58    pub hubness_measure: f64,
59    pub intrinsic_dimensionality: f64,
60    pub noise_estimation: f64,
61    pub quality_score: f64,
62    pub outlier_count: u64,
63    pub similarity_distribution: SimilarityDistribution,
64    pub vector_type_distribution: HashMap<String, u64>,
65}
66
67/// Performance trends analysis over time
68#[derive(Debug, Clone, Default, Serialize, Deserialize)]
69pub struct PerformanceTrends {
70    pub trend_window_hours: u32,
71    pub latency_trend: TrendDirection,
72    pub throughput_trend: TrendDirection,
73    pub error_rate_trend: TrendDirection,
74    pub memory_usage_trend: TrendDirection,
75    pub cache_efficiency_trend: TrendDirection,
76    pub index_performance_trend: TrendDirection,
77    pub historical_data: Vec<PerformanceSnapshot>,
78    pub seasonal_patterns: SeasonalPatterns,
79    pub anomaly_detection: AnomalyDetection,
80}
81
82/// Intelligent optimization recommendations
83#[derive(Debug, Clone, Default, Serialize, Deserialize)]
84pub struct OptimizationRecommendations {
85    pub index_recommendations: Vec<IndexRecommendation>,
86    pub caching_recommendations: Vec<CachingRecommendation>,
87    pub query_recommendations: Vec<QueryRecommendation>,
88    pub hardware_recommendations: Vec<HardwareRecommendation>,
89    pub configuration_recommendations: Vec<ConfigurationRecommendation>,
90    pub priority_actions: Vec<PriorityAction>,
91    pub estimated_improvements: ImprovementEstimates,
92}
93
94/// Real-time metrics collection system
95#[derive(Debug, Clone)]
96pub struct MetricsCollector {
97    start_time: Instant,
98    query_times: Vec<Duration>,
99    query_complexities: Vec<QueryComplexity>,
100    memory_samples: Vec<u64>,
101    cpu_samples: Vec<f64>,
102    cache_metrics: CacheMetrics,
103    error_counts: HashMap<String, u64>,
104    active_queries: u32,
105}
106
107/// Advanced alerting system
108#[derive(Debug, Clone)]
109pub struct AlertingSystem {
110    alert_rules: Vec<AlertRule>,
111    active_alerts: Vec<ActiveAlert>,
112    alert_history: Vec<AlertEvent>,
113    notification_channels: Vec<NotificationChannel>,
114}
115
116// Supporting types
117
118#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct LatencyBucket {
120    pub range_ms: (f64, f64),
121    pub count: u64,
122    pub percentage: f64,
123}
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
126pub enum QueryComplexity {
127    Simple,
128    Moderate,
129    Complex,
130    HighlyComplex,
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct SlowQueryEntry {
135    pub query_id: String,
136    pub execution_time_ms: f64,
137    pub timestamp: SystemTime,
138    pub query_type: String,
139    pub vector_count: u64,
140    pub optimization_potential: f64,
141}
142
143#[derive(Debug, Clone, Default, Serialize, Deserialize)]
144pub struct SimilarityDistribution {
145    pub min_similarity: f64,
146    pub max_similarity: f64,
147    pub mean_similarity: f64,
148    pub std_deviation: f64,
149    pub distribution_buckets: Vec<(f64, u64)>,
150    pub skewness: f64,
151    pub kurtosis: f64,
152}
153
154#[derive(Debug, Clone, Default, Serialize, Deserialize)]
155pub enum TrendDirection {
156    Improving,
157    #[default]
158    Stable,
159    Degrading,
160    Volatile,
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct PerformanceSnapshot {
165    pub timestamp: SystemTime,
166    pub latency_p99: f64,
167    pub throughput: f64,
168    pub memory_usage: u64,
169    pub cpu_usage: f64,
170    pub cache_hit_rate: f64,
171    pub error_rate: f64,
172}
173
174#[derive(Debug, Clone, Default, Serialize, Deserialize)]
175pub struct SeasonalPatterns {
176    pub daily_patterns: Vec<(u8, f64)>,   // Hour -> performance factor
177    pub weekly_patterns: Vec<(u8, f64)>,  // Day of week -> performance factor
178    pub monthly_patterns: Vec<(u8, f64)>, // Day of month -> performance factor
179}
180
181#[derive(Debug, Clone, Default, Serialize, Deserialize)]
182pub struct AnomalyDetection {
183    pub anomaly_threshold: f64,
184    pub detected_anomalies: Vec<AnomalyEvent>,
185    pub baseline_performance: f64,
186    pub current_deviation: f64,
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize)]
190pub struct AnomalyEvent {
191    pub timestamp: SystemTime,
192    pub metric: String,
193    pub value: f64,
194    pub expected_value: f64,
195    pub deviation_score: f64,
196    pub severity: AlertSeverity,
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct IndexRecommendation {
201    pub recommendation_type: String,
202    pub description: String,
203    pub estimated_improvement: f64,
204    pub implementation_effort: EffortLevel,
205    pub prerequisites: Vec<String>,
206}
207
208#[derive(Debug, Clone, Serialize, Deserialize)]
209pub struct CachingRecommendation {
210    pub cache_type: String,
211    pub recommended_size_mb: u64,
212    pub eviction_policy: String,
213    pub estimated_hit_rate: f64,
214    pub cost_benefit_ratio: f64,
215}
216
217#[derive(Debug, Clone, Serialize, Deserialize)]
218pub struct QueryRecommendation {
219    pub query_pattern: String,
220    pub optimization_technique: String,
221    pub expected_speedup: f64,
222    pub applicability: f64,
223}
224
225#[derive(Debug, Clone, Serialize, Deserialize)]
226pub struct HardwareRecommendation {
227    pub component: String,
228    pub current_bottleneck: bool,
229    pub recommended_upgrade: String,
230    pub performance_impact: f64,
231    pub cost_estimate: Option<String>,
232}
233
234#[derive(Debug, Clone, Serialize, Deserialize)]
235pub struct ConfigurationRecommendation {
236    pub parameter: String,
237    pub current_value: String,
238    pub recommended_value: String,
239    pub justification: String,
240    pub risk_level: RiskLevel,
241}
242
243#[derive(Debug, Clone, Serialize, Deserialize)]
244pub struct PriorityAction {
245    pub action: String,
246    pub priority: Priority,
247    pub estimated_impact: f64,
248    pub implementation_time: Duration,
249    pub dependencies: Vec<String>,
250}
251
252#[derive(Debug, Clone, Default, Serialize, Deserialize)]
253pub struct ImprovementEstimates {
254    pub latency_improvement: f64,
255    pub throughput_improvement: f64,
256    pub memory_savings: f64,
257    pub cost_reduction: f64,
258    pub reliability_improvement: f64,
259}
260
261#[derive(Debug, Clone, Default)]
262pub struct CacheMetrics {
263    pub hits: u64,
264    pub misses: u64,
265    pub evictions: u64,
266    pub size_bytes: u64,
267}
268
269#[derive(Debug, Clone, Serialize, Deserialize)]
270pub struct AlertRule {
271    pub name: String,
272    pub metric: String,
273    pub threshold: f64,
274    pub comparison: ComparisonOperator,
275    pub severity: AlertSeverity,
276    pub enabled: bool,
277}
278
279#[derive(Debug, Clone, Serialize, Deserialize)]
280pub struct ActiveAlert {
281    pub rule_name: String,
282    pub triggered_at: SystemTime,
283    pub current_value: f64,
284    pub threshold: f64,
285    pub severity: AlertSeverity,
286    pub acknowledged: bool,
287}
288
289#[derive(Debug, Clone, Serialize, Deserialize)]
290pub struct AlertEvent {
291    pub timestamp: SystemTime,
292    pub alert_name: String,
293    pub event_type: AlertEventType,
294    pub details: String,
295}
296
297#[derive(Debug, Clone, Serialize, Deserialize)]
298pub enum NotificationChannel {
299    Email(String),
300    Webhook(String),
301    Slack(String),
302    Console,
303}
304
305#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
306pub enum EffortLevel {
307    Low,
308    Medium,
309    High,
310    VeryHigh,
311}
312
313#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
314pub enum RiskLevel {
315    Low,
316    Medium,
317    High,
318    Critical,
319}
320
321#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
322pub enum Priority {
323    Low,
324    Medium,
325    High,
326    Critical,
327}
328
329#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
330pub enum ComparisonOperator {
331    GreaterThan,
332    LessThan,
333    Equals,
334    GreaterThanOrEqual,
335    LessThanOrEqual,
336}
337
338#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
339pub enum AlertSeverity {
340    Info,
341    Warning,
342    Critical,
343    Emergency,
344}
345
346#[derive(Debug, Clone, Serialize, Deserialize)]
347pub enum AlertEventType {
348    Triggered,
349    Resolved,
350    Acknowledged,
351    Escalated,
352}
353
354impl PerformanceInsightsAnalyzer {
355    /// Create a new performance insights analyzer
356    pub fn new() -> Self {
357        Self {
358            query_stats: QueryStatistics::default(),
359            vector_stats: VectorStatistics::default(),
360            trends: PerformanceTrends::default(),
361            recommendations: OptimizationRecommendations::default(),
362            metrics_collector: MetricsCollector::new(),
363            alerting_system: AlertingSystem::new(),
364        }
365    }
366
367    /// Record a query execution for performance analysis
368    pub fn record_query(&mut self, duration: Duration, complexity: QueryComplexity, success: bool) {
369        self.metrics_collector
370            .record_query(duration, complexity, success);
371        self.query_stats.total_queries += 1;
372
373        let latency_ms = duration.as_secs_f64() * 1000.0;
374        self.update_latency_statistics(latency_ms);
375
376        if !success {
377            self.query_stats.error_rate = self.calculate_error_rate();
378        }
379
380        self.check_performance_alerts(latency_ms);
381    }
382
383    /// Analyze vector dataset characteristics
384    pub fn analyze_vector_dataset(
385        &mut self,
386        vectors: &[(VectorId, Vector)],
387    ) -> Result<VectorStatistics> {
388        info!("Analyzing vector dataset with {} vectors", vectors.len());
389
390        let mut stats = VectorStatistics {
391            total_vectors: vectors.len() as u64,
392            ..Default::default()
393        };
394
395        if vectors.is_empty() {
396            return Ok(stats);
397        }
398
399        // Analyze dimensions
400        let dimensions: Vec<u32> = vectors.iter().map(|(_, v)| v.dimensions as u32).collect();
401        stats.average_dimension = dimensions.iter().sum::<u32>() / dimensions.len() as u32;
402
403        for &dim in &dimensions {
404            *stats.dimension_distribution.entry(dim).or_insert(0) += 1;
405        }
406
407        // Analyze vector density and sparsity
408        let (density, sparsity) = self.calculate_vector_density(vectors);
409        stats.vector_density = density;
410        stats.sparsity_ratio = sparsity;
411
412        // Calculate clustering coefficient
413        stats.clustering_coefficient = self.calculate_clustering_coefficient(vectors);
414
415        // Calculate hubness measure
416        stats.hubness_measure = self.calculate_hubness_measure(vectors);
417
418        // Estimate intrinsic dimensionality
419        stats.intrinsic_dimensionality = self.estimate_intrinsic_dimensionality(vectors);
420
421        // Estimate noise level
422        stats.noise_estimation = self.estimate_noise_level(vectors);
423
424        // Calculate overall quality score
425        stats.quality_score = self.calculate_quality_score(&stats);
426
427        self.vector_stats = stats.clone();
428        Ok(stats)
429    }
430
431    /// Generate comprehensive optimization recommendations
432    pub fn generate_recommendations(&mut self) -> OptimizationRecommendations {
433        let mut recommendations = OptimizationRecommendations::default();
434
435        // Index recommendations
436        recommendations.index_recommendations = self.generate_index_recommendations();
437
438        // Caching recommendations
439        recommendations.caching_recommendations = self.generate_caching_recommendations();
440
441        // Query optimization recommendations
442        recommendations.query_recommendations = self.generate_query_recommendations();
443
444        // Hardware recommendations
445        recommendations.hardware_recommendations = self.generate_hardware_recommendations();
446
447        // Configuration recommendations
448        recommendations.configuration_recommendations =
449            self.generate_configuration_recommendations();
450
451        // Priority actions
452        recommendations.priority_actions = self.generate_priority_actions();
453
454        // Estimated improvements
455        recommendations.estimated_improvements = self.estimate_improvements(&recommendations);
456
457        self.recommendations = recommendations.clone();
458        recommendations
459    }
460
461    /// Export comprehensive performance report
462    pub fn export_performance_report(&self, format: ReportFormat) -> Result<String> {
463        match format {
464            ReportFormat::Json => {
465                let report = PerformanceReport {
466                    timestamp: SystemTime::now(),
467                    query_stats: self.query_stats.clone(),
468                    vector_stats: self.vector_stats.clone(),
469                    trends: self.trends.clone(),
470                    recommendations: self.recommendations.clone(),
471                    alerts: self.alerting_system.get_active_alerts(),
472                };
473                Ok(serde_json::to_string_pretty(&report)?)
474            }
475            ReportFormat::Csv => self.export_csv_report(),
476            ReportFormat::Html => self.export_html_report(),
477            ReportFormat::Prometheus => self.export_prometheus_metrics(),
478        }
479    }
480
481    // Private helper methods
482
483    fn update_latency_statistics(&mut self, latency_ms: f64) {
484        // Update rolling average
485        let alpha = 0.1; // Exponential moving average factor
486        if self.query_stats.total_queries == 1 {
487            self.query_stats.average_latency_ms = latency_ms;
488        } else {
489            self.query_stats.average_latency_ms =
490                alpha * latency_ms + (1.0 - alpha) * self.query_stats.average_latency_ms;
491        }
492
493        // Update percentiles (simplified implementation)
494        self.metrics_collector
495            .query_times
496            .push(Duration::from_secs_f64(latency_ms / 1000.0));
497        self.update_percentiles();
498    }
499
500    fn update_percentiles(&mut self) {
501        let mut times: Vec<f64> = self
502            .metrics_collector
503            .query_times
504            .iter()
505            .map(|d| d.as_secs_f64() * 1000.0)
506            .collect();
507        times.sort_by(|a, b| a.partial_cmp(b).unwrap());
508
509        if !times.is_empty() {
510            self.query_stats.p50_latency_ms = self.percentile(&times, 0.5);
511            self.query_stats.p95_latency_ms = self.percentile(&times, 0.95);
512            self.query_stats.p99_latency_ms = self.percentile(&times, 0.99);
513        }
514    }
515
516    fn percentile(&self, sorted_data: &[f64], p: f64) -> f64 {
517        if sorted_data.is_empty() {
518            return 0.0;
519        }
520        let index = (p * (sorted_data.len() - 1) as f64).round() as usize;
521        sorted_data[index.min(sorted_data.len() - 1)]
522    }
523
524    fn calculate_error_rate(&self) -> f64 {
525        let total_errors: u64 = self.metrics_collector.error_counts.values().sum();
526        if self.query_stats.total_queries > 0 {
527            total_errors as f64 / self.query_stats.total_queries as f64
528        } else {
529            0.0
530        }
531    }
532
533    fn check_performance_alerts(&mut self, latency_ms: f64) {
534        // Collect rules that need alerts to avoid borrowing conflict
535        let mut rules_to_alert = Vec::new();
536        for rule in &self.alerting_system.alert_rules {
537            if rule.enabled && self.evaluate_alert_rule(rule, latency_ms) {
538                rules_to_alert.push(rule.clone());
539            }
540        }
541
542        // Now trigger alerts for collected rules
543        for rule in rules_to_alert {
544            self.alerting_system.trigger_alert(&rule, latency_ms);
545        }
546    }
547
548    fn evaluate_alert_rule(&self, rule: &AlertRule, value: f64) -> bool {
549        match rule.comparison {
550            ComparisonOperator::GreaterThan => value > rule.threshold,
551            ComparisonOperator::LessThan => value < rule.threshold,
552            ComparisonOperator::Equals => (value - rule.threshold).abs() < f64::EPSILON,
553            ComparisonOperator::GreaterThanOrEqual => value >= rule.threshold,
554            ComparisonOperator::LessThanOrEqual => value <= rule.threshold,
555        }
556    }
557
558    fn calculate_vector_density(&self, vectors: &[(VectorId, Vector)]) -> (f64, f64) {
559        if vectors.is_empty() {
560            return (0.0, 0.0);
561        }
562
563        let mut total_non_zero = 0;
564        let mut total_elements = 0;
565
566        for (_, vector) in vectors {
567            let values = &vector.as_f32();
568            total_elements += values.len();
569            total_non_zero += values.iter().filter(|&&x| x != 0.0).count();
570        }
571
572        let density = total_non_zero as f64 / total_elements as f64;
573        let sparsity = 1.0 - density;
574        (density, sparsity)
575    }
576
577    fn calculate_clustering_coefficient(&self, _vectors: &[(VectorId, Vector)]) -> f64 {
578        // Simplified clustering coefficient calculation
579        // In a full implementation, this would analyze the k-NN graph
580        0.5 // Placeholder
581    }
582
583    fn calculate_hubness_measure(&self, _vectors: &[(VectorId, Vector)]) -> f64 {
584        // Simplified hubness measure
585        // Measures how often certain vectors appear in k-NN lists
586        0.3 // Placeholder
587    }
588
589    fn estimate_intrinsic_dimensionality(&self, _vectors: &[(VectorId, Vector)]) -> f64 {
590        // Simplified intrinsic dimensionality estimation
591        // Could use techniques like MLE or correlation dimension
592        if self.vector_stats.average_dimension > 0 {
593            self.vector_stats.average_dimension as f64 * 0.7
594        } else {
595            10.0
596        }
597    }
598
599    fn estimate_noise_level(&self, _vectors: &[(VectorId, Vector)]) -> f64 {
600        // Simplified noise estimation
601        0.1 // Placeholder - 10% noise level
602    }
603
604    fn calculate_quality_score(&self, stats: &VectorStatistics) -> f64 {
605        let density_score = stats.vector_density;
606        let clustering_score = stats.clustering_coefficient;
607        let noise_penalty = 1.0 - stats.noise_estimation;
608
609        (density_score + clustering_score + noise_penalty) / 3.0
610    }
611
612    fn generate_index_recommendations(&self) -> Vec<IndexRecommendation> {
613        let mut recommendations = Vec::new();
614
615        if self.query_stats.average_latency_ms > 10.0 {
616            recommendations.push(IndexRecommendation {
617                recommendation_type: "HNSW Optimization".to_string(),
618                description: "Consider optimizing HNSW parameters (M, efConstruction) for better search performance".to_string(),
619                estimated_improvement: 0.3,
620                implementation_effort: EffortLevel::Medium,
621                prerequisites: vec!["Performance profiling".to_string()],
622            });
623        }
624
625        if self.vector_stats.sparsity_ratio > 0.8 {
626            recommendations.push(IndexRecommendation {
627                recommendation_type: "Sparse Index".to_string(),
628                description:
629                    "High sparsity detected - consider using sparse-optimized index structures"
630                        .to_string(),
631                estimated_improvement: 0.4,
632                implementation_effort: EffortLevel::High,
633                prerequisites: vec!["Sparse vector support".to_string()],
634            });
635        }
636
637        recommendations
638    }
639
640    fn generate_caching_recommendations(&self) -> Vec<CachingRecommendation> {
641        let mut recommendations = Vec::new();
642
643        if self.query_stats.cache_hit_rate < 0.7 {
644            recommendations.push(CachingRecommendation {
645                cache_type: "Query Result Cache".to_string(),
646                recommended_size_mb: 512,
647                eviction_policy: "LRU".to_string(),
648                estimated_hit_rate: 0.85,
649                cost_benefit_ratio: 2.5,
650            });
651        }
652
653        recommendations
654    }
655
656    fn generate_query_recommendations(&self) -> Vec<QueryRecommendation> {
657        vec![QueryRecommendation {
658            query_pattern: "High-dimensional similarity search".to_string(),
659            optimization_technique: "Dimensionality reduction with PCA".to_string(),
660            expected_speedup: 1.5,
661            applicability: 0.8,
662        }]
663    }
664
665    fn generate_hardware_recommendations(&self) -> Vec<HardwareRecommendation> {
666        let mut recommendations = Vec::new();
667
668        if self.query_stats.memory_usage_mb > 8192.0 {
669            recommendations.push(HardwareRecommendation {
670                component: "Memory".to_string(),
671                current_bottleneck: true,
672                recommended_upgrade: "Increase RAM to 32GB+".to_string(),
673                performance_impact: 0.4,
674                cost_estimate: Some("$200-500".to_string()),
675            });
676        }
677
678        recommendations
679    }
680
681    fn generate_configuration_recommendations(&self) -> Vec<ConfigurationRecommendation> {
682        vec![ConfigurationRecommendation {
683            parameter: "thread_pool_size".to_string(),
684            current_value: "4".to_string(),
685            recommended_value: "8".to_string(),
686            justification: "CPU utilization suggests more threads could improve throughput"
687                .to_string(),
688            risk_level: RiskLevel::Low,
689        }]
690    }
691
692    fn generate_priority_actions(&self) -> Vec<PriorityAction> {
693        let mut actions = Vec::new();
694
695        if self.query_stats.average_latency_ms > 50.0 {
696            actions.push(PriorityAction {
697                action: "Optimize slow queries".to_string(),
698                priority: Priority::High,
699                estimated_impact: 0.6,
700                implementation_time: Duration::from_secs(3600 * 8), // 8 hours
701                dependencies: vec!["Query profiling".to_string()],
702            });
703        }
704
705        actions.sort_by(|a, b| b.priority.cmp(&a.priority));
706        actions
707    }
708
709    fn estimate_improvements(
710        &self,
711        _recommendations: &OptimizationRecommendations,
712    ) -> ImprovementEstimates {
713        ImprovementEstimates {
714            latency_improvement: 0.3,
715            throughput_improvement: 0.25,
716            memory_savings: 0.15,
717            cost_reduction: 0.2,
718            reliability_improvement: 0.1,
719        }
720    }
721
722    fn export_csv_report(&self) -> Result<String> {
723        let mut csv = String::new();
724        csv.push_str("Metric,Value,Unit\n");
725        csv.push_str(&format!(
726            "Total Queries,{},count\n",
727            self.query_stats.total_queries
728        ));
729        csv.push_str(&format!(
730            "Average Latency,{:.2},ms\n",
731            self.query_stats.average_latency_ms
732        ));
733        csv.push_str(&format!(
734            "P99 Latency,{:.2},ms\n",
735            self.query_stats.p99_latency_ms
736        ));
737        csv.push_str(&format!(
738            "Throughput,{:.2},QPS\n",
739            self.query_stats.throughput_qps
740        ));
741        csv.push_str(&format!(
742            "Error Rate,{:.4},ratio\n",
743            self.query_stats.error_rate
744        ));
745        csv.push_str(&format!(
746            "Cache Hit Rate,{:.4},ratio\n",
747            self.query_stats.cache_hit_rate
748        ));
749        Ok(csv)
750    }
751
752    fn export_html_report(&self) -> Result<String> {
753        let html = format!(
754            r#"
755            <!DOCTYPE html>
756            <html>
757            <head><title>OxiRS Vector Search Performance Report</title></head>
758            <body>
759                <h1>Performance Report</h1>
760                <h2>Query Statistics</h2>
761                <p>Total Queries: {}</p>
762                <p>Average Latency: {:.2} ms</p>
763                <p>P99 Latency: {:.2} ms</p>
764                <p>Throughput: {:.2} QPS</p>
765                <p>Error Rate: {:.4}</p>
766                <h2>Vector Statistics</h2>
767                <p>Total Vectors: {}</p>
768                <p>Average Dimension: {}</p>
769                <p>Vector Density: {:.4}</p>
770                <p>Quality Score: {:.4}</p>
771            </body>
772            </html>
773            "#,
774            self.query_stats.total_queries,
775            self.query_stats.average_latency_ms,
776            self.query_stats.p99_latency_ms,
777            self.query_stats.throughput_qps,
778            self.query_stats.error_rate,
779            self.vector_stats.total_vectors,
780            self.vector_stats.average_dimension,
781            self.vector_stats.vector_density,
782            self.vector_stats.quality_score,
783        );
784        Ok(html)
785    }
786
787    fn export_prometheus_metrics(&self) -> Result<String> {
788        let mut metrics = String::new();
789        metrics.push_str(&format!(
790            "oxirs_query_total {}\n",
791            self.query_stats.total_queries
792        ));
793        metrics.push_str(&format!(
794            "oxirs_query_latency_avg {}\n",
795            self.query_stats.average_latency_ms
796        ));
797        metrics.push_str(&format!(
798            "oxirs_query_latency_p99 {}\n",
799            self.query_stats.p99_latency_ms
800        ));
801        metrics.push_str(&format!(
802            "oxirs_query_throughput {}\n",
803            self.query_stats.throughput_qps
804        ));
805        metrics.push_str(&format!(
806            "oxirs_query_error_rate {}\n",
807            self.query_stats.error_rate
808        ));
809        metrics.push_str(&format!(
810            "oxirs_cache_hit_rate {}\n",
811            self.query_stats.cache_hit_rate
812        ));
813        metrics.push_str(&format!(
814            "oxirs_vector_total {}\n",
815            self.vector_stats.total_vectors
816        ));
817        metrics.push_str(&format!(
818            "oxirs_vector_quality_score {}\n",
819            self.vector_stats.quality_score
820        ));
821        Ok(metrics)
822    }
823}
824
825#[derive(Debug, Clone, Serialize, Deserialize)]
826pub struct PerformanceReport {
827    pub timestamp: SystemTime,
828    pub query_stats: QueryStatistics,
829    pub vector_stats: VectorStatistics,
830    pub trends: PerformanceTrends,
831    pub recommendations: OptimizationRecommendations,
832    pub alerts: Vec<ActiveAlert>,
833}
834
835#[derive(Debug, Clone, Copy)]
836pub enum ReportFormat {
837    Json,
838    Csv,
839    Html,
840    Prometheus,
841}
842
843impl Default for MetricsCollector {
844    fn default() -> Self {
845        Self::new()
846    }
847}
848
849impl MetricsCollector {
850    pub fn new() -> Self {
851        Self {
852            start_time: Instant::now(),
853            query_times: Vec::new(),
854            query_complexities: Vec::new(),
855            memory_samples: Vec::new(),
856            cpu_samples: Vec::new(),
857            cache_metrics: CacheMetrics::default(),
858            error_counts: HashMap::new(),
859            active_queries: 0,
860        }
861    }
862
863    pub fn record_query(&mut self, duration: Duration, complexity: QueryComplexity, success: bool) {
864        self.query_times.push(duration);
865        self.query_complexities.push(complexity);
866
867        if !success {
868            *self
869                .error_counts
870                .entry("query_error".to_string())
871                .or_insert(0) += 1;
872        }
873    }
874}
875
876impl Default for AlertingSystem {
877    fn default() -> Self {
878        Self::new()
879    }
880}
881
882impl AlertingSystem {
883    pub fn new() -> Self {
884        Self {
885            alert_rules: Self::default_alert_rules(),
886            active_alerts: Vec::new(),
887            alert_history: Vec::new(),
888            notification_channels: vec![NotificationChannel::Console],
889        }
890    }
891
892    fn default_alert_rules() -> Vec<AlertRule> {
893        vec![
894            AlertRule {
895                name: "High Latency".to_string(),
896                metric: "latency_p99".to_string(),
897                threshold: 100.0,
898                comparison: ComparisonOperator::GreaterThan,
899                severity: AlertSeverity::Warning,
900                enabled: true,
901            },
902            AlertRule {
903                name: "Critical Latency".to_string(),
904                metric: "latency_p99".to_string(),
905                threshold: 1000.0,
906                comparison: ComparisonOperator::GreaterThan,
907                severity: AlertSeverity::Critical,
908                enabled: true,
909            },
910        ]
911    }
912
913    pub fn trigger_alert(&mut self, rule: &AlertRule, value: f64) {
914        let alert = ActiveAlert {
915            rule_name: rule.name.clone(),
916            triggered_at: SystemTime::now(),
917            current_value: value,
918            threshold: rule.threshold,
919            severity: rule.severity,
920            acknowledged: false,
921        };
922
923        self.active_alerts.push(alert.clone());
924
925        let event = AlertEvent {
926            timestamp: SystemTime::now(),
927            alert_name: rule.name.clone(),
928            event_type: AlertEventType::Triggered,
929            details: format!(
930                "Alert triggered: {} = {:.2} > {:.2}",
931                rule.metric, value, rule.threshold
932            ),
933        };
934
935        self.alert_history.push(event);
936        self.send_notifications(&alert);
937    }
938
939    pub fn get_active_alerts(&self) -> Vec<ActiveAlert> {
940        self.active_alerts.clone()
941    }
942
943    fn send_notifications(&self, alert: &ActiveAlert) {
944        for channel in &self.notification_channels {
945            match channel {
946                NotificationChannel::Console => {
947                    warn!(
948                        "ALERT: {} - {} at {:.2} (threshold: {:.2})",
949                        alert.rule_name, alert.severity as u8, alert.current_value, alert.threshold
950                    );
951                }
952                NotificationChannel::Email(_) => {
953                    debug!(
954                        "Would send email notification for alert: {}",
955                        alert.rule_name
956                    );
957                }
958                NotificationChannel::Webhook(_) => {
959                    debug!(
960                        "Would send webhook notification for alert: {}",
961                        alert.rule_name
962                    );
963                }
964                NotificationChannel::Slack(_) => {
965                    debug!(
966                        "Would send Slack notification for alert: {}",
967                        alert.rule_name
968                    );
969                }
970            }
971        }
972    }
973}
974
975impl Default for PerformanceInsightsAnalyzer {
976    fn default() -> Self {
977        Self::new()
978    }
979}
980
981#[cfg(test)]
982mod tests {
983    use super::*;
984
985    #[test]
986    fn test_performance_insights_creation() {
987        let analyzer = PerformanceInsightsAnalyzer::new();
988        assert_eq!(analyzer.query_stats.total_queries, 0);
989        assert_eq!(analyzer.vector_stats.total_vectors, 0);
990    }
991
992    #[test]
993    fn test_query_recording() {
994        let mut analyzer = PerformanceInsightsAnalyzer::new();
995        analyzer.record_query(Duration::from_millis(50), QueryComplexity::Simple, true);
996
997        assert_eq!(analyzer.query_stats.total_queries, 1);
998        assert_eq!(analyzer.query_stats.average_latency_ms, 50.0);
999    }
1000
1001    #[test]
1002    fn test_vector_analysis() {
1003        let mut analyzer = PerformanceInsightsAnalyzer::new();
1004        let vectors = vec![
1005            ("vec1".to_string(), Vector::new(vec![1.0, 2.0, 3.0])),
1006            ("vec2".to_string(), Vector::new(vec![4.0, 5.0, 6.0])),
1007        ];
1008
1009        let stats = analyzer.analyze_vector_dataset(&vectors).unwrap();
1010        assert_eq!(stats.total_vectors, 2);
1011        assert_eq!(stats.average_dimension, 3);
1012    }
1013
1014    #[test]
1015    fn test_alert_generation() {
1016        let mut analyzer = PerformanceInsightsAnalyzer::new();
1017        // Record a slow query that should trigger an alert
1018        analyzer.record_query(Duration::from_millis(1500), QueryComplexity::Complex, true);
1019
1020        assert!(!analyzer.alerting_system.active_alerts.is_empty());
1021    }
1022
1023    #[test]
1024    fn test_recommendations_generation() {
1025        let mut analyzer = PerformanceInsightsAnalyzer::new();
1026        // Set up conditions that should trigger recommendations
1027        analyzer.query_stats.average_latency_ms = 100.0;
1028        analyzer.vector_stats.sparsity_ratio = 0.9;
1029
1030        let recommendations = analyzer.generate_recommendations();
1031        assert!(!recommendations.index_recommendations.is_empty());
1032    }
1033
1034    #[test]
1035    fn test_report_export() {
1036        let analyzer = PerformanceInsightsAnalyzer::new();
1037        let json_report = analyzer
1038            .export_performance_report(ReportFormat::Json)
1039            .unwrap();
1040        assert!(!json_report.is_empty());
1041
1042        let csv_report = analyzer
1043            .export_performance_report(ReportFormat::Csv)
1044            .unwrap();
1045        assert!(csv_report.contains("Metric,Value,Unit"));
1046    }
1047}