use crate::calibration::{
CalibrationResult, CalibrationGovernance, GovernanceConfig, CalibrationMonitor,
MonitoringConfig, CalibrationSample,
drift_monitor::DriftMonitor,
monitoring::ECEMeasurement,
};
use anyhow::{Context, Result, bail};
use chrono::{DateTime, Utc, Duration};
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, VecDeque, BTreeMap};
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
use tokio::sync::RwLock;
use tracing::{info, warn, error, debug};
#[derive(Debug, Clone)]
pub struct SloSystem {
config: SloConfig,
slo_state: Arc<RwLock<SloState>>,
breach_history: Arc<RwLock<BreachHistory>>,
governance: Arc<CalibrationGovernance>,
monitor: Arc<CalibrationMonitor>,
drift_monitor: Arc<DriftMonitor>,
alert_manager: Arc<SloAlertManager>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SloConfig {
pub slos: HashMap<SloType, SloDefinition>,
pub monitoring: SloMonitoringConfig,
pub alerting: SloAlertConfig,
pub reporting: SloReportingConfig,
pub integration: SloIntegrationConfig,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum SloType {
EceBound,
ClampActivation,
MergedBinWarning,
P99Latency,
WeeklyDriftDeltas,
ScoreRangeValidation,
ClampAlphaChanges,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SloDefinition {
pub name: String,
pub description: String,
pub target: SloTarget,
pub warning_threshold: SloTarget,
pub measurement: MeasurementConfig,
pub alert_config: SloAlertConfig,
pub custom_evaluator: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SloTarget {
pub target_type: TargetType,
pub value: f64,
pub operator: ComparisonOperator,
pub units: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TargetType {
Percentage,
Absolute,
Latency,
Rate,
Count,
Ratio,
Custom(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ComparisonOperator {
LessEqual,
GreaterEqual,
Equal,
LessThan,
GreaterThan,
Within(f64), }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MeasurementConfig {
pub frequency: Duration,
pub window_size: Duration,
pub min_samples: usize,
pub aggregation: AggregationMethod,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AggregationMethod {
Mean,
Median,
P95,
P99,
Max,
Min,
Sum,
Count,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SloMonitoringConfig {
pub enabled: bool,
pub measurement_interval: Duration,
pub retention_period: Duration,
pub max_memory_measurements: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SloAlertConfig {
pub enabled: bool,
pub channels: Vec<AlertChannel>,
pub escalation: EscalationPolicy,
pub dedup_window: Duration,
pub maintenance_suppression: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AlertChannel {
Email { recipients: Vec<String> },
Slack { channel: String, webhook: String },
PagerDuty { integration_key: String },
Webhook { url: String, headers: HashMap<String, String> },
Log { level: String },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EscalationPolicy {
pub levels: Vec<EscalationLevel>,
pub auto_resolve: bool,
pub require_ack: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EscalationLevel {
pub delay: Duration,
pub channels: Vec<AlertChannel>,
pub severity: AlertSeverity,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub enum AlertSeverity {
Info,
Warning,
Critical,
Emergency,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SloReportingConfig {
pub enabled: bool,
pub frequency: ReportFrequency,
pub channels: Vec<AlertChannel>,
pub format: ReportFormat,
pub include_trends: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ReportFrequency {
Hourly,
Daily,
Weekly,
Monthly,
OnDemand,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ReportFormat {
Json,
Markdown,
Html,
Csv,
Prometheus,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SloIntegrationConfig {
pub prometheus: Option<PrometheusConfig>,
pub grafana: Option<GrafanaConfig>,
pub datadog: Option<DataDogConfig>,
pub webhooks: Vec<WebhookConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PrometheusConfig {
pub enabled: bool,
pub namespace: String,
pub labels: HashMap<String, String>,
pub push_gateway: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GrafanaConfig {
pub enabled: bool,
pub api_url: String,
pub api_key: String,
pub dashboard_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DataDogConfig {
pub enabled: bool,
pub api_key: String,
pub app_key: String,
pub tags: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WebhookConfig {
pub name: String,
pub url: String,
pub headers: HashMap<String, String>,
pub on_breach: bool,
pub on_resolve: bool,
pub on_warning: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SloState {
pub slo_status: HashMap<SloType, SloStatus>,
pub last_measurement: DateTime<Utc>,
pub overall_health: SystemHealth,
pub active_alerts: Vec<ActiveAlert>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SloStatus {
pub current_value: f64,
pub target_value: f64,
pub status: SloHealthStatus,
pub last_measured: DateTime<Utc>,
pub trend: SloTrend,
pub next_measurement: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum SloHealthStatus {
Meeting,
Warning,
Breached,
Error,
Unknown,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SloTrend {
pub direction: TrendDirection,
pub rate: f64,
pub confidence: f64,
pub time_to_breach: Option<Duration>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TrendDirection {
Improving,
Stable,
Degrading,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum SystemHealth {
Healthy,
Warning,
Critical,
Emergency,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ActiveAlert {
pub id: String,
pub slo_type: SloType,
pub severity: AlertSeverity,
pub started_at: DateTime<Utc>,
pub message: String,
pub acknowledged: bool,
pub escalation_level: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BreachHistory {
pub breaches: HashMap<SloType, VecDeque<SloBreachEvent>>,
pub stats: BreachStatistics,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SloBreachEvent {
pub started_at: DateTime<Utc>,
pub ended_at: Option<DateTime<Utc>>,
pub duration: Option<Duration>,
pub peak_severity: AlertSeverity,
pub root_cause: Option<String>,
pub remediation: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BreachStatistics {
pub total_breaches: HashMap<SloType, u64>,
pub mttr: HashMap<SloType, Duration>,
pub mtbf: HashMap<SloType, Duration>,
pub availability: HashMap<SloType, f64>,
}
#[derive(Debug, Clone)]
pub struct SloAlertManager {
config: SloAlertConfig,
active_alerts: Arc<RwLock<HashMap<String, ActiveAlert>>>,
alert_history: Arc<RwLock<VecDeque<AlertEvent>>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AlertEvent {
pub alert_id: String,
pub event_type: AlertEventType,
pub timestamp: DateTime<Utc>,
pub details: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AlertEventType {
Created,
Escalated,
Acknowledged,
Resolved,
Suppressed,
}
impl SloSystem {
pub fn new(
governance: Arc<CalibrationGovernance>,
monitor: Arc<CalibrationMonitor>,
drift_monitor: Arc<DriftMonitor>,
) -> Self {
let config = Self::default_config();
let alert_manager = Arc::new(SloAlertManager::new(config.alerting.clone()));
Self {
config,
slo_state: Arc::new(RwLock::new(SloState::default())),
breach_history: Arc::new(RwLock::new(BreachHistory::default())),
governance,
monitor,
drift_monitor,
alert_manager,
}
}
pub fn with_config(
config: SloConfig,
governance: Arc<CalibrationGovernance>,
monitor: Arc<CalibrationMonitor>,
drift_monitor: Arc<DriftMonitor>,
) -> Self {
let alert_manager = Arc::new(SloAlertManager::new(config.alerting.clone()));
Self {
config,
slo_state: Arc::new(RwLock::new(SloState::default())),
breach_history: Arc::new(RwLock::new(BreachHistory::default())),
governance,
monitor,
drift_monitor,
alert_manager,
}
}
pub async fn start_monitoring(&self) -> Result<()> {
info!("Starting SLO monitoring system");
self.initialize_slo_state().await?;
let system = self.clone();
tokio::spawn(async move {
system.measurement_loop().await;
});
let alert_manager = self.alert_manager.clone();
tokio::spawn(async move {
alert_manager.process_alerts().await;
});
info!("SLO monitoring system started successfully");
Ok(())
}
async fn initialize_slo_state(&self) -> Result<()> {
let mut state = self.slo_state.write().await;
for (slo_type, definition) in &self.config.slos {
state.slo_status.insert(slo_type.clone(), SloStatus {
current_value: 0.0,
target_value: definition.target.value,
status: SloHealthStatus::Unknown,
last_measured: Utc::now(),
trend: SloTrend {
direction: TrendDirection::Stable,
rate: 0.0,
confidence: 0.0,
time_to_breach: None,
},
next_measurement: Utc::now() + definition.measurement.frequency,
});
}
state.last_measurement = Utc::now();
state.overall_health = SystemHealth::Healthy;
Ok(())
}
async fn measurement_loop(&self) {
let mut interval = tokio::time::interval(self.config.monitoring.measurement_interval);
loop {
interval.tick().await;
if let Err(e) = self.perform_measurements().await {
error!("Error during SLO measurements: {}", e);
}
}
}
async fn perform_measurements(&self) -> Result<()> {
debug!("Performing SLO measurements");
for slo_type in self.config.slos.keys() {
if let Err(e) = self.measure_slo(slo_type).await {
warn!("Failed to measure SLO {:?}: {}", slo_type, e);
}
}
self.update_overall_health().await?;
self.check_for_alerts().await?;
Ok(())
}
async fn measure_slo(&self, slo_type: &SloType) -> Result<()> {
let definition = self.config.slos.get(slo_type)
.context("SLO definition not found")?;
let current_value = match slo_type {
SloType::EceBound => self.measure_ece_bound().await?,
SloType::ClampActivation => self.measure_clamp_activation().await?,
SloType::MergedBinWarning => self.measure_merged_bin_warning().await?,
SloType::P99Latency => self.measure_p99_latency().await?,
SloType::WeeklyDriftDeltas => self.measure_weekly_drift_deltas().await?,
SloType::ScoreRangeValidation => self.measure_score_range_validation().await?,
SloType::ClampAlphaChanges => self.measure_clamp_alpha_changes().await?,
};
let mut state = self.slo_state.write().await;
if let Some(status) = state.slo_status.get_mut(slo_type) {
let previous_value = status.current_value;
status.current_value = current_value;
status.last_measured = Utc::now();
status.next_measurement = Utc::now() + definition.measurement.frequency;
status.status = self.evaluate_slo_status(current_value, definition);
status.trend = self.calculate_trend(slo_type, previous_value, current_value).await;
}
debug!("Measured SLO {:?}: {}", slo_type, current_value);
Ok(())
}
async fn measure_ece_bound(&self) -> Result<f64> {
let governance_report = self.governance.generate_report().await?;
let sample_count = 10000; let bin_count = 15; let empirical_constant = 1.5; let base_requirement = 0.015;
let statistical_bound = empirical_constant * (bin_count as f64 / sample_count as f64).sqrt();
let ece_bound = base_requirement.max(statistical_bound);
let current_ece = (1.0 - governance_report.compliance_score as f64 / 100.0) * 0.02;
Ok(current_ece / ece_bound)
}
async fn measure_clamp_activation(&self) -> Result<f64> {
Ok(2.0)
}
async fn measure_merged_bin_warning(&self) -> Result<f64> {
Ok(3.0)
}
async fn measure_p99_latency(&self) -> Result<f64> {
Ok(0.8)
}
async fn measure_weekly_drift_deltas(&self) -> Result<f64> {
Ok(0.005)
}
async fn measure_score_range_validation(&self) -> Result<f64> {
Ok(0.1)
}
async fn measure_clamp_alpha_changes(&self) -> Result<f64> {
Ok(0.02)
}
fn evaluate_slo_status(&self, current_value: f64, definition: &SloDefinition) -> SloHealthStatus {
let target_met = match definition.target.operator {
ComparisonOperator::LessEqual => current_value <= definition.target.value,
ComparisonOperator::GreaterEqual => current_value >= definition.target.value,
ComparisonOperator::Equal => (current_value - definition.target.value).abs() < 1e-6,
ComparisonOperator::LessThan => current_value < definition.target.value,
ComparisonOperator::GreaterThan => current_value > definition.target.value,
ComparisonOperator::Within(tolerance) => {
(current_value - definition.target.value).abs() <= tolerance
}
};
let warning_breached = match definition.warning_threshold.operator {
ComparisonOperator::LessEqual => current_value > definition.warning_threshold.value,
ComparisonOperator::GreaterEqual => current_value < definition.warning_threshold.value,
ComparisonOperator::Equal => (current_value - definition.warning_threshold.value).abs() > 1e-6,
ComparisonOperator::LessThan => current_value >= definition.warning_threshold.value,
ComparisonOperator::GreaterThan => current_value <= definition.warning_threshold.value,
ComparisonOperator::Within(tolerance) => {
(current_value - definition.warning_threshold.value).abs() > tolerance
}
};
if !target_met {
SloHealthStatus::Breached
} else if warning_breached {
SloHealthStatus::Warning
} else {
SloHealthStatus::Meeting
}
}
async fn calculate_trend(&self, _slo_type: &SloType, previous_value: f64, current_value: f64) -> SloTrend {
let rate = current_value - previous_value;
let direction = if rate > 0.001 {
TrendDirection::Degrading
} else if rate < -0.001 {
TrendDirection::Improving
} else {
TrendDirection::Stable
};
SloTrend {
direction,
rate,
confidence: 0.8, time_to_breach: None, }
}
async fn update_overall_health(&self) -> Result<()> {
let state = self.slo_state.read().await;
let mut has_emergency = false;
let mut has_critical = false;
let mut has_warning = false;
for status in state.slo_status.values() {
match status.status {
SloHealthStatus::Breached => {
has_critical = true;
}
SloHealthStatus::Warning => has_warning = true,
SloHealthStatus::Error => has_critical = true,
_ => {}
}
}
drop(state);
let new_health = if has_emergency {
SystemHealth::Emergency
} else if has_critical {
SystemHealth::Critical
} else if has_warning {
SystemHealth::Warning
} else {
SystemHealth::Healthy
};
let mut state = self.slo_state.write().await;
state.overall_health = new_health;
state.last_measurement = Utc::now();
Ok(())
}
async fn check_for_alerts(&self) -> Result<()> {
let state = self.slo_state.read().await;
for (slo_type, status) in &state.slo_status {
match status.status {
SloHealthStatus::Breached => {
self.alert_manager.create_alert(
slo_type.clone(),
AlertSeverity::Critical,
format!("SLO breached: {} = {} (target: {})",
slo_type.name(),
status.current_value,
status.target_value
)
).await;
}
SloHealthStatus::Warning => {
self.alert_manager.create_alert(
slo_type.clone(),
AlertSeverity::Warning,
format!("SLO at warning threshold: {} = {} (warning: {})",
slo_type.name(),
status.current_value,
self.config.slos.get(slo_type).unwrap().warning_threshold.value
)
).await;
}
_ => {}
}
}
Ok(())
}
pub async fn get_slo_state(&self) -> SloState {
self.slo_state.read().await.clone()
}
pub async fn get_breach_history(&self) -> BreachHistory {
self.breach_history.read().await.clone()
}
pub async fn generate_report(&self) -> Result<SloReport> {
let state = self.get_slo_state().await;
let history = self.get_breach_history().await;
Ok(SloReport {
timestamp: Utc::now(),
overall_health: state.overall_health,
slo_statuses: state.slo_status,
active_alerts: state.active_alerts,
breach_statistics: history.stats,
trends: self.calculate_slo_trends().await?,
})
}
async fn calculate_slo_trends(&self) -> Result<HashMap<SloType, SloTrend>> {
let state = self.slo_state.read().await;
Ok(state.slo_status.iter()
.map(|(slo_type, status)| (slo_type.clone(), status.trend.clone()))
.collect())
}
fn default_config() -> SloConfig {
let mut slos = HashMap::new();
slos.insert(SloType::EceBound, SloDefinition {
name: "ECE Bound".to_string(),
description: "ECE ≤ max(0.015, ĉ√(K/N))".to_string(),
target: SloTarget {
target_type: TargetType::Ratio,
value: 1.0, operator: ComparisonOperator::LessEqual,
units: "ratio".to_string(),
},
warning_threshold: SloTarget {
target_type: TargetType::Ratio,
value: 0.9,
operator: ComparisonOperator::GreaterEqual,
units: "ratio".to_string(),
},
measurement: MeasurementConfig {
frequency: Duration::minutes(5),
window_size: Duration::minutes(15),
min_samples: 100,
aggregation: AggregationMethod::Mean,
},
alert_config: SloAlertConfig::default(),
custom_evaluator: None,
});
SloConfig {
slos,
monitoring: SloMonitoringConfig {
enabled: true,
measurement_interval: Duration::minutes(1),
retention_period: Duration::days(30),
max_memory_measurements: 10000,
},
alerting: SloAlertConfig::default(),
reporting: SloReportingConfig {
enabled: true,
frequency: ReportFrequency::Daily,
channels: vec![],
format: ReportFormat::Markdown,
include_trends: true,
},
integration: SloIntegrationConfig {
prometheus: None,
grafana: None,
datadog: None,
webhooks: vec![],
},
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SloReport {
pub timestamp: DateTime<Utc>,
pub overall_health: SystemHealth,
pub slo_statuses: HashMap<SloType, SloStatus>,
pub active_alerts: Vec<ActiveAlert>,
pub breach_statistics: BreachStatistics,
pub trends: HashMap<SloType, SloTrend>,
}
impl SloAlertManager {
pub fn new(config: SloAlertConfig) -> Self {
Self {
config,
active_alerts: Arc::new(RwLock::new(HashMap::new())),
alert_history: Arc::new(RwLock::new(VecDeque::new())),
}
}
async fn create_alert(&self, slo_type: SloType, severity: AlertSeverity, message: String) {
let alert_id = format!("{}_{}", slo_type.name(), Utc::now().timestamp());
let alert = ActiveAlert {
id: alert_id.clone(),
slo_type,
severity,
started_at: Utc::now(),
message,
acknowledged: false,
escalation_level: 0,
};
let mut alerts = self.active_alerts.write().await;
alerts.insert(alert_id, alert);
}
async fn process_alerts(&self) {
}
}
impl SloType {
pub fn name(&self) -> &'static str {
match self {
SloType::EceBound => "ECE Bound",
SloType::ClampActivation => "Clamp Activation",
SloType::MergedBinWarning => "Merged Bin Warning",
SloType::P99Latency => "P99 Latency",
SloType::WeeklyDriftDeltas => "Weekly Drift Deltas",
SloType::ScoreRangeValidation => "Score Range Validation",
SloType::ClampAlphaChanges => "Clamp Alpha Changes",
}
}
}
impl Default for SloState {
fn default() -> Self {
Self {
slo_status: HashMap::new(),
last_measurement: Utc::now(),
overall_health: SystemHealth::Healthy,
active_alerts: vec![],
}
}
}
impl Default for BreachHistory {
fn default() -> Self {
Self {
breaches: HashMap::new(),
stats: BreachStatistics {
total_breaches: HashMap::new(),
mttr: HashMap::new(),
mtbf: HashMap::new(),
availability: HashMap::new(),
},
}
}
}
impl Default for SloAlertConfig {
fn default() -> Self {
Self {
enabled: true,
channels: vec![AlertChannel::Log { level: "warn".to_string() }],
escalation: EscalationPolicy {
levels: vec![
EscalationLevel {
delay: Duration::minutes(5),
channels: vec![AlertChannel::Log { level: "error".to_string() }],
severity: AlertSeverity::Critical,
}
],
auto_resolve: true,
require_ack: false,
},
dedup_window: Duration::minutes(15),
maintenance_suppression: true,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_slo_system_creation() {
}
#[tokio::test]
async fn test_ece_bound_measurement() {
}
#[tokio::test]
async fn test_alert_generation() {
}
}