#[macro_export]
#[doc(hidden)]
macro_rules! retry {
($code:expr) => {
retry!(0, 80, 1000, $code)
};
($max_attempts:expr, $code:expr) => {
retry!($max_attempts, 80, 1000, $code)
};
($max_attempts:expr, $base_delay:expr, $max_delay:expr, $code:expr) => {{
let max_attempts: u64 = ($max_attempts as f64).round() as u64;
let base_delay: u64 = ($base_delay as f64).round() as u64;
let max_delay: u64 = ($max_delay as f64).round() as u64;
if base_delay == 0 {
anyhow::bail!(
"[retry!] `base_delay` cannot be zero. Received: {}",
$base_delay
);
}
if max_delay == 0 {
anyhow::bail!(
"[retry!] `max_delay` cannot be zero. Received: {}",
$max_delay
);
}
if max_delay <= base_delay {
anyhow::bail!(
"[retry!] `max_delay`: {} must be greater than `base_delay`: {}.",
$base_delay,
$max_delay
);
}
let mut n = 1;
loop {
match $code {
Ok(result) => {
tracing::debug!("[retry!] Attempt {} succeeded.", n);
break Ok::<_, anyhow::Error>(result);
}
Err(err) => {
let message = format!(
"[retry!] Attempt {}/{} failed with error: {:?}.",
n,
if max_attempts == 0 {
"inf".to_string()
} else {
max_attempts.to_string()
},
err,
);
if max_attempts > 0 && n >= max_attempts {
tracing::warn!("{} Stopping after {} attempts.", message, n);
anyhow::bail!(err);
}
let delay = (base_delay * (1 << (n - 1))).min(max_delay);
let delay = std::time::Duration::from_millis(delay);
tracing::debug!("{} Retrying in {:?}..", message, delay);
std::thread::sleep(delay);
n += 1;
}
}
}
}};
}