use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PoolPressureMonitorConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "default_min_pool_size")]
pub min_pool_size: u32,
#[serde(default = "default_max_pool_size")]
pub max_pool_size: u32,
#[serde(default = "default_target_queue_depth")]
pub target_queue_depth: u32,
#[serde(default = "default_scale_up_step")]
pub scale_up_step: u32,
#[serde(default = "default_scale_down_step")]
pub scale_down_step: u32,
#[serde(default = "default_scale_down_idle_ratio")]
pub scale_down_idle_ratio: f64,
#[serde(default = "default_tuning_interval_ms")]
pub tuning_interval_ms: u64,
#[serde(default = "default_samples_before_action")]
pub samples_before_action: u32,
}
const fn default_min_pool_size() -> u32 {
5
}
const fn default_max_pool_size() -> u32 {
50
}
const fn default_target_queue_depth() -> u32 {
3
}
const fn default_scale_up_step() -> u32 {
5
}
const fn default_scale_down_step() -> u32 {
2
}
const fn default_scale_down_idle_ratio() -> f64 {
0.5
}
const fn default_tuning_interval_ms() -> u64 {
30_000
}
const fn default_samples_before_action() -> u32 {
3
}
impl Default for PoolPressureMonitorConfig {
fn default() -> Self {
Self {
enabled: false,
min_pool_size: default_min_pool_size(),
max_pool_size: default_max_pool_size(),
target_queue_depth: default_target_queue_depth(),
scale_up_step: default_scale_up_step(),
scale_down_step: default_scale_down_step(),
scale_down_idle_ratio: default_scale_down_idle_ratio(),
tuning_interval_ms: default_tuning_interval_ms(),
samples_before_action: default_samples_before_action(),
}
}
}
#[deprecated(since = "2.0.1", note = "Use PoolPressureMonitorConfig")]
pub type PoolTuningConfig = PoolPressureMonitorConfig;
impl PoolPressureMonitorConfig {
pub fn validate(&self) -> Result<(), String> {
if self.min_pool_size >= self.max_pool_size {
return Err(format!(
"pool_tuning: min_pool_size ({}) must be less than max_pool_size ({})",
self.min_pool_size, self.max_pool_size
));
}
if self.scale_up_step == 0 {
return Err("pool_tuning: scale_up_step must be > 0".to_string());
}
if self.scale_down_step == 0 {
return Err("pool_tuning: scale_down_step must be > 0".to_string());
}
if !(0.0..=1.0).contains(&self.scale_down_idle_ratio) {
return Err(format!(
"pool_tuning: scale_down_idle_ratio ({}) must be in [0.0, 1.0]",
self.scale_down_idle_ratio
));
}
if self.tuning_interval_ms < 100 {
return Err(format!(
"pool_tuning: tuning_interval_ms ({}) must be >= 100",
self.tuning_interval_ms
));
}
Ok(())
}
}
#[cfg(test)]
mod tests {
#[allow(clippy::wildcard_imports)]
use super::*;
#[test]
fn test_default_config_is_disabled() {
let cfg = PoolPressureMonitorConfig::default();
assert!(!cfg.enabled, "pool pressure monitoring should be off by default");
}
#[test]
fn test_default_bounds_are_sensible() {
let cfg = PoolPressureMonitorConfig::default();
assert!(cfg.min_pool_size < cfg.max_pool_size);
assert!(cfg.scale_up_step > 0);
assert!(cfg.scale_down_step > 0);
assert!(cfg.tuning_interval_ms >= 1000);
}
#[test]
fn test_validate_passes_for_defaults() {
PoolPressureMonitorConfig::default()
.validate()
.unwrap_or_else(|e| panic!("default pool monitor config should pass validation: {e}"));
}
#[test]
fn test_validate_min_lt_max() {
let cfg = PoolPressureMonitorConfig {
min_pool_size: 10,
max_pool_size: 5,
..Default::default()
};
assert!(
cfg.validate().is_err(),
"min >= max should be invalid, got: {:?}",
cfg.validate()
);
}
#[test]
fn test_validate_min_equals_max_is_invalid() {
let cfg = PoolPressureMonitorConfig {
min_pool_size: 10,
max_pool_size: 10,
..Default::default()
};
assert!(
cfg.validate().is_err(),
"min == max should be invalid, got: {:?}",
cfg.validate()
);
}
#[test]
fn test_validate_idle_ratio_above_one() {
let cfg = PoolPressureMonitorConfig {
scale_down_idle_ratio: 1.5,
..Default::default()
};
assert!(
cfg.validate().is_err(),
"idle ratio > 1.0 should be invalid, got: {:?}",
cfg.validate()
);
}
#[test]
fn test_validate_idle_ratio_negative() {
let cfg = PoolPressureMonitorConfig {
scale_down_idle_ratio: -0.1,
..Default::default()
};
assert!(
cfg.validate().is_err(),
"idle ratio < 0.0 should be invalid, got: {:?}",
cfg.validate()
);
}
#[test]
fn test_validate_zero_scale_up_step() {
let cfg = PoolPressureMonitorConfig {
scale_up_step: 0,
..Default::default()
};
assert!(
cfg.validate().is_err(),
"scale_up_step == 0 should be invalid, got: {:?}",
cfg.validate()
);
}
#[test]
fn test_validate_zero_scale_down_step() {
let cfg = PoolPressureMonitorConfig {
scale_down_step: 0,
..Default::default()
};
assert!(
cfg.validate().is_err(),
"scale_down_step == 0 should be invalid, got: {:?}",
cfg.validate()
);
}
#[test]
#[allow(deprecated)] fn test_pool_tuning_config_alias_works() {
let _cfg: PoolTuningConfig = PoolTuningConfig::default();
}
#[test]
fn test_validate_interval_too_short() {
let cfg = PoolPressureMonitorConfig {
tuning_interval_ms: 50,
..Default::default()
};
assert!(
cfg.validate().is_err(),
"tuning_interval_ms < 100 should be invalid, got: {:?}",
cfg.validate()
);
}
}