use mettle::{
Backoff, DecorrelatedBackoff, DecorrelatedBackoffConfig, ExponentialBackoff,
ExponentialBackoffConfig, retry,
};
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
use std::time::Duration;
#[derive(Debug)]
enum FetchError {
Timeout, NotFound, }
#[tokio::main]
async fn main() -> Result<(), mettle::BackoffConfigError> {
let result = retry(flaky_fetch).await;
println!("1. defaults: {result:?}");
let result = retry(always_404)
.when(|e| matches!(e, FetchError::Timeout))
.await;
println!("2. filtered: {result:?}");
let result = retry(flaky_fetch)
.backoff(fast_backoff())
.max_elapsed(Duration::from_secs(5))
.when(|e| matches!(e, FetchError::Timeout))
.await;
println!("3. full: {result:?}");
let result = retry(flaky_fetch)
.backoff(fast_backoff().jittered())
.when(|e| matches!(e, FetchError::Timeout))
.await;
println!("4. jittered: {result:?}");
let result = retry(flaky_fetch)
.backoff(DecorrelatedBackoff::new(DecorrelatedBackoffConfig {
base: Duration::from_millis(20),
max_retries: 5,
max_delay: Duration::from_secs(1),
})?)
.when(|e| matches!(e, FetchError::Timeout))
.await;
println!("5. decorrel.: {result:?}");
Ok(())
}
async fn flaky_fetch() -> Result<&'static str, FetchError> {
static ATTEMPTS: AtomicUsize = AtomicUsize::new(0);
if ATTEMPTS.fetch_add(1, SeqCst) < 2 {
Err(FetchError::Timeout)
} else {
ATTEMPTS.store(0, SeqCst);
Ok("user data")
}
}
async fn always_404() -> Result<&'static str, FetchError> {
Err(FetchError::NotFound)
}
fn fast_backoff() -> ExponentialBackoff {
ExponentialBackoff::new(ExponentialBackoffConfig {
base: Duration::from_millis(20),
..Default::default()
})
.expect("backoff config is valid")
}