use std::time::Duration;
use rand::random;
use crate::FloatDuration;
#[derive(Clone, Copy, Debug, PartialEq)]
#[non_exhaustive]
pub enum Strategy {
Every(FloatDuration),
Backoff(ExponentialBackoff),
}
impl Strategy {
pub(crate) fn retry_in(&self, last_wait: Option<FloatDuration>) -> FloatDuration {
match self {
Self::Every(t) => *t,
Self::Backoff(exp) => exp.retry_in(last_wait),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ExponentialBackoff {
pub min: FloatDuration,
pub max: FloatDuration,
pub jitter: f32,
}
impl Default for ExponentialBackoff {
fn default() -> Self {
Self {
min: Duration::from_millis(250).into(),
max: Duration::from_secs(10).into(),
jitter: 0.1,
}
}
}
impl ExponentialBackoff {
pub(crate) fn retry_in(&self, last_wait: Option<FloatDuration>) -> FloatDuration {
let min = self.min.as_secs_f32();
let max = self.max.as_secs_f32();
let attempt = last_wait.map_or(min, |t| 2.0 * t.as_secs_f32());
let perturb = (1.0 - (self.jitter * 2.0 * (random::<f32>() - 1.0))).clamp(0.0, 2.0);
let mut target_time = attempt * perturb;
let safe_max = if max < min { min } else { max };
target_time = target_time.clamp(min, safe_max);
FloatDuration::from_secs_f32(target_time)
}
}