use altair_retry::prelude::*;
use std::sync::Arc;
use std::sync::atomic::{AtomicU32, Ordering};
use std::time::Duration;
type BoxedError = Box<dyn std::error::Error + Send + Sync>;
struct Response {
status: u16,
body: &'static str,
}
#[allow(clippy::unnecessary_wraps)]
fn make_request(attempt: u32) -> std::io::Result<Response> {
match attempt {
1 | 2 => Ok(Response {
status: 502,
body: "bad gateway",
}),
_ => Ok(Response {
status: 200,
body: "{\"id\":42}",
}),
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let attempts = Arc::new(AtomicU32::new(0));
let counter = attempts.clone();
let cfg = Config::builder()
.name("orders.api")
.max_retries(3)
.initial_interval(Duration::from_millis(50))
.jitter(true)
.build();
let response: Response = retry(cfg, move || {
let counter = counter.clone();
async move {
let n = counter.fetch_add(1, Ordering::SeqCst) + 1;
let response = make_request(n).map_err(|e| Box::new(e) as BoxedError)?;
println!("attempt {n}: HTTP {}", response.status);
match response.status {
200..=299 => Ok(response),
400..=499 => {
Err(Box::new(PermanentError::wrap(format!(
"client error {}: {}",
response.status, response.body
))) as BoxedError)
}
_ => {
Err(Box::new(std::io::Error::other(format!(
"server error {}",
response.status
))) as BoxedError)
}
}
}
})
.await?;
println!("final response body: {}", response.body);
Ok(())
}