# tokio-retry2 Forked from https://github.com/srijs/rust-tokio-retry to keep it up-to-date Extensible, asynchronous retry behaviours for the ecosystem of [tokio](https://tokio.rs/) libraries. [![Crates.io](https://img.shields.io/crates/v/tokio-retry2.svg)](https://crates.io/crates/tokio-retry2) [![dependency status](https://deps.rs/repo/github/naomijub/tokio-retry/status.svg)](https://deps.rs/repo/github/namijub/tokio-retry) [![codecov](https://codecov.io/gh/naomijub/tokio-retry/branch/main/graph/badge.svg?token=4VMVTZTN8A)](https://codecov.io/gh/naomijub/tokio-retry) [Documentation](https://docs.rs/tokio-retry2) ## Installation Add this to your `Cargo.toml`: ```toml [dependencies] tokio-retry2 = { version = "0.5", features = ["jitter", "tracing"] } ``` ### Features: - `jitter`: adds jittery duration to the retry. Mechanism to avoid multiple systems retrying at the same time. - `tracing`: using `tracing` crate to indicate that a strategy has reached its `max_duration` or `max_delay`. ## Examples ```rust use tokio_retry2::{Retry, RetryError}; use tokio_retry2::strategy::{ExponentialBackoff, jitter, MaxInterval}; async fn action() -> Result> { // do some real-world stuff here... RetryError::to_transient(()) } #[tokio::main] async fn main() -> Result<(), ()> { let retry_strategy = ExponentialBackoff::from_millis(10) .factor(1) // multiplication factor applied to deplay .max_delay_millis(100) // set max delay between retries to 500ms .max_interval(10000) // set max interval to 10 seconds .map(jitter) // add jitter to delays .take(3); // limit to 3 retries let result = Retry::spawn(retry_strategy, action).await?; Ok(()) } ``` Or, to retry with a notification function: ```rust use tokio_retry2::{Retry, RetryError}; use tokio_retry2::strategy::{ExponentialBackoff, jitter, MaxInterval}; async fn action() -> Result> { // do some real-world stuff here... RetryError::to_permanent(()) // Early exits on this error } fn notify(err: &std::io::Error, duration: std::time::Duration) { tracing::info!("Error {err:?} occurred at {duration:?}"); } #[tokio::main] async fn main() -> Result<(), ()> { let retry_strategy = ExponentialBackoff::from_millis(10) .factor(1) // multiplication factor applied to deplay .max_delay_millis(100) // set max delay between retries to 500ms .max_interval(10000) // set max interval to 10 seconds .map(jitter) // add jitter to delays .take(3); // limit to 3 retries let result = Retry::spawn_notify(retry_strategy, action, notify).await?; Ok(()) } ``` ## Early Exit and Error Handling Actions must return a `RetryError` that can wrap any other error type. There are 2 `RetryError` error trypes: - `Permanent`, which receives an error and brakes the retry loop. It can be constructed manually or with auxiliary functions `RetryError::permanent(e: E)`, that returns a `RetryError::Permanent`, or `RetryError::to_permanent(e: E)`, that returns an `Err(RetryError::Permanent)`. - `Transient`, which is the **Default** error for the loop. It has 2 modes: 1. `RetryError::transient(e: E)` and `RetryError::to_transient(e: E)`, that return a `RetryError::Transient`, which is an error that triggers the retry strategy. 2. `RetryError::retry_after(e: E, duration: std::time::Duration)` and `RetryError::to_retry_after(e: E, duration: std::time::Duration)`, that return a `RetryError::Transient`, which is an error that triggers the retry strategy after the specified duration. - Thet is also the trait `MapErr` that possesses 2 auxiliary functions that map the current function Result to `Result>`: 1. `fn map_transient_err(self) -> Result>;` 2. `fn map_permanent_err(self) -> Result>;` - Using the `?` operator on an `Option` type will always propagate a `RetryError::Transient` with no extra duration. ## Retry Strategies breakdown: There are 4 backoff strategies: - `ExponentialBackoff`: base is considered the initial retry interval, so if defined from 500ms, the next retry will happen at 250000ms. | attempt | delay | |---------|-------| | 1 | 500ms | | 2 | 250000ms| - `ExponentialFactorBackoff`: this is a exponential backoff strategy with a base factor. What is exponentially configured is the factor, while the base retry delay is the same. So if a factor 2 is applied to an initial delay off 500ms, the attempts are as follows: | attempt | delay | |---------|-------| | 1 | 500ms | | 2 | 1000ms| | 3 | 2000ms| | 4 | 4000ms| - `FixedInterval`: in this backoff strategy, a fixed interval is used as constant. so if defined from 500ms, all attempts will happen at 500ms. | attempt | delay | |---------|-------| | 1 | 500ms| | 2 | 500ms| | 3 | 500ms| - `FibonacciBackoff`: a Fibonacci backoff strategy is used. so if defined from 500ms, the next retry will happen at 500ms, and the following will be at 1000ms. | attempt | delay | |---------|-------| | 1 | 500ms| | 2 | 500ms| | 3 | 1000ms| | 4 | 1500ms|