use std::time::Duration;
use backoff::{ExponentialBackoff, ExponentialBackoffBuilder};
const DEFAULT_HEARTBEAT_INTERVAL_DURATION: Duration = Duration::from_secs(10);
const DEFAULT_HEARTBEAT_TIMEOUT_DURATION: Duration = Duration::from_secs(30);
const DEFAULT_INITIAL_BACKOFF_DURATION: Duration = Duration::from_secs(1);
const DEFAULT_MAX_BACKOFF_DURATION: Duration = Duration::from_secs(60);
const DEFAULT_BACKOFF_MULTIPLIER: f64 = 2.0;
#[non_exhaustive]
#[derive(Debug, Clone)]
pub struct Config {
pub heartbeat_interval: Duration,
pub heartbeat_timeout: Duration,
pub reconnect: ReconnectConfig,
}
impl Default for Config {
fn default() -> Self {
Self {
heartbeat_interval: DEFAULT_HEARTBEAT_INTERVAL_DURATION,
heartbeat_timeout: DEFAULT_HEARTBEAT_TIMEOUT_DURATION,
reconnect: ReconnectConfig::default(),
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone)]
pub struct ReconnectConfig {
pub max_attempts: Option<u32>,
pub initial_backoff: Duration,
pub max_backoff: Duration,
pub backoff_multiplier: f64,
}
impl Default for ReconnectConfig {
fn default() -> Self {
Self {
max_attempts: None, initial_backoff: DEFAULT_INITIAL_BACKOFF_DURATION,
max_backoff: DEFAULT_MAX_BACKOFF_DURATION,
backoff_multiplier: DEFAULT_BACKOFF_MULTIPLIER,
}
}
}
impl From<ReconnectConfig> for ExponentialBackoff {
fn from(config: ReconnectConfig) -> Self {
ExponentialBackoffBuilder::default()
.with_initial_interval(config.initial_backoff)
.with_max_interval(config.max_backoff)
.with_multiplier(config.backoff_multiplier)
.with_max_elapsed_time(None) .build()
}
}
#[cfg(test)]
mod tests {
use backoff::backoff::Backoff as _;
use super::*;
#[test]
fn backoff_sequence() {
let config = ReconnectConfig::default();
let mut backoff: ExponentialBackoff = config.into();
let first = backoff.next_backoff().unwrap();
assert!(first >= Duration::from_millis(500) && first <= Duration::from_millis(1500));
}
#[test]
fn backoff_respects_max() {
let config = ReconnectConfig {
initial_backoff: Duration::from_secs(1),
max_backoff: Duration::from_secs(2),
backoff_multiplier: 3.0,
max_attempts: None,
};
let mut backoff: ExponentialBackoff = config.into();
for _ in 0..10 {
let _next = backoff.next_backoff();
}
let duration = backoff.next_backoff().unwrap();
assert!(duration <= Duration::from_secs(3));
}
}