use serde::{Deserialize, Serialize};
use std::collections::{HashMap, VecDeque};
use std::time::{Duration, Instant};
use chrono::{DateTime, Utc};
use anyhow::{Result, Context as AnyhowContext};
use tracing::{debug, warn, error};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GateEvaluation {
pub gate_name: String,
pub passed: bool,
pub measured_value: f64,
pub threshold_value: f64,
pub deviation: f64,
pub statistical_significance: Option<StatisticalResult>,
pub timestamp: DateTime<Utc>,
pub evaluation_duration_us: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StatisticalResult {
pub p_value: f64,
pub confidence_level: f64,
pub is_significant: bool,
pub test_type: String,
pub sample_size: usize,
pub effect_size: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StatisticalValidation {
pub enabled: bool,
pub confidence_level: f64,
pub min_sample_size: usize,
pub max_lookback_minutes: u32,
pub effect_size_threshold: f64,
}
impl Default for StatisticalValidation {
fn default() -> Self {
Self {
enabled: true,
confidence_level: 0.95,
min_sample_size: 30,
max_lookback_minutes: 60, effect_size_threshold: 0.1, }
}
}
pub trait SlaGate {
fn evaluate(&self, measured_value: f64, context: &GateContext) -> Result<GateEvaluation>;
fn get_config(&self) -> &GateConfig;
fn get_name(&self) -> &str;
fn update_measurement(&mut self, value: f64, timestamp: DateTime<Utc>);
}
#[derive(Debug, Clone)]
pub struct GateContext {
pub slice_key: String,
pub timestamp: DateTime<Utc>,
pub baseline_value: Option<f64>,
pub historical_values: Vec<(DateTime<Utc>, f64)>,
pub metadata: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GateConfig {
pub name: String,
pub threshold: f64,
pub tolerance: f64,
pub statistical_validation: StatisticalValidation,
pub enable_trend_analysis: bool,
pub trend_window_size: usize,
}
#[derive(Debug, Clone)]
pub struct P99LatencyGate {
config: GateConfig,
measurements: VecDeque<(DateTime<Utc>, f64)>,
}
impl P99LatencyGate {
pub fn new(threshold: f64, tolerance: f64) -> Self {
Self {
config: GateConfig {
name: "p99_calibration_latency".to_string(),
threshold,
tolerance,
statistical_validation: StatisticalValidation::default(),
enable_trend_analysis: true,
trend_window_size: 100,
},
measurements: VecDeque::with_capacity(1000),
}
}
fn perform_latency_test(&self, current_value: f64, context: &GateContext) -> Option<StatisticalResult> {
if !self.config.statistical_validation.enabled {
return None;
}
if context.historical_values.len() < self.config.statistical_validation.min_sample_size {
return None;
}
let historical_latencies: Vec<f64> = context.historical_values
.iter()
.map(|(_, value)| *value)
.collect();
let historical_mean = historical_latencies.iter().sum::<f64>() / historical_latencies.len() as f64;
let historical_variance = historical_latencies
.iter()
.map(|x| (x - historical_mean).powi(2))
.sum::<f64>() / (historical_latencies.len() - 1) as f64;
let historical_std = historical_variance.sqrt();
let z_score = (current_value - historical_mean) / (historical_std / (historical_latencies.len() as f64).sqrt());
let p_value = 1.0 - standard_normal_cdf(z_score);
let is_significant = p_value < (1.0 - self.config.statistical_validation.confidence_level);
let effect_size = if historical_std > 0.0 {
Some((current_value - historical_mean) / historical_std)
} else {
None
};
Some(StatisticalResult {
p_value,
confidence_level: self.config.statistical_validation.confidence_level,
is_significant,
test_type: "one_tailed_t_test".to_string(),
sample_size: historical_latencies.len(),
effect_size,
})
}
}
impl SlaGate for P99LatencyGate {
fn evaluate(&self, measured_value: f64, context: &GateContext) -> Result<GateEvaluation> {
let start_time = Instant::now();
let effective_threshold = self.config.threshold + self.config.tolerance;
let passed = measured_value <= effective_threshold;
let deviation = measured_value - self.config.threshold;
let statistical_significance = self.perform_latency_test(measured_value, context);
let final_passed = if let Some(ref stats) = statistical_significance {
passed && !stats.is_significant
} else {
passed
};
let evaluation_duration = start_time.elapsed();
if evaluation_duration > Duration::from_micros(100) {
warn!("P99 latency gate evaluation exceeded 100μs target: {:?}", evaluation_duration);
}
Ok(GateEvaluation {
gate_name: self.config.name.clone(),
passed: final_passed,
measured_value,
threshold_value: self.config.threshold,
deviation,
statistical_significance,
timestamp: context.timestamp,
evaluation_duration_us: evaluation_duration.as_micros() as u64,
})
}
fn get_config(&self) -> &GateConfig {
&self.config
}
fn get_name(&self) -> &str {
&self.config.name
}
fn update_measurement(&mut self, value: f64, timestamp: DateTime<Utc>) {
self.measurements.push_back((timestamp, value));
if self.measurements.len() > self.config.trend_window_size {
self.measurements.pop_front();
}
}
}
#[derive(Debug, Clone)]
pub struct AeceTauGate {
config: GateConfig,
measurements: VecDeque<(DateTime<Utc>, f64)>,
}
impl AeceTauGate {
pub fn new(threshold: f64, tolerance: f64) -> Self {
Self {
config: GateConfig {
name: "aece_minus_tau".to_string(),
threshold,
tolerance,
statistical_validation: StatisticalValidation::default(),
enable_trend_analysis: true,
trend_window_size: 50,
},
measurements: VecDeque::with_capacity(500),
}
}
fn perform_aece_test(&self, current_value: f64, context: &GateContext) -> Option<StatisticalResult> {
if !self.config.statistical_validation.enabled {
return None;
}
if context.historical_values.len() < self.config.statistical_validation.min_sample_size {
return None;
}
let historical_values: Vec<f64> = context.historical_values
.iter()
.map(|(_, value)| *value)
.collect();
let historical_mean = historical_values.iter().sum::<f64>() / historical_values.len() as f64;
let historical_variance = historical_values
.iter()
.map(|x| (x - historical_mean).powi(2))
.sum::<f64>() / (historical_values.len() - 1) as f64;
let historical_std = historical_variance.sqrt();
let target_value = 0.0;
let t_statistic = if historical_std > 0.0 {
(current_value - target_value) / (historical_std / (historical_values.len() as f64).sqrt())
} else {
0.0
};
let p_value = 2.0 * (1.0 - standard_normal_cdf(t_statistic.abs()));
let is_significant = p_value < (1.0 - self.config.statistical_validation.confidence_level);
Some(StatisticalResult {
p_value,
confidence_level: self.config.statistical_validation.confidence_level,
is_significant,
test_type: "two_tailed_t_test".to_string(),
sample_size: historical_values.len(),
effect_size: Some(current_value.abs()),
})
}
}
impl SlaGate for AeceTauGate {
fn evaluate(&self, measured_value: f64, context: &GateContext) -> Result<GateEvaluation> {
let start_time = Instant::now();
let abs_value = measured_value.abs();
let effective_threshold = self.config.threshold + self.config.tolerance;
let passed = abs_value <= effective_threshold;
let deviation = abs_value - self.config.threshold;
let statistical_significance = self.perform_aece_test(measured_value, context);
let evaluation_duration = start_time.elapsed();
Ok(GateEvaluation {
gate_name: self.config.name.clone(),
passed,
measured_value,
threshold_value: self.config.threshold,
deviation,
statistical_significance,
timestamp: context.timestamp,
evaluation_duration_us: evaluation_duration.as_micros() as u64,
})
}
fn get_config(&self) -> &GateConfig {
&self.config
}
fn get_name(&self) -> &str {
&self.config.name
}
fn update_measurement(&mut self, value: f64, timestamp: DateTime<Utc>) {
self.measurements.push_back((timestamp, value));
if self.measurements.len() > self.config.trend_window_size {
self.measurements.pop_front();
}
}
}
#[derive(Debug, Clone)]
pub struct ConfidenceShiftGate {
config: GateConfig,
measurements: VecDeque<(DateTime<Utc>, f64)>,
}
impl ConfidenceShiftGate {
pub fn new(threshold: f64, tolerance: f64) -> Self {
Self {
config: GateConfig {
name: "median_confidence_shift".to_string(),
threshold,
tolerance,
statistical_validation: StatisticalValidation::default(),
enable_trend_analysis: true,
trend_window_size: 100,
},
measurements: VecDeque::with_capacity(1000),
}
}
fn perform_confidence_test(&self, current_value: f64, context: &GateContext) -> Option<StatisticalResult> {
if !self.config.statistical_validation.enabled {
return None;
}
if context.historical_values.len() < self.config.statistical_validation.min_sample_size {
return None;
}
let baseline = context.baseline_value.unwrap_or(0.0);
let effect_size = if baseline != 0.0 {
Some((current_value - baseline) / baseline)
} else {
Some(current_value)
};
let historical_shifts: Vec<f64> = context.historical_values
.iter()
.map(|(_, value)| *value - baseline)
.collect();
let current_shift = current_value - baseline;
let mean_historical_shift = historical_shifts.iter().sum::<f64>() / historical_shifts.len() as f64;
let std_historical_shift = if historical_shifts.len() > 1 {
let variance = historical_shifts
.iter()
.map(|x| (x - mean_historical_shift).powi(2))
.sum::<f64>() / (historical_shifts.len() - 1) as f64;
variance.sqrt()
} else {
0.1 };
let z_score = if std_historical_shift > 0.0 {
(current_shift - mean_historical_shift) / std_historical_shift
} else {
0.0
};
let p_value = 2.0 * (1.0 - standard_normal_cdf(z_score.abs()));
let is_significant = p_value < (1.0 - self.config.statistical_validation.confidence_level);
Some(StatisticalResult {
p_value,
confidence_level: self.config.statistical_validation.confidence_level,
is_significant,
test_type: "wilcoxon_signed_rank_approximation".to_string(),
sample_size: historical_shifts.len(),
effect_size,
})
}
}
impl SlaGate for ConfidenceShiftGate {
fn evaluate(&self, measured_value: f64, context: &GateContext) -> Result<GateEvaluation> {
let start_time = Instant::now();
let abs_shift = measured_value.abs();
let effective_threshold = self.config.threshold + self.config.tolerance;
let passed = abs_shift <= effective_threshold;
let deviation = abs_shift - self.config.threshold;
let statistical_significance = self.perform_confidence_test(measured_value, context);
let evaluation_duration = start_time.elapsed();
Ok(GateEvaluation {
gate_name: self.config.name.clone(),
passed,
measured_value,
threshold_value: self.config.threshold,
deviation,
statistical_significance,
timestamp: context.timestamp,
evaluation_duration_us: evaluation_duration.as_micros() as u64,
})
}
fn get_config(&self) -> &GateConfig {
&self.config
}
fn get_name(&self) -> &str {
&self.config.name
}
fn update_measurement(&mut self, value: f64, timestamp: DateTime<Utc>) {
self.measurements.push_back((timestamp, value));
if self.measurements.len() > self.config.trend_window_size {
self.measurements.pop_front();
}
}
}
#[derive(Debug, Clone)]
pub struct SlaRecallGate {
config: GateConfig,
measurements: VecDeque<(DateTime<Utc>, f64)>,
}
impl SlaRecallGate {
pub fn new(threshold: f64, tolerance: f64) -> Self {
Self {
config: GateConfig {
name: "sla_recall_at_50_change".to_string(),
threshold,
tolerance,
statistical_validation: StatisticalValidation {
enabled: true,
confidence_level: 0.99, min_sample_size: 100, max_lookback_minutes: 120, effect_size_threshold: 0.01, },
enable_trend_analysis: true,
trend_window_size: 200,
},
measurements: VecDeque::with_capacity(1000),
}
}
fn perform_recall_test(&self, current_value: f64, context: &GateContext) -> Option<StatisticalResult> {
if !self.config.statistical_validation.enabled {
return None;
}
if context.historical_values.len() < self.config.statistical_validation.min_sample_size {
return None;
}
let baseline = context.baseline_value.unwrap_or(0.0);
let change = current_value - baseline;
let strict_tolerance = 0.001; let is_significant = change.abs() > strict_tolerance;
let effect_size = Some(change.abs());
let p_value = if is_significant {
0.001 } else {
0.5 };
Some(StatisticalResult {
p_value,
confidence_level: self.config.statistical_validation.confidence_level,
is_significant,
test_type: "exact_binomial_test_approximation".to_string(),
sample_size: context.historical_values.len(),
effect_size,
})
}
}
impl SlaGate for SlaRecallGate {
fn evaluate(&self, measured_value: f64, context: &GateContext) -> Result<GateEvaluation> {
let start_time = Instant::now();
let abs_change = measured_value.abs();
let effective_threshold = self.config.tolerance; let passed = abs_change <= effective_threshold;
let deviation = abs_change - self.config.threshold;
let statistical_significance = self.perform_recall_test(measured_value, context);
let evaluation_duration = start_time.elapsed();
Ok(GateEvaluation {
gate_name: self.config.name.clone(),
passed,
measured_value,
threshold_value: self.config.threshold,
deviation,
statistical_significance,
timestamp: context.timestamp,
evaluation_duration_us: evaluation_duration.as_micros() as u64,
})
}
fn get_config(&self) -> &GateConfig {
&self.config
}
fn get_name(&self) -> &str {
&self.config.name
}
fn update_measurement(&mut self, value: f64, timestamp: DateTime<Utc>) {
self.measurements.push_back((timestamp, value));
if self.measurements.len() > self.config.trend_window_size {
self.measurements.pop_front();
}
}
}
pub struct SlaGateEvaluator {
gates: HashMap<String, Box<dyn SlaGate + Send + Sync>>,
evaluation_history: VecDeque<(DateTime<Utc>, HashMap<String, GateEvaluation>)>,
}
impl SlaGateEvaluator {
pub fn new() -> Self {
Self {
gates: HashMap::new(),
evaluation_history: VecDeque::with_capacity(1000),
}
}
pub fn add_gate(&mut self, gate: Box<dyn SlaGate + Send + Sync>) {
let name = gate.get_name().to_string();
self.gates.insert(name, gate);
}
pub fn evaluate_all_gates(
&mut self,
measurements: &HashMap<String, f64>,
context: &GateContext,
) -> Result<HashMap<String, GateEvaluation>> {
let start_time = Instant::now();
let mut results = HashMap::new();
for (gate_name, gate) in self.gates.iter() {
if let Some(measured_value) = measurements.get(gate_name) {
match gate.evaluate(*measured_value, context) {
Ok(evaluation) => {
results.insert(gate_name.clone(), evaluation);
}
Err(e) => {
error!("Failed to evaluate gate '{}': {}", gate_name, e);
}
}
}
}
self.evaluation_history.push_back((context.timestamp, results.clone()));
if self.evaluation_history.len() > 1000 {
self.evaluation_history.pop_front();
}
let total_duration = start_time.elapsed();
debug!("Evaluated {} gates in {:?}", results.len(), total_duration);
Ok(results)
}
pub fn get_evaluation_history(&self) -> &VecDeque<(DateTime<Utc>, HashMap<String, GateEvaluation>)> {
&self.evaluation_history
}
pub fn get_gate_summary(&self) -> HashMap<String, String> {
self.gates
.iter()
.map(|(name, gate)| {
let config = gate.get_config();
let summary = format!(
"threshold: {:.6}, tolerance: {:.6}, statistical_validation: {}",
config.threshold,
config.tolerance,
config.statistical_validation.enabled
);
(name.clone(), summary)
})
.collect()
}
}
fn standard_normal_cdf(z: f64) -> f64 {
let a1 = 0.254829592;
let a2 = -0.284496736;
let a3 = 1.421413741;
let a4 = -1.453152027;
let a5 = 1.061405429;
let p = 0.3275911;
let sign = if z >= 0.0 { 1.0 } else { -1.0 };
let x = z.abs() / (1.4142135623730951); let t = 1.0 / (1.0 + p * x);
let erf = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * (-x * x).exp();
0.5 * (1.0 + sign * erf)
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
#[test]
fn test_p99_latency_gate() {
let mut gate = P99LatencyGate::new(1000.0, 50.0);
let context = GateContext {
slice_key: "test:rust".to_string(),
timestamp: Utc::now(),
baseline_value: Some(500.0),
historical_values: vec![
(Utc::now(), 450.0),
(Utc::now(), 480.0),
(Utc::now(), 520.0),
],
metadata: HashMap::new(),
};
let result = gate.evaluate(800.0, &context);
assert!(result.is_ok());
let evaluation = result.unwrap();
assert!(evaluation.passed);
assert_eq!(evaluation.measured_value, 800.0);
assert_eq!(evaluation.threshold_value, 1000.0);
let result = gate.evaluate(1200.0, &context);
assert!(result.is_ok());
let evaluation = result.unwrap();
assert!(!evaluation.passed);
}
#[test]
fn test_aece_tau_gate() {
let mut gate = AeceTauGate::new(0.01, 0.005);
let context = GateContext {
slice_key: "test:python".to_string(),
timestamp: Utc::now(),
baseline_value: Some(0.0),
historical_values: vec![
(Utc::now(), -0.002),
(Utc::now(), 0.001),
(Utc::now(), 0.003),
],
metadata: HashMap::new(),
};
let result = gate.evaluate(0.008, &context);
assert!(result.is_ok());
let evaluation = result.unwrap();
assert!(evaluation.passed);
let result = gate.evaluate(0.02, &context);
assert!(result.is_ok());
let evaluation = result.unwrap();
assert!(!evaluation.passed);
}
#[test]
fn test_confidence_shift_gate() {
let mut gate = ConfidenceShiftGate::new(0.02, 0.005);
let context = GateContext {
slice_key: "test:go".to_string(),
timestamp: Utc::now(),
baseline_value: Some(0.75),
historical_values: vec![
(Utc::now(), 0.01),
(Utc::now(), -0.005),
(Utc::now(), 0.015),
],
metadata: HashMap::new(),
};
let result = gate.evaluate(0.018, &context);
assert!(result.is_ok());
let evaluation = result.unwrap();
assert!(evaluation.passed);
let result = gate.evaluate(0.03, &context);
assert!(result.is_ok());
let evaluation = result.unwrap();
assert!(!evaluation.passed);
}
#[test]
fn test_sla_recall_gate() {
let mut gate = SlaRecallGate::new(0.0, 0.001);
let context = GateContext {
slice_key: "test:java".to_string(),
timestamp: Utc::now(),
baseline_value: Some(0.85),
historical_values: vec![
(Utc::now(), 0.0005),
(Utc::now(), -0.0003),
(Utc::now(), 0.0001),
],
metadata: HashMap::new(),
};
let result = gate.evaluate(0.0008, &context);
assert!(result.is_ok());
let evaluation = result.unwrap();
assert!(evaluation.passed);
let result = gate.evaluate(0.005, &context);
assert!(result.is_ok());
let evaluation = result.unwrap();
assert!(!evaluation.passed);
}
#[test]
fn test_gate_evaluator() {
let mut evaluator = SlaGateEvaluator::new();
evaluator.add_gate(Box::new(P99LatencyGate::new(1000.0, 50.0)));
evaluator.add_gate(Box::new(AeceTauGate::new(0.01, 0.005)));
let measurements = [
("p99_calibration_latency".to_string(), 900.0),
("aece_minus_tau".to_string(), 0.008),
].iter().cloned().collect();
let context = GateContext {
slice_key: "test:comprehensive".to_string(),
timestamp: Utc::now(),
baseline_value: None,
historical_values: vec![],
metadata: HashMap::new(),
};
let results = evaluator.evaluate_all_gates(&measurements, &context);
assert!(results.is_ok());
let evaluations = results.unwrap();
assert_eq!(evaluations.len(), 2);
assert!(evaluations.contains_key("p99_calibration_latency"));
assert!(evaluations.contains_key("aece_minus_tau"));
}
#[test]
fn test_statistical_functions() {
let result = standard_normal_cdf(0.0);
assert!((result - 0.5).abs() < 0.01);
let result = standard_normal_cdf(1.96);
assert!((result - 0.975).abs() < 0.01);
let result = standard_normal_cdf(-1.96);
assert!((result - 0.025).abs() < 0.01);
}
}