use std::time::Duration;
#[derive(Debug)]
pub enum RetryStrategy {
Linear,
ExponentialBackoff,
FibonacciBackoff,
ArithmeticProgression { coefficient: usize },
}
impl RetryStrategy {
pub(crate) fn calculate_delay(&self, base_delay: Duration, attempt: usize) -> Duration {
match self {
RetryStrategy::Linear => base_delay,
RetryStrategy::ExponentialBackoff => {
if attempt == 0 {
base_delay
} else {
base_delay * 2u32.pow((attempt - 1) as u32)
}
}
RetryStrategy::FibonacciBackoff => {
if attempt < 2 {
base_delay
} else {
let mut prev = base_delay;
let mut curr = base_delay;
for _ in 2..=attempt {
let next = prev + curr;
prev = curr;
curr = next;
}
curr
}
}
RetryStrategy::ArithmeticProgression { coefficient } => {
base_delay * (*coefficient as u32 * attempt as u32)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
#[test]
fn test_linear_strategy() {
let base_delay = Duration::from_secs(2);
let linear = RetryStrategy::Linear;
assert_eq!(
linear.calculate_delay(base_delay, 0),
Duration::from_secs(2)
); assert_eq!(
linear.calculate_delay(base_delay, 1),
Duration::from_secs(2)
); assert_eq!(
linear.calculate_delay(base_delay, 2),
Duration::from_secs(2)
); assert_eq!(
linear.calculate_delay(base_delay, 3),
Duration::from_secs(2)
); }
#[test]
fn test_exponential_backoff_strategy() {
let base_delay = Duration::from_secs(2);
let expo = RetryStrategy::ExponentialBackoff;
assert_eq!(expo.calculate_delay(base_delay, 0), Duration::from_secs(2));
assert_eq!(expo.calculate_delay(base_delay, 1), Duration::from_secs(2));
assert_eq!(expo.calculate_delay(base_delay, 2), Duration::from_secs(4));
assert_eq!(expo.calculate_delay(base_delay, 3), Duration::from_secs(8));
assert_eq!(expo.calculate_delay(base_delay, 4), Duration::from_secs(16));
}
#[test]
fn test_exponential_backoff_strategy_mill() {
let base_delay = Duration::from_millis(2000); let expo = RetryStrategy::ExponentialBackoff;
assert_eq!(
expo.calculate_delay(base_delay, 0),
Duration::from_millis(2000)
); assert_eq!(
expo.calculate_delay(base_delay, 1),
Duration::from_millis(2000)
); assert_eq!(
expo.calculate_delay(base_delay, 2),
Duration::from_millis(4000)
); assert_eq!(
expo.calculate_delay(base_delay, 3),
Duration::from_millis(8000)
); assert_eq!(
expo.calculate_delay(base_delay, 4),
Duration::from_millis(16000)
); }
#[test]
fn test_fibonacci_backoff_strategy() {
let base_delay = Duration::from_secs(1);
let fib = RetryStrategy::FibonacciBackoff;
assert_eq!(fib.calculate_delay(base_delay, 0), Duration::from_secs(1));
assert_eq!(fib.calculate_delay(base_delay, 1), Duration::from_secs(1));
assert_eq!(fib.calculate_delay(base_delay, 2), Duration::from_secs(2));
assert_eq!(fib.calculate_delay(base_delay, 3), Duration::from_secs(3));
assert_eq!(fib.calculate_delay(base_delay, 4), Duration::from_secs(5));
assert_eq!(fib.calculate_delay(base_delay, 5), Duration::from_secs(8));
}
#[test]
fn test_fibonacci_backoff_strategy_millis() {
let base_delay = Duration::from_millis(2000); let fib = RetryStrategy::FibonacciBackoff;
assert_eq!(
fib.calculate_delay(base_delay, 0),
Duration::from_millis(2000)
); assert_eq!(
fib.calculate_delay(base_delay, 1),
Duration::from_millis(2000)
); assert_eq!(
fib.calculate_delay(base_delay, 2),
Duration::from_millis(4000)
); assert_eq!(
fib.calculate_delay(base_delay, 3),
Duration::from_millis(6000)
); assert_eq!(
fib.calculate_delay(base_delay, 4),
Duration::from_millis(10000)
); assert_eq!(
fib.calculate_delay(base_delay, 5),
Duration::from_millis(16000)
); }
#[test]
fn test_arithmetic_progression_strategy() {
let base_delay = Duration::from_secs(2);
let ap = RetryStrategy::ArithmeticProgression { coefficient: 3 };
assert_eq!(ap.calculate_delay(base_delay, 1), Duration::from_secs(6));
assert_eq!(ap.calculate_delay(base_delay, 2), Duration::from_secs(12));
assert_eq!(ap.calculate_delay(base_delay, 3), Duration::from_secs(18));
}
}