use crate::provider::ProviderError;
use std::time::Duration;
use tracing::warn;
#[derive(Debug, Clone)]
pub struct RetryConfig {
pub max_retries: usize,
pub initial_delay_ms: u64,
pub backoff_multiplier: f64,
pub max_delay_ms: u64,
}
impl Default for RetryConfig {
fn default() -> Self {
Self {
max_retries: 3,
initial_delay_ms: 1000,
backoff_multiplier: 2.0,
max_delay_ms: 30_000,
}
}
}
impl RetryConfig {
pub fn none() -> Self {
Self {
max_retries: 0,
..Default::default()
}
}
pub fn delay_for_attempt(&self, attempt: usize) -> Duration {
let base_ms =
self.initial_delay_ms as f64 * self.backoff_multiplier.powi((attempt - 1) as i32);
let capped_ms = base_ms.min(self.max_delay_ms as f64);
let jitter = 0.8 + rand::random::<f64>() * 0.4;
Duration::from_millis((capped_ms * jitter) as u64)
}
}
impl ProviderError {
pub fn is_retryable(&self) -> bool {
matches!(self, Self::RateLimited { .. } | Self::Network(_))
}
pub fn retry_after(&self) -> Option<Duration> {
match self {
Self::RateLimited {
retry_after_ms: Some(ms),
} => Some(Duration::from_millis(*ms)),
_ => None,
}
}
}
pub(crate) fn log_retry(attempt: usize, max: usize, delay: &Duration, error: &ProviderError) {
warn!(
"Provider error (attempt {}/{}), retrying in {:.1}s: {}",
attempt,
max,
delay.as_secs_f64(),
error
);
}