use std::time::Duration;
#[derive(Debug, Clone)]
pub struct Backoff {
base: Duration,
jittered: Duration,
}
#[cfg(not(target_arch = "wasm32"))]
async fn sleep(duration: Duration) {
tokio::time::sleep(duration).await;
}
#[cfg(target_arch = "wasm32")]
async fn sleep(duration: Duration) {
async fn sleep_ms(millis: i32) {
let mut cb = |resolve: js_sys::Function, _reject: js_sys::Function| {
web_sys::window()
.expect("Failed to get window")
.set_timeout_with_callback_and_timeout_and_arguments_0(&resolve, millis)
.expect("Failed to call set_timeout");
};
let p = js_sys::Promise::new(&mut cb);
wasm_bindgen_futures::JsFuture::from(p)
.await
.expect("Failed to await sleep promise");
}
sleep_ms(duration.as_millis() as i32).await;
}
impl Backoff {
#[inline]
pub async fn sleep(&self) {
sleep(self.jittered).await;
}
#[inline]
pub fn base(&self) -> Duration {
self.base
}
#[inline]
pub fn jittered(&self) -> Duration {
self.jittered
}
}
#[derive(Debug)]
pub struct BackoffGenerator {
base: Duration,
max: Duration,
jitter_factor: f64,
iteration: u32,
}
impl BackoffGenerator {
pub const DEFAULT_JITTER_FACTOR: f64 = 0.5;
pub fn new(base: Duration, max: Duration) -> Result<Self, String> {
Self::new_with_custom_jitter(base, max, Self::DEFAULT_JITTER_FACTOR)
}
pub fn new_with_custom_jitter(
base: Duration,
max: Duration,
jitter_factor: f64,
) -> Result<Self, String> {
if base > max {
return Err("base duration must be less than or equal to max duration".to_owned());
}
if jitter_factor < 0.0 || jitter_factor > 1.0 {
return Err("jitter factor must be between 0 and 1".to_owned());
}
Ok(Self {
base,
max,
jitter_factor,
iteration: 0,
})
}
fn jitter(&self, duration: Duration) -> Duration {
let jitter = rand::random::<f64>() * self.jitter_factor;
let jittered_secs = duration.as_secs_f64() * (1.0 + jitter);
Duration::try_from_secs_f64(jittered_secs).unwrap_or(duration)
}
pub fn gen_next(&mut self) -> Backoff {
let base = 2u32
.checked_pow(self.iteration)
.and_then(|p| self.base.checked_mul(p))
.unwrap_or(self.max)
.clamp(self.base, self.max);
let jittered = self.jitter(base);
self.iteration += 1;
Backoff { base, jittered }
}
pub fn max_backoff(&self) -> Backoff {
let jittered = self.jitter(self.max);
Backoff {
base: self.max,
jittered,
}
}
pub fn is_reset(&self) -> bool {
self.iteration == 0
}
pub fn reset(&mut self) {
self.iteration = 0;
}
}