use std::time::Duration;
#[derive(Debug, Clone)]
pub struct ReconnectConfig {
pub max_attempts: Option<usize>,
pub initial_delay: Duration,
pub max_delay: Duration,
pub backoff_multiplier: f64,
pub use_jitter: bool,
}
impl Default for ReconnectConfig {
fn default() -> Self {
Self {
max_attempts: Some(10),
initial_delay: Duration::from_millis(100),
max_delay: Duration::from_secs(30),
backoff_multiplier: 2.0,
use_jitter: true,
}
}
}
impl ReconnectConfig {
pub fn infinite() -> Self {
Self {
max_attempts: None,
..Default::default()
}
}
pub fn with_max_attempts(attempts: usize) -> Self {
Self {
max_attempts: Some(attempts),
..Default::default()
}
}
pub fn with_delays(initial: Duration, max: Duration) -> Self {
Self {
initial_delay: initial,
max_delay: max,
..Default::default()
}
}
pub(crate) fn delay_for_attempt(&self, attempt: usize) -> Duration {
let delay_ms =
self.initial_delay.as_millis() as f64 * self.backoff_multiplier.powi(attempt as i32);
let mut delay =
Duration::from_millis(delay_ms.min(self.max_delay.as_millis() as f64) as u64);
if self.use_jitter {
use std::collections::hash_map::RandomState;
use std::hash::BuildHasher;
let hash = RandomState::new().hash_one(attempt);
let jitter = (hash % 25) as f64 / 100.0;
let jitter_ms = (delay.as_millis() as f64 * jitter) as u64;
delay = Duration::from_millis(delay.as_millis() as u64 + jitter_ms);
}
delay
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_reconnect_config_defaults() {
let config = ReconnectConfig::default();
assert_eq!(config.max_attempts, Some(10));
assert_eq!(config.initial_delay, Duration::from_millis(100));
assert_eq!(config.max_delay, Duration::from_secs(30));
assert_eq!(config.backoff_multiplier, 2.0);
assert!(config.use_jitter);
}
#[test]
fn test_reconnect_config_infinite() {
let config = ReconnectConfig::infinite();
assert_eq!(config.max_attempts, None);
}
#[test]
fn test_reconnect_config_delay_calculation() {
let config = ReconnectConfig {
initial_delay: Duration::from_millis(100),
max_delay: Duration::from_secs(10),
backoff_multiplier: 2.0,
use_jitter: false,
max_attempts: Some(10),
};
assert_eq!(config.delay_for_attempt(0), Duration::from_millis(100));
assert_eq!(config.delay_for_attempt(1), Duration::from_millis(200));
assert_eq!(config.delay_for_attempt(2), Duration::from_millis(400));
assert_eq!(config.delay_for_attempt(3), Duration::from_millis(800));
}
}