use std::time::Duration;
use crate::error::WeexError;
pub struct RetryConfig {
pub max_attempts: u32,
pub base_delay_ms: u64,
pub max_delay_ms: u64,
}
impl Default for RetryConfig {
fn default() -> Self {
RetryConfig {
max_attempts: 3,
base_delay_ms: 100,
max_delay_ms: 5000,
}
}
}
pub async fn with_retry<T, F, Fut>(config: &RetryConfig, mut operation: F) -> Result<T, WeexError>
where
F: FnMut() -> Fut,
Fut: std::future::Future<Output = Result<T, WeexError>>,
{
let mut attempts = 0;
let mut delay = config.base_delay_ms;
loop {
attempts += 1;
match operation().await {
Ok(result) => return Ok(result),
Err(e) => {
if attempts >= config.max_attempts {
return Err(e);
}
if !is_retryable(&e) {
return Err(e);
}
let jitter = rand::random::<u64>() % 50;
tokio::time::sleep(Duration::from_millis(delay + jitter)).await;
delay = std::cmp::min(delay * 2, config.max_delay_ms);
}
}
}
}
fn is_retryable(error: &WeexError) -> bool {
match error {
WeexError::Http(_) => true, WeexError::Api { code, .. } => {
code == "429" || code.starts_with("5")
}
_ => false,
}
}