#[derive(Clone, Debug, PartialEq, Eq)]
pub enum AlertSeverity {
Info,
Warning,
Error,
Critical,
}
#[derive(Clone, Debug, PartialEq)]
pub enum PolicyAction {
SlowProducer {
reduction_factor: f64,
},
ShedLoad {
priority_threshold: u8,
},
ScaleConsumer,
Alert {
severity: AlertSeverity,
},
}
#[derive(Clone, Debug, PartialEq)]
pub struct PressurePolicy {
pub threshold: f64,
pub action: PolicyAction,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct ChannelPolicyConfig {
policies: Vec<PressurePolicy>,
}
impl ChannelPolicyConfig {
#[must_use]
pub fn new(mut policies: Vec<PressurePolicy>) -> Self {
policies.sort_by(|left, right| left.threshold.total_cmp(&right.threshold));
Self { policies }
}
#[must_use]
pub fn policies(&self) -> &[PressurePolicy] {
&self.policies
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.policies.is_empty()
}
#[must_use]
pub fn len(&self) -> usize {
self.policies.len()
}
#[must_use]
pub fn actions_for_pressure(&self, pressure: f64) -> Vec<PolicyAction> {
let mut actions = Vec::new();
for policy in &self.policies {
if policy.threshold <= pressure {
actions.push(policy.action.clone());
} else {
break;
}
}
actions
}
}
#[cfg(test)]
mod tests {
use super::{AlertSeverity, ChannelPolicyConfig, PolicyAction, PressurePolicy};
fn action_name(action: &PolicyAction) -> &'static str {
match action {
PolicyAction::SlowProducer { .. } => "slow-producer",
PolicyAction::ShedLoad { .. } => "shed-load",
PolicyAction::ScaleConsumer => "scale-consumer",
PolicyAction::Alert { .. } => "alert",
}
}
fn close_to(actual: f64, expected: f64) -> bool {
(actual - expected).abs() < f64::EPSILON
}
#[test]
fn policy_action_defines_exact_pressure_actions() {
let slow = PolicyAction::SlowProducer {
reduction_factor: 0.5,
};
let shed = PolicyAction::ShedLoad {
priority_threshold: 3,
};
let scale = PolicyAction::ScaleConsumer;
let alert = PolicyAction::Alert {
severity: AlertSeverity::Warning,
};
assert_eq!(action_name(&slow), "slow-producer");
assert_eq!(action_name(&shed), "shed-load");
assert_eq!(action_name(&scale), "scale-consumer");
assert_eq!(action_name(&alert), "alert");
}
#[test]
fn pressure_policy_constructs_with_action_and_threshold() {
let policy = PressurePolicy {
threshold: 0.7,
action: PolicyAction::SlowProducer {
reduction_factor: 0.25,
},
};
assert!(close_to(policy.threshold, 0.7));
assert!(matches!(
policy.action,
PolicyAction::SlowProducer { reduction_factor } if close_to(reduction_factor, 0.25)
));
}
#[test]
fn channel_policy_config_stores_policies_in_threshold_order() {
let high = PressurePolicy {
threshold: 0.9,
action: PolicyAction::ShedLoad {
priority_threshold: 2,
},
};
let low = PressurePolicy {
threshold: 0.5,
action: PolicyAction::SlowProducer {
reduction_factor: 0.5,
},
};
let config = ChannelPolicyConfig::new(vec![high, low]);
assert_eq!(config.len(), 2);
assert!(close_to(config.policies()[0].threshold, 0.5));
assert!(close_to(config.policies()[1].threshold, 0.9));
}
#[test]
fn policies_compose_in_escalation_sequence() {
let slow = PressurePolicy {
threshold: 0.5,
action: PolicyAction::SlowProducer {
reduction_factor: 0.5,
},
};
let shed = PressurePolicy {
threshold: 0.8,
action: PolicyAction::ShedLoad {
priority_threshold: 4,
},
};
let config = ChannelPolicyConfig::new(vec![shed, slow]);
let moderate = config.actions_for_pressure(0.6);
let high = config.actions_for_pressure(0.9);
let reduced = config.actions_for_pressure(0.6);
let clear = config.actions_for_pressure(0.3);
assert_eq!(moderate.len(), 1);
assert!(matches!(
moderate.as_slice(),
[PolicyAction::SlowProducer { reduction_factor }] if close_to(*reduction_factor, 0.5)
));
assert_eq!(high.len(), 2);
assert!(matches!(high[0], PolicyAction::SlowProducer { .. }));
assert!(matches!(high[1], PolicyAction::ShedLoad { .. }));
assert_eq!(reduced.len(), 1);
assert!(clear.is_empty());
}
#[test]
fn pressure_root_re_exports_policy_types() {
use crate::pressure::{AlertSeverity as RootSeverity, PolicyAction as RootAction};
let action = RootAction::Alert {
severity: RootSeverity::Critical,
};
assert!(matches!(
action,
RootAction::Alert {
severity: RootSeverity::Critical
}
));
}
}