Files
rustytorch/crates/data/rtx-data-validation/src/metrics.rs
T
2026-03-04 00:08:42 +00:00

1046 lines
32 KiB
Rust

//! Validation metrics collection and performance monitoring
//!
//! This module provides comprehensive metrics collection for validation operations
//! including performance metrics, success rates, error tracking, and system
//! resource utilization.
use std::collections::{HashMap, VecDeque};
use std::time::Duration;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
/// Main validation metrics collector
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationMetrics {
/// Performance metrics
pub performance: PerformanceMetrics,
/// Success and failure rates
pub success_rates: SuccessRateMetrics,
/// Error tracking metrics
pub error_metrics: ErrorMetrics,
/// Resource utilization metrics
pub resource_metrics: ResourceMetrics,
/// Throughput metrics
pub throughput_metrics: ThroughputMetrics,
/// Quality metrics
pub quality_metrics: QualityMetrics,
/// Historical metrics (last N measurements)
pub historical: HistoricalMetrics,
/// Metrics metadata
pub metadata: MetricsMetadata,
}
/// Performance-related metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceMetrics {
/// Total validation duration in milliseconds
pub validation_duration_ms: u64,
/// Average validation time per record
pub avg_validation_time_ms: f64,
/// 50th percentile validation time
pub p50_validation_time_ms: f64,
/// 95th percentile validation time
pub p95_validation_time_ms: f64,
/// 99th percentile validation time
pub p99_validation_time_ms: f64,
/// Maximum validation time observed
pub max_validation_time_ms: u64,
/// Minimum validation time observed
pub min_validation_time_ms: u64,
/// Number of validation rules applied
pub rule_count: usize,
/// Number of records processed
pub records_processed: usize,
/// Validation queue depth
pub queue_depth: usize,
/// Active validation count
pub active_validations: usize,
}
/// Success and failure rate metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SuccessRateMetrics {
/// Total number of validations
pub total_validations: usize,
/// Number of successful validations
pub successful_validations: usize,
/// Number of failed validations
pub failed_validations: usize,
/// Overall success rate (0.0 - 1.0)
pub success_rate: f64,
/// Success rate over last hour
pub hourly_success_rate: f64,
/// Success rate over last day
pub daily_success_rate: f64,
/// Success rate trend (increasing/decreasing)
pub success_rate_trend: TrendDirection,
/// First validation timestamp
pub first_validation: Option<DateTime<Utc>>,
/// Last validation timestamp
pub last_validation: Option<DateTime<Utc>>,
}
/// Error tracking metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorMetrics {
/// Total number of errors
pub total_errors: usize,
/// Errors by type
pub errors_by_type: HashMap<String, usize>,
/// Errors by severity
pub errors_by_severity: HashMap<String, usize>,
/// Recent error rate (errors per minute)
pub error_rate_per_minute: f64,
/// Error frequency over time
pub error_frequency: Vec<ErrorFrequencyPoint>,
/// Most common error types
pub top_error_types: Vec<(String, usize)>,
/// Error resolution time statistics
pub error_resolution_stats: ErrorResolutionStats,
}
/// Resource utilization metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceMetrics {
/// CPU utilization percentage
pub cpu_utilization: f64,
/// Memory usage in bytes
pub memory_usage_bytes: usize,
/// Peak memory usage
pub peak_memory_bytes: usize,
/// Memory utilization percentage
pub memory_utilization: f64,
/// I/O operations count
pub io_operations: usize,
/// Network I/O statistics
pub network_io: NetworkIoStats,
/// Disk I/O statistics
pub disk_io: DiskIoStats,
/// Thread pool utilization
pub thread_utilization: ThreadUtilization,
}
/// Throughput metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThroughputMetrics {
/// Records processed per second
pub records_per_second: f64,
/// Peak throughput achieved
pub peak_records_per_second: f64,
/// Average throughput over last hour
pub hourly_avg_throughput: f64,
/// Throughput trend
pub throughput_trend: TrendDirection,
/// Throughput percentiles
pub throughput_percentiles: ThroughputPercentiles,
/// Batch processing statistics
pub batch_stats: BatchStats,
/// Streaming processing statistics
pub streaming_stats: StreamingStats,
}
/// Data quality metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QualityMetrics {
/// Average data quality score
pub avg_quality_score: f64,
/// Quality score distribution
pub quality_distribution: QualityDistribution,
/// Quality trend over time
pub quality_trend: TrendDirection,
/// Quality metrics by dimension
pub quality_by_dimension: HashMap<String, f64>,
/// Anomaly detection statistics
pub anomaly_stats: AnomalyStats,
/// Schema compliance statistics
pub schema_compliance: SchemaComplianceStats,
}
/// Historical metrics storage
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HistoricalMetrics {
/// Maximum number of historical points to keep
pub max_history_points: usize,
/// Historical validation times
pub validation_times: VecDeque<f64>,
/// Historical success rates
pub success_rates: VecDeque<SuccessRatePoint>,
/// Historical throughput measurements
pub throughput_history: VecDeque<ThroughputPoint>,
/// Historical resource usage
pub resource_history: VecDeque<ResourceUsagePoint>,
/// Historical quality scores
pub quality_history: VecDeque<QualityPoint>,
}
/// Metadata about metrics collection
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MetricsMetadata {
/// When metrics collection started
pub collection_started: DateTime<Utc>,
/// Last metrics update timestamp
pub last_updated: DateTime<Utc>,
/// Metrics collection interval (seconds)
pub collection_interval_seconds: u64,
/// Metrics version
pub version: String,
/// Collector configuration
pub config: MetricsConfig,
}
/// Configuration for metrics collection
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MetricsConfig {
/// Enable detailed metrics collection
pub detailed_collection: bool,
/// Maximum history retention
pub max_history_days: u32,
/// Enable resource monitoring
pub monitor_resources: bool,
/// Enable quality tracking
pub track_quality: bool,
/// Metrics aggregation interval
pub aggregation_interval_seconds: u64,
}
/// Trend direction enumeration
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum TrendDirection {
/// Increasing trend
Increasing,
/// Decreasing trend
Decreasing,
/// Stable trend
Stable,
/// Volatile/unpredictable trend
Volatile,
/// Unknown/insufficient data
Unknown,
}
/// Error frequency point
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorFrequencyPoint {
/// Timestamp
pub timestamp: DateTime<Utc>,
/// Error count
pub count: usize,
/// Error types at this point
pub error_types: Vec<String>,
}
/// Error resolution statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorResolutionStats {
/// Average time to resolve errors (minutes)
pub avg_resolution_time_minutes: f64,
/// Number of unresolved errors
pub unresolved_errors: usize,
/// Error escalation rate
pub escalation_rate: f64,
/// Recovery success rate
pub recovery_success_rate: f64,
}
/// Network I/O statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkIoStats {
/// Bytes sent
pub bytes_sent: usize,
/// Bytes received
pub bytes_received: usize,
/// Packets sent
pub packets_sent: usize,
/// Packets received
pub packets_received: usize,
/// Network latency (milliseconds)
pub latency_ms: f64,
}
/// Disk I/O statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DiskIoStats {
/// Bytes read from disk
pub bytes_read: usize,
/// Bytes written to disk
pub bytes_written: usize,
/// Read operations count
pub read_operations: usize,
/// Write operations count
pub write_operations: usize,
/// Average I/O wait time
pub avg_io_wait_ms: f64,
}
/// Thread pool utilization
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThreadUtilization {
/// Active threads count
pub active_threads: usize,
/// Total thread pool size
pub total_threads: usize,
/// Thread utilization percentage
pub utilization_percentage: f64,
/// Average task queue length
pub avg_queue_length: f64,
}
/// Throughput percentiles
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThroughputPercentiles {
/// 50th percentile
pub p50: f64,
/// 75th percentile
pub p75: f64,
/// 90th percentile
pub p90: f64,
/// 95th percentile
pub p95: f64,
/// 99th percentile
pub p99: f64,
}
/// Batch processing statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchStats {
/// Total batches processed
pub total_batches: usize,
/// Average batch size
pub avg_batch_size: f64,
/// Average batch processing time
pub avg_batch_time_ms: f64,
/// Batch success rate
pub batch_success_rate: f64,
}
/// Streaming processing statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StreamingStats {
/// Current stream throughput
pub current_throughput: f64,
/// Stream backlog size
pub backlog_size: usize,
/// Stream latency (milliseconds)
pub stream_latency_ms: f64,
/// Buffer utilization
pub buffer_utilization: f64,
}
/// Quality score distribution
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QualityDistribution {
/// Excellent quality (>90%)
pub excellent_count: usize,
/// Good quality (70-90%)
pub good_count: usize,
/// Fair quality (50-70%)
pub fair_count: usize,
/// Poor quality (<50%)
pub poor_count: usize,
}
/// Anomaly detection statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnomalyStats {
/// Total anomalies detected
pub total_anomalies: usize,
/// Anomalies by type
pub anomalies_by_type: HashMap<String, usize>,
/// Anomaly detection rate
pub detection_rate: f64,
/// False positive rate
pub false_positive_rate: f64,
}
/// Schema compliance statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SchemaComplianceStats {
/// Overall compliance rate
pub compliance_rate: f64,
/// Schema violations count
pub violations_count: usize,
/// Compliance by field
pub field_compliance: HashMap<String, f64>,
}
/// Success rate measurement point
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SuccessRatePoint {
/// Measurement timestamp
pub timestamp: DateTime<Utc>,
/// Success rate at this point
pub rate: f64,
/// Sample size
pub sample_size: usize,
}
/// Throughput measurement point
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThroughputPoint {
/// Measurement timestamp
pub timestamp: DateTime<Utc>,
/// Throughput value
pub throughput: f64,
/// Measurement duration
pub duration_seconds: f64,
}
/// Resource usage measurement point
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceUsagePoint {
/// Measurement timestamp
pub timestamp: DateTime<Utc>,
/// CPU usage percentage
pub cpu_percent: f64,
/// Memory usage in bytes
pub memory_bytes: usize,
/// I/O operations count
pub io_ops: usize,
}
/// Quality measurement point
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QualityPoint {
/// Measurement timestamp
pub timestamp: DateTime<Utc>,
/// Quality score
pub score: f64,
/// Number of records in sample
pub sample_size: usize,
}
impl Default for ValidationMetrics {
fn default() -> Self {
Self::new()
}
}
impl ValidationMetrics {
/// Create a new metrics collector
pub fn new() -> Self {
let now = Utc::now();
Self {
performance: PerformanceMetrics::default(),
success_rates: SuccessRateMetrics::default(),
error_metrics: ErrorMetrics::default(),
resource_metrics: ResourceMetrics::default(),
throughput_metrics: ThroughputMetrics::default(),
quality_metrics: QualityMetrics::default(),
historical: HistoricalMetrics::new(1000), // Keep last 1000 points
metadata: MetricsMetadata {
collection_started: now,
last_updated: now,
collection_interval_seconds: 60,
version: "1.0.0".to_string(),
config: MetricsConfig::default(),
},
}
}
/// Record a validation operation
pub fn record_validation(&mut self, duration: Duration, success: bool) {
let duration_ms = duration.as_millis() as f64;
// Update performance metrics
self.performance.records_processed += 1;
self.performance.validation_duration_ms += duration.as_millis() as u64;
// Update running averages
let total_records = self.performance.records_processed as f64;
let prev_avg = self.performance.avg_validation_time_ms;
self.performance.avg_validation_time_ms =
prev_avg + (duration_ms - prev_avg) / total_records;
// Update min/max
if self.performance.records_processed == 1 {
self.performance.min_validation_time_ms = duration.as_millis() as u64;
self.performance.max_validation_time_ms = duration.as_millis() as u64;
} else {
self.performance.min_validation_time_ms = self
.performance
.min_validation_time_ms
.min(duration.as_millis() as u64);
self.performance.max_validation_time_ms = self
.performance
.max_validation_time_ms
.max(duration.as_millis() as u64);
}
// Update success rates
self.success_rates.total_validations += 1;
if success {
self.success_rates.successful_validations += 1;
} else {
self.success_rates.failed_validations += 1;
}
self.success_rates.success_rate = self.success_rates.successful_validations as f64
/ self.success_rates.total_validations as f64;
// Update timestamps
let now = Utc::now();
if self.success_rates.first_validation.is_none() {
self.success_rates.first_validation = Some(now);
}
self.success_rates.last_validation = Some(now);
// Update historical data
if self.historical.validation_times.len() >= self.historical.max_history_points {
self.historical.validation_times.pop_front();
}
self.historical.validation_times.push_back(duration_ms);
// Update metadata
self.metadata.last_updated = now;
// Calculate percentiles if we have enough data
if self.historical.validation_times.len() >= 10 {
self.calculate_percentiles();
}
}
/// Record an error
pub fn record_error(&mut self, error_type: &str, severity: &str) {
self.error_metrics.total_errors += 1;
*self
.error_metrics
.errors_by_type
.entry(error_type.to_string())
.or_insert(0) += 1;
*self
.error_metrics
.errors_by_severity
.entry(severity.to_string())
.or_insert(0) += 1;
// Update error frequency
let now = Utc::now();
self.error_metrics
.error_frequency
.push(ErrorFrequencyPoint {
timestamp: now,
count: 1,
error_types: vec![error_type.to_string()],
});
// Keep only recent error frequency data (last 1000 points)
if self.error_metrics.error_frequency.len() > 1000 {
self.error_metrics.error_frequency.remove(0);
}
// Calculate error rate (simplified)
let recent_errors = self
.error_metrics
.error_frequency
.iter()
.filter(|e| (now - e.timestamp).num_minutes() <= 1)
.map(|e| e.count)
.sum::<usize>();
self.error_metrics.error_rate_per_minute = recent_errors as f64;
// Update top error types
let mut error_types: Vec<_> = self
.error_metrics
.errors_by_type
.iter()
.map(|(k, v)| (k.clone(), *v))
.collect();
error_types.sort_by(|a, b| b.1.cmp(&a.1));
self.error_metrics.top_error_types = error_types.into_iter().take(10).collect();
}
/// Update resource metrics
pub fn update_resource_metrics(
&mut self,
cpu_percent: f64,
memory_bytes: usize,
io_ops: usize,
) {
self.resource_metrics.cpu_utilization = cpu_percent;
self.resource_metrics.memory_usage_bytes = memory_bytes;
self.resource_metrics.peak_memory_bytes =
self.resource_metrics.peak_memory_bytes.max(memory_bytes);
self.resource_metrics.io_operations += io_ops;
// Add to historical data
let point = ResourceUsagePoint {
timestamp: Utc::now(),
cpu_percent,
memory_bytes,
io_ops,
};
if self.historical.resource_history.len() >= self.historical.max_history_points {
self.historical.resource_history.pop_front();
}
self.historical.resource_history.push_back(point);
}
/// Calculate throughput metrics
pub fn calculate_throughput(&mut self, time_window_seconds: f64) {
if time_window_seconds <= 0.0 || self.performance.records_processed == 0 {
return;
}
let records_per_second = self.performance.records_processed as f64 / time_window_seconds;
self.throughput_metrics.records_per_second = records_per_second;
self.throughput_metrics.peak_records_per_second = self
.throughput_metrics
.peak_records_per_second
.max(records_per_second);
// Add throughput point to history
let point = ThroughputPoint {
timestamp: Utc::now(),
throughput: records_per_second,
duration_seconds: time_window_seconds,
};
if self.historical.throughput_history.len() >= self.historical.max_history_points {
self.historical.throughput_history.pop_front();
}
self.historical.throughput_history.push_back(point);
// Calculate trend (simplified)
if self.historical.throughput_history.len() >= 5 {
let recent: Vec<f64> = self
.historical
.throughput_history
.iter()
.rev()
.take(5)
.map(|p| p.throughput)
.collect();
self.throughput_metrics.throughput_trend = self.calculate_trend(&recent);
}
}
/// Update quality metrics
pub fn update_quality_metrics(&mut self, quality_score: f64, sample_size: usize) {
// Update running average
let prev_total = self
.quality_metrics
.quality_by_dimension
.values()
.sum::<f64>();
let count = self.quality_metrics.quality_by_dimension.len() as f64;
if count == 0.0 {
self.quality_metrics.avg_quality_score = quality_score;
} else {
self.quality_metrics.avg_quality_score = (prev_total + quality_score) / (count + 1.0);
}
// Update quality distribution
match quality_score {
s if s >= 0.9 => self.quality_metrics.quality_distribution.excellent_count += 1,
s if s >= 0.7 => self.quality_metrics.quality_distribution.good_count += 1,
s if s >= 0.5 => self.quality_metrics.quality_distribution.fair_count += 1,
_ => self.quality_metrics.quality_distribution.poor_count += 1,
}
// Add to historical data
let point = QualityPoint {
timestamp: Utc::now(),
score: quality_score,
sample_size,
};
if self.historical.quality_history.len() >= self.historical.max_history_points {
self.historical.quality_history.pop_front();
}
self.historical.quality_history.push_back(point);
}
/// Get current metrics summary
pub fn summary(&self) -> MetricsSummary {
MetricsSummary {
total_validations: self.success_rates.total_validations,
success_rate: self.success_rates.success_rate,
avg_validation_time_ms: self.performance.avg_validation_time_ms,
current_throughput: self.throughput_metrics.records_per_second,
error_rate: self.error_metrics.error_rate_per_minute,
avg_quality_score: self.quality_metrics.avg_quality_score,
cpu_utilization: self.resource_metrics.cpu_utilization,
memory_usage_mb: self.resource_metrics.memory_usage_bytes as f64 / 1_000_000.0,
last_updated: self.metadata.last_updated,
}
}
/// Reset all metrics
pub fn reset(&mut self) {
*self = Self::new();
}
/// Export metrics to JSON
pub fn to_json(&self) -> Result<String, serde_json::Error> {
serde_json::to_string_pretty(self)
}
fn calculate_percentiles(&mut self) {
let mut times: Vec<f64> = self.historical.validation_times.iter().copied().collect();
times.sort_by(|a, b| a.total_cmp(b));
if !times.is_empty() {
let len = times.len();
self.performance.p50_validation_time_ms = times[len * 50 / 100];
self.performance.p95_validation_time_ms = times[len * 95 / 100];
self.performance.p99_validation_time_ms = times[len * 99 / 100];
}
}
fn calculate_trend(&self, values: &[f64]) -> TrendDirection {
if values.len() < 3 {
return TrendDirection::Unknown;
}
// Simple trend calculation based on linear regression slope
let n = values.len() as f64;
let sum_x = (0..values.len()).sum::<usize>() as f64;
let sum_y = values.iter().sum::<f64>();
let sum_xy = values
.iter()
.enumerate()
.map(|(i, &y)| i as f64 * y)
.sum::<f64>();
let sum_x2 = (0..values.len()).map(|i| (i * i) as f64).sum::<f64>();
let slope = (n * sum_xy - sum_x * sum_y) / (n * sum_x2 - sum_x * sum_x);
match slope {
s if s > 0.1 => TrendDirection::Increasing,
s if s < -0.1 => TrendDirection::Decreasing,
s if s.abs() <= 0.1 => TrendDirection::Stable,
_ => TrendDirection::Volatile,
}
}
}
/// Condensed metrics summary
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MetricsSummary {
/// Total validations performed
pub total_validations: usize,
/// Overall success rate
pub success_rate: f64,
/// Average validation time
pub avg_validation_time_ms: f64,
/// Current throughput
pub current_throughput: f64,
/// Current error rate
pub error_rate: f64,
/// Average quality score
pub avg_quality_score: f64,
/// CPU utilization
pub cpu_utilization: f64,
/// Memory usage in MB
pub memory_usage_mb: f64,
/// Last update timestamp
pub last_updated: DateTime<Utc>,
}
impl Default for PerformanceMetrics {
fn default() -> Self {
Self {
validation_duration_ms: 0,
avg_validation_time_ms: 0.0,
p50_validation_time_ms: 0.0,
p95_validation_time_ms: 0.0,
p99_validation_time_ms: 0.0,
max_validation_time_ms: 0,
min_validation_time_ms: 0,
rule_count: 0,
records_processed: 0,
queue_depth: 0,
active_validations: 0,
}
}
}
impl Default for SuccessRateMetrics {
fn default() -> Self {
Self {
total_validations: 0,
successful_validations: 0,
failed_validations: 0,
success_rate: 0.0,
hourly_success_rate: 0.0,
daily_success_rate: 0.0,
success_rate_trend: TrendDirection::Unknown,
first_validation: None,
last_validation: None,
}
}
}
impl Default for ErrorMetrics {
fn default() -> Self {
Self {
total_errors: 0,
errors_by_type: HashMap::new(),
errors_by_severity: HashMap::new(),
error_rate_per_minute: 0.0,
error_frequency: Vec::new(),
top_error_types: Vec::new(),
error_resolution_stats: ErrorResolutionStats {
avg_resolution_time_minutes: 0.0,
unresolved_errors: 0,
escalation_rate: 0.0,
recovery_success_rate: 0.0,
},
}
}
}
impl Default for ResourceMetrics {
fn default() -> Self {
Self {
cpu_utilization: 0.0,
memory_usage_bytes: 0,
peak_memory_bytes: 0,
memory_utilization: 0.0,
io_operations: 0,
network_io: NetworkIoStats {
bytes_sent: 0,
bytes_received: 0,
packets_sent: 0,
packets_received: 0,
latency_ms: 0.0,
},
disk_io: DiskIoStats {
bytes_read: 0,
bytes_written: 0,
read_operations: 0,
write_operations: 0,
avg_io_wait_ms: 0.0,
},
thread_utilization: ThreadUtilization {
active_threads: 0,
total_threads: 0,
utilization_percentage: 0.0,
avg_queue_length: 0.0,
},
}
}
}
impl Default for ThroughputMetrics {
fn default() -> Self {
Self {
records_per_second: 0.0,
peak_records_per_second: 0.0,
hourly_avg_throughput: 0.0,
throughput_trend: TrendDirection::Unknown,
throughput_percentiles: ThroughputPercentiles {
p50: 0.0,
p75: 0.0,
p90: 0.0,
p95: 0.0,
p99: 0.0,
},
batch_stats: BatchStats {
total_batches: 0,
avg_batch_size: 0.0,
avg_batch_time_ms: 0.0,
batch_success_rate: 0.0,
},
streaming_stats: StreamingStats {
current_throughput: 0.0,
backlog_size: 0,
stream_latency_ms: 0.0,
buffer_utilization: 0.0,
},
}
}
}
impl Default for QualityMetrics {
fn default() -> Self {
Self {
avg_quality_score: 0.0,
quality_distribution: QualityDistribution {
excellent_count: 0,
good_count: 0,
fair_count: 0,
poor_count: 0,
},
quality_trend: TrendDirection::Unknown,
quality_by_dimension: HashMap::new(),
anomaly_stats: AnomalyStats {
total_anomalies: 0,
anomalies_by_type: HashMap::new(),
detection_rate: 0.0,
false_positive_rate: 0.0,
},
schema_compliance: SchemaComplianceStats {
compliance_rate: 0.0,
violations_count: 0,
field_compliance: HashMap::new(),
},
}
}
}
impl Default for MetricsConfig {
fn default() -> Self {
Self {
detailed_collection: true,
max_history_days: 30,
monitor_resources: true,
track_quality: true,
aggregation_interval_seconds: 60,
}
}
}
impl HistoricalMetrics {
fn new(max_points: usize) -> Self {
Self {
max_history_points: max_points,
validation_times: VecDeque::new(),
success_rates: VecDeque::new(),
throughput_history: VecDeque::new(),
resource_history: VecDeque::new(),
quality_history: VecDeque::new(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
#[test]
fn test_metrics_creation() {
let metrics = ValidationMetrics::new();
assert_eq!(metrics.success_rates.total_validations, 0);
assert_eq!(metrics.performance.records_processed, 0);
}
#[test]
fn test_record_validation() {
let mut metrics = ValidationMetrics::new();
// Record successful validation
metrics.record_validation(Duration::from_millis(100), true);
assert_eq!(metrics.success_rates.total_validations, 1);
assert_eq!(metrics.success_rates.successful_validations, 1);
assert_eq!(metrics.success_rates.success_rate, 1.0);
assert_eq!(metrics.performance.avg_validation_time_ms, 100.0);
}
#[test]
fn test_record_error() {
let mut metrics = ValidationMetrics::new();
metrics.record_error("validation_error", "critical");
assert_eq!(metrics.error_metrics.total_errors, 1);
assert_eq!(
metrics.error_metrics.errors_by_type.get("validation_error"),
Some(&1)
);
assert_eq!(
metrics.error_metrics.errors_by_severity.get("critical"),
Some(&1)
);
}
#[test]
fn test_success_rate_calculation() {
let mut metrics = ValidationMetrics::new();
// Record mixed results
metrics.record_validation(Duration::from_millis(50), true);
metrics.record_validation(Duration::from_millis(75), false);
metrics.record_validation(Duration::from_millis(25), true);
assert_eq!(metrics.success_rates.total_validations, 3);
assert_eq!(metrics.success_rates.successful_validations, 2);
assert_eq!(metrics.success_rates.failed_validations, 1);
assert_eq!(metrics.success_rates.success_rate, 2.0 / 3.0);
}
#[test]
fn test_throughput_calculation() {
let mut metrics = ValidationMetrics::new();
// Record some validations
metrics.record_validation(Duration::from_millis(100), true);
metrics.record_validation(Duration::from_millis(150), true);
// Calculate throughput over 2 seconds
metrics.calculate_throughput(2.0);
assert_eq!(metrics.throughput_metrics.records_per_second, 1.0);
}
#[test]
fn test_quality_metrics_update() {
let mut metrics = ValidationMetrics::new();
metrics.update_quality_metrics(0.85, 100);
assert_eq!(metrics.quality_metrics.avg_quality_score, 0.85);
assert_eq!(metrics.quality_metrics.quality_distribution.good_count, 1);
}
#[test]
fn test_trend_direction() {
let metrics = ValidationMetrics::new();
// Test increasing trend
let increasing = vec![1.0, 2.0, 3.0, 4.0, 5.0];
assert_eq!(
metrics.calculate_trend(&increasing),
TrendDirection::Increasing
);
// Test decreasing trend
let decreasing = vec![5.0, 4.0, 3.0, 2.0, 1.0];
assert_eq!(
metrics.calculate_trend(&decreasing),
TrendDirection::Decreasing
);
// Test stable trend
let stable = vec![3.0, 3.0, 3.0, 3.0, 3.0];
assert_eq!(metrics.calculate_trend(&stable), TrendDirection::Stable);
}
#[test]
fn test_metrics_summary() {
let mut metrics = ValidationMetrics::new();
metrics.record_validation(Duration::from_millis(100), true);
metrics.update_quality_metrics(0.9, 50);
let summary = metrics.summary();
assert_eq!(summary.total_validations, 1);
assert_eq!(summary.success_rate, 1.0);
assert_eq!(summary.avg_quality_score, 0.9);
}
#[test]
fn test_metrics_reset() {
let mut metrics = ValidationMetrics::new();
metrics.record_validation(Duration::from_millis(100), true);
metrics.record_error("test_error", "warning");
assert_eq!(metrics.success_rates.total_validations, 1);
assert_eq!(metrics.error_metrics.total_errors, 1);
metrics.reset();
assert_eq!(metrics.success_rates.total_validations, 0);
assert_eq!(metrics.error_metrics.total_errors, 0);
}
}