use std::time::Duration;
#[derive(Debug, Clone)]
pub struct RetryStrategy {
pub max_retries: u32,
pub initial_delay: Duration,
pub max_delay: Duration,
pub backoff_multiplier: f64,
pub use_jitter: bool,
}
impl RetryStrategy {
pub fn exponential_backoff(max_retries: u32, initial_delay: Duration) -> Self {
Self {
max_retries,
initial_delay,
max_delay: Duration::from_secs(300), backoff_multiplier: 2.0,
use_jitter: true,
}
}
pub fn linear_backoff(max_retries: u32, delay: Duration) -> Self {
Self {
max_retries,
initial_delay: delay,
max_delay: delay,
backoff_multiplier: 1.0,
use_jitter: false,
}
}
pub fn fixed_delay(max_retries: u32, delay: Duration) -> Self {
Self {
max_retries,
initial_delay: delay,
max_delay: delay,
backoff_multiplier: 1.0,
use_jitter: false,
}
}
pub fn fibonacci_backoff(max_retries: u32, base_delay: Duration) -> Self {
Self {
max_retries,
initial_delay: base_delay,
max_delay: Duration::from_secs(600), backoff_multiplier: 1.618, use_jitter: true,
}
}
pub fn calculate_delay(&self, attempt: u32) -> Duration {
if attempt == 0 {
return Duration::from_secs(0);
}
let base_delay = if self.backoff_multiplier == 1.0 {
self.initial_delay
} else {
let multiplier = self.backoff_multiplier.powi(attempt as i32 - 1);
let delay_ms = self.initial_delay.as_millis() as f64 * multiplier;
Duration::from_millis(delay_ms.min(self.max_delay.as_millis() as f64) as u64)
};
if self.use_jitter {
let jitter_factor = 0.75 + (rand::random::<f64>() * 0.5);
let delay_ms = (base_delay.as_millis() as f64 * jitter_factor) as u64;
Duration::from_millis(delay_ms)
} else {
base_delay
}
}
pub fn with_max_delay(mut self, max_delay: Duration) -> Self {
self.max_delay = max_delay;
self
}
pub fn with_jitter(mut self, use_jitter: bool) -> Self {
self.use_jitter = use_jitter;
self
}
pub fn with_multiplier(mut self, multiplier: f64) -> Self {
self.backoff_multiplier = multiplier;
self
}
}
impl Default for RetryStrategy {
fn default() -> Self {
Self::exponential_backoff(3, Duration::from_secs(1))
}
}
pub trait RetryPolicy: Send + Sync {
fn should_retry(&self, error: &str, attempt: u32) -> bool;
}
pub struct DefaultRetryPolicy {
max_retries: u32,
}
impl DefaultRetryPolicy {
pub fn new(max_retries: u32) -> Self {
Self { max_retries }
}
}
impl RetryPolicy for DefaultRetryPolicy {
fn should_retry(&self, _error: &str, attempt: u32) -> bool {
attempt < self.max_retries
}
}
pub struct ErrorPatternRetryPolicy {
max_retries: u32,
retryable_patterns: Vec<String>,
}
impl ErrorPatternRetryPolicy {
pub fn new(max_retries: u32, retryable_patterns: Vec<String>) -> Self {
Self {
max_retries,
retryable_patterns,
}
}
}
impl RetryPolicy for ErrorPatternRetryPolicy {
fn should_retry(&self, error: &str, attempt: u32) -> bool {
if attempt >= self.max_retries {
return false;
}
self.retryable_patterns
.iter()
.any(|pattern| error.contains(pattern))
}
}