etdl-core 0.1.4

ETDL runtime: BranchMonitor, retry policies, SLA anomaly detection, chaos injection, and telemetry for reliability-aware event-driven services
Documentation
use std::collections::HashMap;

const DEFAULT_WINDOW_SIZE: usize = 1000;
const DEFAULT_DEVIATION_THRESHOLD: f64 = 0.10;

pub struct SlaTracker {
    window_size: usize,
    deviation_threshold: f64,
    counters: HashMap<String, OutcomeCounter>,
}

struct OutcomeCounter {
    expected_probability: f64,
    occurrences: Vec<bool>,
    total_evaluations: usize,
}

impl SlaTracker {
    pub fn new() -> Self {
        SlaTracker {
            window_size: Self::env_window_size(),
            deviation_threshold: Self::env_deviation_threshold(),
            counters: HashMap::new(),
        }
    }

    pub fn with_config(window_size: usize, deviation_threshold: f64) -> Self {
        SlaTracker {
            window_size,
            deviation_threshold,
            counters: HashMap::new(),
        }
    }

    pub fn record(
        &mut self,
        node_id: &str,
        outcome: &str,
        declared_probability: f64,
        occurred: bool,
    ) -> bool {
        let key = format!("{}:{}", node_id, outcome);
        let counter = self.counters.entry(key).or_insert_with(|| OutcomeCounter {
            expected_probability: declared_probability,
            occurrences: Vec::with_capacity(self.window_size),
            total_evaluations: 0,
        });

        counter.expected_probability = declared_probability;
        counter.total_evaluations += 1;

        if counter.occurrences.len() >= self.window_size {
            counter.occurrences.remove(0);
        }
        counter.occurrences.push(occurred);

        if counter.occurrences.len() >= 10 {
            let observed = counter
                .occurrences
                .iter()
                .filter(|&&o| o)
                .count() as f64
                / counter.occurrences.len() as f64;

            let deviation = (observed - counter.expected_probability).abs();
            deviation > self.deviation_threshold
        } else {
            false
        }
    }

    pub fn observed_frequency(&self, node_id: &str, outcome: &str) -> f64 {
        let key = format!("{}:{}", node_id, outcome);
        self.counters
            .get(&key)
            .map(|c| {
                if c.occurrences.is_empty() {
                    0.0
                } else {
                    c.occurrences.iter().filter(|&&o| o).count() as f64
                        / c.occurrences.len() as f64
                }
            })
            .unwrap_or(0.0)
    }

    pub fn total_evaluations(&self) -> usize {
        self.counters.values().map(|c| c.total_evaluations).sum()
    }

    fn env_window_size() -> usize {
        std::env::var("ETDL_SLA_WINDOW")
            .ok()
            .and_then(|v| v.parse().ok())
            .unwrap_or(DEFAULT_WINDOW_SIZE)
    }

    fn env_deviation_threshold() -> f64 {
        std::env::var("ETDL_SLA_THRESHOLD")
            .ok()
            .and_then(|v| v.parse().ok())
            .unwrap_or(DEFAULT_DEVIATION_THRESHOLD)
    }
}

impl Default for SlaTracker {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_sla_tracking() {
        let mut tracker = SlaTracker::new();
        for _ in 0..100 {
            tracker.record("barrier_1", "SUCCESS", 0.95, true);
        }
        for _ in 0..100 {
            tracker.record("barrier_1", "FAILURE", 0.05, false);
        }
        let freq = tracker.observed_frequency("barrier_1", "SUCCESS");
        assert!((freq - 1.0).abs() < 0.01);
    }

    #[test]
    fn test_sla_anomaly_detection() {
        let mut tracker = SlaTracker::with_config(100, 0.10);

        for _ in 0..20 {
            tracker.record("barrier_1", "SUCCESS", 0.95, true);
        }

        let mut anomaly_detected = false;
        for _ in 0..80 {
            let is_anomaly = tracker.record("barrier_1", "SUCCESS", 0.95, false);
            if is_anomaly {
                anomaly_detected = true;
            }
        }
        assert!(anomaly_detected);
    }
}