#![allow(dead_code)]
use serde::Serialize;
use super::latency::{
COMPILATION_BUDGET_MS, COMPILATION_PANIC_MS, NON_COMPILATION_BUDGET_MS,
NON_COMPILATION_PANIC_MS, WORKER_SELECTION_BUDGET_MS, WORKER_SELECTION_PANIC_MS,
};
use super::{DECISION_BUDGET_VIOLATIONS, DECISION_PANIC_THRESHOLD_VIOLATIONS};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum BudgetStatus {
Passing,
Warning,
Failing,
}
impl std::fmt::Display for BudgetStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BudgetStatus::Passing => write!(f, "passing"),
BudgetStatus::Warning => write!(f, "warning"),
BudgetStatus::Failing => write!(f, "failing"),
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct BudgetInfo {
pub budget_ms: f64,
pub panic_threshold_ms: f64,
pub violations: u64,
pub critical_violations: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub violation_rate: Option<f64>,
}
#[derive(Debug, Clone, Serialize)]
pub struct BudgetStatusResponse {
pub status: BudgetStatus,
pub budgets: BudgetBreakdown,
pub computed_at: String,
}
#[derive(Debug, Clone, Serialize)]
pub struct BudgetBreakdown {
pub non_compilation: BudgetInfo,
pub compilation: BudgetInfo,
pub worker_selection: BudgetInfo,
}
pub fn get_budget_status() -> BudgetStatusResponse {
let non_compilation_violations = get_violations("non_compilation");
let compilation_violations = get_violations("compilation");
let worker_selection_violations = get_violations("worker_selection");
let non_compilation_critical_violations = get_critical_violations("non_compilation");
let compilation_critical_violations = get_critical_violations("compilation");
let worker_selection_critical_violations = get_critical_violations("worker_selection");
let total_violations =
non_compilation_violations + compilation_violations + worker_selection_violations;
let total_critical_violations = non_compilation_critical_violations
+ compilation_critical_violations
+ worker_selection_critical_violations;
let status = if total_critical_violations > 0 {
BudgetStatus::Failing
} else if total_violations == 0 {
BudgetStatus::Passing
} else {
BudgetStatus::Warning
};
let computed_at = chrono::Utc::now().to_rfc3339();
BudgetStatusResponse {
status,
budgets: BudgetBreakdown {
non_compilation: BudgetInfo {
budget_ms: NON_COMPILATION_BUDGET_MS,
panic_threshold_ms: NON_COMPILATION_PANIC_MS,
violations: non_compilation_violations,
critical_violations: non_compilation_critical_violations,
violation_rate: None, },
compilation: BudgetInfo {
budget_ms: COMPILATION_BUDGET_MS,
panic_threshold_ms: COMPILATION_PANIC_MS,
violations: compilation_violations,
critical_violations: compilation_critical_violations,
violation_rate: None,
},
worker_selection: BudgetInfo {
budget_ms: WORKER_SELECTION_BUDGET_MS,
panic_threshold_ms: WORKER_SELECTION_PANIC_MS,
violations: worker_selection_violations,
critical_violations: worker_selection_critical_violations,
violation_rate: None,
},
},
computed_at,
}
}
fn get_violations(decision_type: &str) -> u64 {
DECISION_BUDGET_VIOLATIONS
.with_label_values(&[decision_type])
.get() as u64
}
fn get_critical_violations(decision_type: &str) -> u64 {
DECISION_PANIC_THRESHOLD_VIOLATIONS
.with_label_values(&[decision_type])
.get() as u64
}
pub fn is_passing() -> bool {
get_budget_status().status == BudgetStatus::Passing
}
pub fn has_critical_violations() -> bool {
get_budget_status().status == BudgetStatus::Failing
}
#[cfg(test)]
mod tests {
use super::*;
use rch_common::test_guard;
#[test]
fn test_budget_status_display() {
let _guard = test_guard!();
assert_eq!(BudgetStatus::Passing.to_string(), "passing");
assert_eq!(BudgetStatus::Warning.to_string(), "warning");
assert_eq!(BudgetStatus::Failing.to_string(), "failing");
}
#[test]
fn test_get_budget_status() {
let _guard = test_guard!();
let status = get_budget_status();
assert!(status.budgets.non_compilation.budget_ms > 0.0);
assert!(status.budgets.compilation.budget_ms > 0.0);
assert!(status.budgets.worker_selection.budget_ms > 0.0);
assert!(
status.budgets.non_compilation.panic_threshold_ms
> status.budgets.non_compilation.budget_ms
);
assert!(
status.budgets.compilation.panic_threshold_ms > status.budgets.compilation.budget_ms
);
assert!(
status.budgets.worker_selection.panic_threshold_ms
> status.budgets.worker_selection.budget_ms
);
}
#[test]
fn test_budget_info_serialization() {
let _guard = test_guard!();
let info = BudgetInfo {
budget_ms: 1.0,
panic_threshold_ms: 5.0,
violations: 0,
critical_violations: 0,
violation_rate: None,
};
let json = serde_json::to_string(&info).unwrap();
assert!(json.contains("budget_ms"));
assert!(json.contains("violations"));
assert!(json.contains("critical_violations"));
assert!(!json.contains("violation_rate"));
}
#[test]
fn test_budget_breakdown() {
let _guard = test_guard!();
let breakdown = BudgetBreakdown {
non_compilation: BudgetInfo {
budget_ms: NON_COMPILATION_BUDGET_MS,
panic_threshold_ms: NON_COMPILATION_PANIC_MS,
violations: 0,
critical_violations: 0,
violation_rate: None,
},
compilation: BudgetInfo {
budget_ms: COMPILATION_BUDGET_MS,
panic_threshold_ms: COMPILATION_PANIC_MS,
violations: 2,
critical_violations: 0,
violation_rate: Some(0.01),
},
worker_selection: BudgetInfo {
budget_ms: WORKER_SELECTION_BUDGET_MS,
panic_threshold_ms: WORKER_SELECTION_PANIC_MS,
violations: 0,
critical_violations: 0,
violation_rate: None,
},
};
let json = serde_json::to_string_pretty(&breakdown).unwrap();
assert!(json.contains("non_compilation"));
assert!(json.contains("compilation"));
assert!(json.contains("worker_selection"));
}
#[test]
fn test_get_budget_status_fails_with_critical_violations() {
let _guard = test_guard!();
let before = get_critical_violations("worker_selection");
DECISION_PANIC_THRESHOLD_VIOLATIONS
.with_label_values(&["worker_selection"])
.inc();
let status = get_budget_status();
assert_eq!(status.status, BudgetStatus::Failing);
assert!(status.budgets.worker_selection.critical_violations > before);
assert!(has_critical_violations());
}
}