use rand::{Rng, rng};
use reqwest::Client;
use std::time::Duration;
use tokio::time::sleep;
async fn fetch_once(
client: &Client,
url: &str,
) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
let resp = client.get(url).send().await?;
let text = resp.text().await?;
Ok(text)
}
pub async fn fetch_with_retry(
client: &Client,
url: &str,
) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
let retry_attempts = 10;
for i in 0..retry_attempts {
match fetch_once(client, url).await {
Ok(text) => {
return Ok(text);
}
Err(e) => {
eprintln!(
"[fetch_with_retry] Error on attempt {}/{}: {}",
i + 1,
retry_attempts,
e
);
let sleep_duration = calc_exponential_backoff_duration(i);
sleep(sleep_duration).await;
}
}
}
Err(format!(
"Failed to fetch data from {} after {} attempts.",
url, retry_attempts
)
.into())
}
fn calc_exponential_backoff_duration(retry_count: u32) -> Duration {
let mut rng = rng();
let random_part: f64 = rng.random();
let base = 2u64.pow(retry_count);
let backoff_seconds = (base as f64) + random_part;
Duration::from_secs_f64(backoff_seconds)
}