Expand description
Crate retry
provides utilities for retrying operations that can fail.
Usage
Retry an operation using the retry
function. retry
accepts an iterator over
Duration
s and a closure that returns a Result
(or OperationResult
; see below). The
iterator is used to determine how long to wait after each unsuccessful try and how many times to
try before giving up and returning Result::Err
. The closure determines either the final
successful value, or an error value, which can either be returned immediately or used to
indicate that the operation should be retried.
Any type that implements Iterator
with an associated Item
type of Duration
can be
used to determine retry behavior, though a few useful implementations are provided in the
delay
module, including a fixed delay and exponential backoff.
let mut collection = vec![1, 2, 3].into_iter();
let result = retry(Fixed::from_millis(100), || {
match collection.next() {
Some(n) if n == 3 => Ok("n is 3!"),
Some(_) => Err("n must be 3!"),
None => Err("n was never 3!"),
}
});
assert!(result.is_ok());
The Iterator
API can be used to limit or modify the delay strategy. For example, to limit
the number of retries to 1:
let mut collection = vec![1, 2, 3].into_iter();
let result = retry(Fixed::from_millis(100).take(1), || {
match collection.next() {
Some(n) if n == 3 => Ok("n is 3!"),
Some(_) => Err("n must be 3!"),
None => Err("n was never 3!"),
}
});
assert!(result.is_err());
To apply random jitter to any delay strategy, the delay::jitter
function can be used in
combination with the Iterator
API:
let mut collection = vec![1, 2, 3].into_iter();
let result = retry(Exponential::from_millis(10).map(jitter).take(3), || {
match collection.next() {
Some(n) if n == 3 => Ok("n is 3!"),
Some(_) => Err("n must be 3!"),
None => Err("n was never 3!"),
}
});
assert!(result.is_ok());
To deal with fatal errors, return OperationResult
, which is like Result
, but with a
third case to distinguish between errors that should cause a retry and errors that should
immediately return, halting retry behavior. (Internally, OperationResult
is always used, and
closures passed to retry
that return plain Result
are converted into
OperationResult
.)
use retry::OperationResult;
let mut collection = vec![1, 2].into_iter();
let value = retry(Fixed::from_millis(1), || {
match collection.next() {
Some(n) if n == 2 => OperationResult::Ok(n),
Some(_) => OperationResult::Retry("not 2"),
None => OperationResult::Err("not found"),
}
}).unwrap();
assert_eq!(value, 2);
If your operation needs to know how many times it’s been tried, use the retry_with_index
function. This works the same as retry
, but passes the number of the current try to the
closure as an argument.
let mut collection = vec![1, 2, 3, 4, 5].into_iter();
let result = retry_with_index(Fixed::from_millis(100), |current_try| {
if current_try > 3 {
return OperationResult::Err("did not succeed within 3 tries");
}
match collection.next() {
Some(n) if n == 5 => OperationResult::Ok("n is 5!"),
Some(_) => OperationResult::Retry("n must be 5!"),
None => OperationResult::Retry("n was never 5!"),
}
});
assert!(result.is_err());
Features
random
: offer some random delay utilities (on by default)