use std::time::Duration;
use backoff::{future::Retry, ExponentialBackoff};
use futures::{channel::oneshot, future::BoxFuture, Future, FutureExt as _};
use crate::platform;
#[derive(Debug)]
pub struct BackoffDelayer(ExponentialBackoff);
impl BackoffDelayer {
#[must_use]
pub fn new(
initial_interval: Duration,
multiplier: f64,
max_interval: Duration,
max_elapsed_time: Option<Duration>,
) -> Self {
let max_interval = max_elapsed_time
.map_or(max_interval, |max_elapsed| max_interval.min(max_elapsed));
let initial_interval = initial_interval.min(max_interval);
Self(ExponentialBackoff {
current_interval: initial_interval,
initial_interval,
randomization_factor: 0.0,
multiplier,
max_interval,
max_elapsed_time,
..ExponentialBackoff::default()
})
}
pub async fn retry<Fn, Fut, I, E>(self, operation: Fn) -> Result<I, E>
where
Fn: FnMut() -> Fut,
Fut: Future<Output = Result<I, backoff::Error<E>>>,
{
Retry::new(Sleeper, self.0, |_, _| {}, operation).await
}
}
struct Sleeper;
impl backoff::future::Sleeper for Sleeper {
type Sleep = BoxFuture<'static, ()>;
fn sleep(&self, dur: Duration) -> Self::Sleep {
let (tx, rx) = oneshot::channel();
platform::spawn(async move {
platform::delay_for(dur).await;
_ = tx.send(());
});
Box::pin(rx.map(drop))
}
}