use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct ScalingPressureConfig {
pub enabled: bool,
pub memory_gate_threshold: f64,
}
impl Default for ScalingPressureConfig {
fn default() -> Self {
Self {
enabled: true,
memory_gate_threshold: 0.8,
}
}
}
impl ScalingPressureConfig {
#[must_use]
pub fn from_cascade() -> Self {
#[cfg(feature = "config")]
{
if let Some(cfg) = crate::config::try_get()
&& let Ok(scaling) = cfg.unmarshal_key_registered::<Self>("scaling")
{
return scaling;
}
}
Self::default()
}
}
#[derive(Debug, Clone)]
pub struct ScalingComponent {
pub name: String,
pub weight: f64,
pub saturation: f64,
}
impl ScalingComponent {
#[must_use]
pub fn new(name: impl Into<String>, weight: f64, saturation: f64) -> Self {
Self {
name: name.into(),
weight,
saturation,
}
}
}
fn default_true() -> bool {
true
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct ScalingEngineConfig {
pub enabled: bool,
pub interval_secs: u64,
pub params: BTreeMap<String, f64>,
pub pressures: Vec<PressureExpr>,
pub transport: ScalingTransportConfig,
}
impl Default for ScalingEngineConfig {
fn default() -> Self {
let mut params = BTreeMap::new();
params.insert("cpu_target".to_string(), 0.70);
Self {
enabled: true,
interval_secs: 15,
params,
pressures: Vec::new(),
transport: ScalingTransportConfig::default(),
}
}
}
impl ScalingEngineConfig {
#[must_use]
pub fn from_cascade() -> Self {
#[cfg(feature = "config")]
{
if let Some(cfg) = crate::config::try_get()
&& let Ok(engine) = cfg.unmarshal_key::<Self>("scaling")
{
return engine;
}
}
Self::default()
}
#[must_use]
pub fn cpu_target(&self) -> f64 {
self.params
.get("cpu_target")
.copied()
.filter(|v| *v > 0.0)
.unwrap_or(0.70)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PressureExpr {
pub name: String,
pub expression: String,
#[serde(default = "default_true")]
pub enabled: bool,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct ScalingTransportConfig {
pub inbound: Option<String>,
pub outbound: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_config_defaults() {
let config = ScalingPressureConfig::default();
assert!(config.enabled);
assert!((config.memory_gate_threshold - 0.8).abs() < f64::EPSILON);
}
#[test]
fn test_config_serde_roundtrip() {
let config = ScalingPressureConfig {
enabled: false,
memory_gate_threshold: 0.9,
};
let json = serde_json::to_string(&config).unwrap();
let parsed: ScalingPressureConfig = serde_json::from_str(&json).unwrap();
assert!(!parsed.enabled);
assert!((parsed.memory_gate_threshold - 0.9).abs() < f64::EPSILON);
}
#[test]
fn test_component_new() {
let c = ScalingComponent::new("kafka_lag", 0.35, 100_000.0);
assert_eq!(c.name, "kafka_lag");
assert!((c.weight - 0.35).abs() < f64::EPSILON);
assert!((c.saturation - 100_000.0).abs() < f64::EPSILON);
}
}