Skip to main content

miden_node_utils/
retry.rs

1//! Shared retry/backoff helpers built on top of the [`backon`] crate.
2//!
3//! These constructors give the node a single definition of the "standard" backoff schedules so
4//! that retry behaviour stays consistent across components instead of being re-derived at each call
5//! site. Use them together with the [`Retryable`] suffix extension, e.g.
6//!
7//! ```ignore
8//! use miden_node_utils::retry::{self, Retryable};
9//! use miden_node_utils::tracing::warn;
10//!
11//! let value = (|| async { do_thing().await })
12//!     .retry(retry::exponential(min, max))
13//!     .when(|err| is_transient(err))
14//!     .notify(|err, dur| {
15//!         warn!(
16//!             err,
17//!             "retrying",
18//!             retry.delay_ms = dur.as_millis() as u64
19//!         );
20//!     })
21//!     .await?;
22//! ```
23
24use std::time::Duration;
25
26pub use backon::{BackoffBuilder, Retryable, RetryableWithContext};
27use backon::{ConstantBuilder, ExponentialBuilder};
28
29// BACKOFF BUILDERS
30// ================================================================================================
31
32/// Builds an exponential backoff schedule that retries indefinitely.
33///
34/// Delays start at `min`, double on each attempt (factor `2.0`), are capped at `max`, and have
35/// jitter applied to spread out concurrent retriers.
36pub fn exponential(min: Duration, max: Duration) -> ExponentialBuilder {
37    ExponentialBuilder::default()
38        .with_min_delay(min)
39        .with_max_delay(max)
40        .with_factor(2.0)
41        .with_jitter()
42        .without_max_times()
43}
44
45/// Same as [`exponential`], but stops after `max_times` retries (i.e. `max_times + 1` total
46/// attempts).
47pub fn exponential_bounded(min: Duration, max: Duration, max_times: usize) -> ExponentialBuilder {
48    exponential(min, max).with_max_times(max_times)
49}
50
51/// Builds a constant-delay backoff schedule.
52///
53/// `max_times` bounds the number of retries; pass `None` to retry indefinitely.
54pub fn constant(delay: Duration, max_times: Option<usize>) -> ConstantBuilder {
55    let builder = ConstantBuilder::default().with_delay(delay);
56    match max_times {
57        Some(max_times) => builder.with_max_times(max_times),
58        None => builder.without_max_times(),
59    }
60}