maybe_backoff/maybe_backoff.rs
1use crate::{backoff::Backoff as _, ExponentialBackoff, ExponentialBackoffBuilder};
2use std::time::Duration;
3
4/// `MaybeBackoff` provides a simplified way to manage an optional exponential backoff while giving control over when to wait.
5///
6/// # Example
7/// ```rust,no_run,ignore
8/// let mut backoff = MaybeBackoff::default();
9///
10/// // Loop that runs fallible operation that should be retried with backoff.
11/// loop {
12/// backoff.sleep().await; // Does nothing when not armed (disarmed by default).
13/// backoff.arm();
14///
15/// while let Some(event) = event_source.next().await {
16/// match event {
17/// Ok(Event::Open) => debug!("Connected!"),
18/// Ok(Event::Message(event)) => match parse(event) {
19/// Ok(data) => {
20/// backoff.disarm();
21/// forward_data(data).await;
22/// break;
23/// }
24/// Err(error) => {
25/// error!("Parsing failed: {error:?}");
26/// event_source.close();
27/// continue;
28/// }
29/// },
30/// Err(error) => {
31/// error!("Event source failed: {error:?}");
32/// event_source.close();
33/// continue;
34/// }
35/// }
36/// }
37/// }
38/// ```
39#[derive(Default)]
40pub struct MaybeBackoff {
41 backoff: Option<ExponentialBackoff>,
42}
43
44impl MaybeBackoff {
45 pub fn arm(&mut self) {
46 if self.backoff.is_none() {
47 self.backoff = Some(
48 ExponentialBackoffBuilder::new()
49 .with_initial_interval(Duration::from_millis(50))
50 .with_max_interval(Duration::from_secs(3))
51 .with_multiplier(1.5)
52 .with_randomization_factor(0.2)
53 .build(),
54 )
55 }
56 }
57
58 pub fn disarm(&mut self) {
59 self.backoff = None;
60 }
61
62 pub async fn sleep(&mut self) {
63 if let Some(duration) = self.backoff.as_mut().and_then(|b| b.next_backoff()) {
64 #[cfg(all(not(target_arch = "wasm32"), not(feature = "tokio")))]
65 std::thread::sleep(duration);
66 #[cfg(all(not(target_arch = "wasm32"), feature = "tokio"))]
67 tokio_1::time::sleep(duration).await;
68 #[cfg(target_arch = "wasm32")]
69 gloo::timers::future::TimeoutFuture::new(duration.as_millis().try_into().unwrap())
70 .await;
71 }
72 }
73}