use std::time::Duration;
use tracing::{debug, warn};
use crate::collect::errors::{CollectError, Result};
pub(crate) const MAX_RETRIES: u32 = 3;
pub(crate) const RETRY_BASE_MS: u64 = 1000;
pub(crate) async fn retry_get(client: &reqwest::Client, url: &str) -> Result<reqwest::Response> {
let mut last_err: Option<reqwest::Error> = None;
for attempt in 0..=MAX_RETRIES {
debug!(url = %url, attempt, "GET (with retry)");
match client.get(url).send().await {
Ok(resp) => {
let status = resp.status();
let transient = status.as_u16() == 429 || (500..=599).contains(&status.as_u16());
if !transient || attempt == MAX_RETRIES {
return Ok(resp);
}
let delay = RETRY_BASE_MS * (1u64 << attempt);
warn!(
status = %status,
attempt,
delay_ms = delay,
"GitHub returned transient status; retrying"
);
tokio::time::sleep(Duration::from_millis(delay)).await;
}
Err(e) => {
if attempt == MAX_RETRIES {
return Err(CollectError::Http(e));
}
let delay = RETRY_BASE_MS * (1u64 << attempt);
warn!(error = %e, attempt, delay_ms = delay, "transport error; retrying");
last_err = Some(e);
tokio::time::sleep(Duration::from_millis(delay)).await;
}
}
}
Err(CollectError::Http(
last_err.expect("retry loop preserved error"),
))
}