use std::time::Duration;
use super::error::SwimError;
use super::incarnation::Incarnation;
#[derive(Debug, Clone)]
pub struct SwimConfig {
pub probe_interval: Duration,
pub probe_timeout: Duration,
pub indirect_probes: u8,
pub suspicion_mult: u8,
pub min_suspicion: Duration,
pub initial_incarnation: Incarnation,
pub max_piggyback: usize,
pub fanout_lambda: u32,
}
impl SwimConfig {
pub fn production() -> Self {
Self {
probe_interval: Duration::from_millis(1000),
probe_timeout: Duration::from_millis(500),
indirect_probes: 3,
suspicion_mult: 4,
min_suspicion: Duration::from_secs(2),
initial_incarnation: Incarnation::ZERO,
max_piggyback: 6,
fanout_lambda: 3,
}
}
pub fn validate(&self) -> Result<(), SwimError> {
if self.probe_interval.is_zero() {
return Err(SwimError::InvalidConfig {
field: "probe_interval",
reason: "must be non-zero",
});
}
if self.probe_timeout >= self.probe_interval {
return Err(SwimError::InvalidConfig {
field: "probe_timeout",
reason: "must be strictly less than probe_interval",
});
}
if self.indirect_probes == 0 {
return Err(SwimError::InvalidConfig {
field: "indirect_probes",
reason: "must be at least 1",
});
}
if self.suspicion_mult == 0 {
return Err(SwimError::InvalidConfig {
field: "suspicion_mult",
reason: "must be at least 1",
});
}
if self.min_suspicion.is_zero() {
return Err(SwimError::InvalidConfig {
field: "min_suspicion",
reason: "must be non-zero",
});
}
if self.max_piggyback == 0 {
return Err(SwimError::InvalidConfig {
field: "max_piggyback",
reason: "must be at least 1",
});
}
if self.fanout_lambda == 0 {
return Err(SwimError::InvalidConfig {
field: "fanout_lambda",
reason: "must be at least 1",
});
}
Ok(())
}
}
impl Default for SwimConfig {
fn default() -> Self {
Self::production()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn production_defaults_are_valid() {
SwimConfig::production().validate().expect("valid");
}
#[test]
fn zero_probe_interval_rejected() {
let mut cfg = SwimConfig::production();
cfg.probe_interval = Duration::ZERO;
assert!(matches!(
cfg.validate(),
Err(SwimError::InvalidConfig {
field: "probe_interval",
..
})
));
}
#[test]
fn probe_timeout_must_be_less_than_interval() {
let mut cfg = SwimConfig::production();
cfg.probe_timeout = cfg.probe_interval;
assert!(matches!(
cfg.validate(),
Err(SwimError::InvalidConfig {
field: "probe_timeout",
..
})
));
}
#[test]
fn zero_indirect_probes_rejected() {
let mut cfg = SwimConfig::production();
cfg.indirect_probes = 0;
assert!(matches!(
cfg.validate(),
Err(SwimError::InvalidConfig {
field: "indirect_probes",
..
})
));
}
#[test]
fn zero_suspicion_mult_rejected() {
let mut cfg = SwimConfig::production();
cfg.suspicion_mult = 0;
assert!(matches!(
cfg.validate(),
Err(SwimError::InvalidConfig {
field: "suspicion_mult",
..
})
));
}
#[test]
fn zero_min_suspicion_rejected() {
let mut cfg = SwimConfig::production();
cfg.min_suspicion = Duration::ZERO;
assert!(matches!(
cfg.validate(),
Err(SwimError::InvalidConfig {
field: "min_suspicion",
..
})
));
}
#[test]
fn zero_max_piggyback_rejected() {
let mut cfg = SwimConfig::production();
cfg.max_piggyback = 0;
assert!(matches!(
cfg.validate(),
Err(SwimError::InvalidConfig {
field: "max_piggyback",
..
})
));
}
#[test]
fn zero_fanout_lambda_rejected() {
let mut cfg = SwimConfig::production();
cfg.fanout_lambda = 0;
assert!(matches!(
cfg.validate(),
Err(SwimError::InvalidConfig {
field: "fanout_lambda",
..
})
));
}
}