use crate::chaos::ChaosController;
use crate::sla::SlaTracker;
use std::sync::Arc;
use std::sync::Mutex;
pub struct BranchMonitor {
node_id: String,
sla_tracker: Arc<Mutex<SlaTracker>>,
chaos: Arc<Mutex<ChaosController>>,
}
impl BranchMonitor {
pub fn new(node_id: &str) -> Self {
BranchMonitor {
node_id: node_id.to_string(),
sla_tracker: Arc::new(Mutex::new(SlaTracker::new())),
chaos: Arc::new(Mutex::new(ChaosController::new())),
}
}
pub fn with_sla(node_id: &str, sla_tracker: Arc<Mutex<SlaTracker>>) -> Self {
BranchMonitor {
node_id: node_id.to_string(),
sla_tracker,
chaos: Arc::new(Mutex::new(ChaosController::new())),
}
}
pub fn with_chaos(node_id: &str, chaos: Arc<Mutex<ChaosController>>) -> Self {
BranchMonitor {
node_id: node_id.to_string(),
sla_tracker: Arc::new(Mutex::new(SlaTracker::new())),
chaos,
}
}
pub fn record_branch(&mut self, outcome: &str, declared_probability: f64) {
let should_chaos = {
let mut chaos = self.chaos.lock().unwrap();
chaos.should_inject_chaos(&self.node_id)
};
if should_chaos {
return;
}
let mut sla = self.sla_tracker.lock().unwrap();
let anomaly = sla.record(
&self.node_id,
outcome,
declared_probability,
true,
);
if anomaly {
crate::telemetry::emit_anomaly_event(
&self.node_id,
outcome,
declared_probability,
sla.observed_frequency(&self.node_id, outcome),
);
}
crate::telemetry::attach_node_span_attribute(&self.node_id);
}
pub fn record_failure(
&mut self,
operation_id: &str,
error: &dyn std::error::Error,
declared_probability: Option<f64>,
) {
let key = format!("{}.failure", operation_id);
let outcome = "FAILURE";
if let Some(prob) = declared_probability {
let mut sla = self.sla_tracker.lock().unwrap();
let anomaly = sla.record(&key, outcome, prob, true);
if anomaly {
crate::telemetry::emit_anomaly_event(
&key,
outcome,
prob,
sla.observed_frequency(&key, outcome),
);
}
}
crate::telemetry::attach_node_span_attribute(&key);
eprintln!(
"[etdl] operation '{}' failed: {}",
operation_id, error
);
}
pub fn flush(&self) {
let sla = self.sla_tracker.lock().unwrap();
eprintln!(
"[etdl] node '{}': {} evaluations recorded",
self.node_id,
sla.total_evaluations()
);
}
}
impl Drop for BranchMonitor {
fn drop(&mut self) {
self.flush();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_record_branch() {
let mut monitor = BranchMonitor::new("test_barrier");
monitor.record_branch("SUCCESS", 0.95);
monitor.record_branch("SUCCESS", 0.95);
monitor.record_branch("FAILURE", 0.05);
}
}