backoff/
lib.rs

1#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3
4//! `ExponentialBackoff` is a backoff implementation that increases the backoff
5//! period for each retry attempt using a randomization function that grows exponentially.
6//!
7//! [`next_backoff`]: backoff/trait.Backoff.html#tymethod.next_backoff
8//! [`reset`]: backoff/trait.Backoff.html#tymethod.reset
9//!
10//! [`next_backoff`] is calculated using the following formula:
11//!
12//!```text
13//!  randomized interval =
14//!      retry_interval * (random value in range [1 - randomization_factor, 1 + randomization_factor])
15//!```
16//!
17//! In other words [`next_backoff`] will range between the randomization factor
18//! percentage below and above the retry interval.
19//!
20//! For example, given the following parameters:
21//!
22//!```text
23//!retry_interval = 2
24//!randomization_factor = 0.5
25//!multiplier = 2
26//!```
27//!
28//! the actual backoff period used in the next retry attempt will range between 1 and 3 seconds,
29//! multiplied by the exponential, that is, between 2 and 6 seconds.
30//!
31//! **Note**: `max_interval` caps the `retry_interval` and not the randomized interval.
32//!
33//! If the time elapsed since an [`ExponentialBackoff`](type.ExponentialBackoff.html) instance is created goes past the
34//! `max_elapsed_time`, then the method [`next_backoff`] starts returning `None`.
35//!
36//! The elapsed time can be reset by calling [`reset`].
37//!
38//! Example: Given the following default arguments, for 10 tries the sequence will be,
39//! and assuming we go over the `max_elapsed_time` on the 10th try:
40//!
41//!   Request # | `retry_interval` (seconds) |  Randomized Interval (seconds)
42//!  -----------|--------------------------|--------------------------------
43//!    1        |  0.5                     | [0.25,   0.75]
44//!    2        |  0.75                    | [0.375,  1.125]
45//!    3        |  1.125                   | [0.562,  1.687]
46//!    4        |  1.687                   | [0.8435, 2.53]
47//!    5        |  2.53                    | [1.265,  3.795]
48//!    6        |  3.795                   | [1.897,  5.692]
49//!    7        |  5.692                   | [2.846,  8.538]
50//!    8        |  8.538                   | [4.269, 12.807]
51//!    9        | 12.807                   | [6.403, 19.210]
52//!   10        | 19.210                   | None
53//!
54//! # Examples
55//!
56//! ## Permanent errors
57//!
58//! Permanent errors are not retried. You have to wrap your error value explicitly
59//! into `Error::Permanent`. You can use `Result`'s `map_err` method.
60//!
61//! `examples/permanent_error.rs`:
62//!
63//! ```rust,no_run
64//! use backoff::{Error, ExponentialBackoff};
65//! use reqwest::Url;
66//!
67//! use std::fmt::Display;
68//! use std::io::{self, Read};
69//!
70//! fn new_io_err<E: Display>(err: E) -> io::Error {
71//!     io::Error::new(io::ErrorKind::Other, err.to_string())
72//! }
73//!
74//! fn fetch_url(url: &str) -> Result<String, Error<io::Error>> {
75//!     let op = || {
76//!         println!("Fetching {}", url);
77//!         let url = Url::parse(url)
78//!             .map_err(new_io_err)
79//!             // Permanent errors need to be explicitly constructed.
80//!             .map_err(Error::Permanent)?;
81//!
82//!         let mut resp = reqwest::blocking::get(url)
83//!             // Transient errors can be constructed with the ? operator
84//!             // or with the try! macro. No explicit conversion needed
85//!             // from E: Error to backoff::Error;
86//!             .map_err(new_io_err)?;
87//!
88//!         let mut content = String::new();
89//!         let _ = resp.read_to_string(&mut content);
90//!         Ok(content)
91//!     };
92//!
93//!     let backoff = ExponentialBackoff::default();
94//!     backoff::retry(backoff, op)
95//! }
96//!
97//! fn main() {
98//!     match fetch_url("https::///wrong URL") {
99//!         Ok(_) => println!("Successfully fetched"),
100//!         Err(err) => panic!("Failed to fetch: {}", err),
101//!     }
102//! }
103//! ```
104//!
105//! ## Transient errors
106//!
107//! Transient errors can be constructed by wrapping your error value into `Error::transient`.
108//! By using the ? operator or the `try!` macro, you always get transient errors.
109//!
110//! You can also construct transient errors that are retried after a given
111//! interval with `Error::retry_after()` - useful for 429 errors.
112//!
113//! `examples/retry.rs`:
114//!
115//! ```rust
116//! use backoff::{retry, Error, ExponentialBackoff};
117//!
118//! use std::io::Read;
119//!
120//! fn fetch_url(url: &str) -> Result<String, Error<reqwest::Error>> {
121//!     let mut op = || {
122//!         println!("Fetching {}", url);
123//!         let mut resp = reqwest::blocking::get(url)?;
124//!
125//!         let mut content = String::new();
126//!         let _ = resp.read_to_string(&mut content);
127//!         Ok(content)
128//!     };
129//!
130//!     let backoff = ExponentialBackoff::default();
131//!     retry(backoff, op)
132//! }
133//!
134//! fn main() {
135//!     match fetch_url("https://www.rust-lang.org") {
136//!         Ok(_) => println!("Sucessfully fetched"),
137//!         Err(err) => panic!("Failed to fetch: {}", err),
138//!     }
139//! }
140//! ```
141//!
142//! Output with internet connection:
143//!
144//! ```text
145//! $ time cargo run --example retry
146//!    Compiling backoff v0.1.0 (file:///home/tibi/workspace/backoff)
147//!     Finished dev [unoptimized + debuginfo] target(s) in 1.54 secs
148//!      Running `target/debug/examples/retry`
149//! Fetching https://www.rust-lang.org
150//! Sucessfully fetched
151//!
152//! real    0m2.003s
153//! user    0m1.536s
154//! sys    0m0.184s
155//! ```
156//!
157//! Output without internet connection
158//!
159//! ```text
160//! $ time cargo run --example retry
161//!     Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
162//!      Running `target/debug/examples/retry`
163//! Fetching https://www.rust-lang.org
164//! Fetching https://www.rust-lang.org
165//! Fetching https://www.rust-lang.org
166//! Fetching https://www.rust-lang.org
167//! ^C
168//!
169//! real    0m2.826s
170//! user    0m0.008s
171//! sys    0m0.000s
172//! ```
173//!
174//! ### Async
175//!
176//! Please set either the `tokio` or `async-std` features in Cargo.toml to enable the async support of this library, i.e.:
177//!
178//! ```toml
179//! backoff = { version = "x.y.z", features = ["tokio"] }
180//! ```
181//!
182//! A `Future<Output = Result<T, backoff::Error<E>>` can be easily retried:
183//!
184//! `examples/async.rs`:
185//!
186//! ```rust,no_run,ignore
187//!
188//! extern crate tokio_1 as tokio;
189//!
190//! use backoff::ExponentialBackoff;
191//!
192//! async fn fetch_url(url: &str) -> Result<String, reqwest::Error> {
193//!     backoff::future::retry(ExponentialBackoff::default(), || async {
194//!         println!("Fetching {}", url);
195//!         Ok(reqwest::get(url).await?.text().await?)
196//!     })
197//!     .await
198//! }
199//!
200//! #[tokio::main]
201//! async fn main() {
202//!     match fetch_url("https://www.rust-lang.org").await {
203//!         Ok(_) => println!("Successfully fetched"),
204//!         Err(err) => panic!("Failed to fetch: {}", err),
205//!     }
206//! }
207//! ```
208//! # Feature flags
209//!
210//! - `futures`: enables futures support,
211//! - `tokio`: enables support for the [tokio](https://crates.io/crates/tokio) async runtime, implies `futures`,
212//! - `async-std`: enables support for the [async-std](https://crates.io/crates/async-std) async runtime, implies `futures`,
213//! - `wasm-bindgen`: enabled support for [wasm-bindgen](https://crates.io/crates/wasm-bindgen).
214
215pub mod backoff;
216mod clock;
217pub mod default;
218mod error;
219pub mod exponential;
220
221#[cfg(feature = "futures")]
222#[cfg_attr(docsrs, doc(cfg(feature = "futures")))]
223pub mod future;
224
225mod retry;
226
227pub use crate::clock::{Clock, SystemClock};
228pub use crate::error::Error;
229pub use crate::retry::{retry, retry_notify, Notify};
230
231/// Exponential backoff policy with system's clock.
232///
233/// This type is preferred over
234/// `exponential::ExponentialBackoff` as it is generic over any [Clocks](trait.Clock.html)
235/// and in the real world mostly system's clock is used.
236pub type ExponentialBackoff = exponential::ExponentialBackoff<SystemClock>;
237
238/// Builder for exponential backoff policy with system's clock.
239pub type ExponentialBackoffBuilder = exponential::ExponentialBackoffBuilder<SystemClock>;