1 unstable release
0.1.0 | Dec 19, 2023 |
---|
#818 in Rust patterns
1,499 downloads per month
24KB
419 lines
retrying
General-purpose retrying library, written in Rust, to simplify the task of adding retry behavior to rust functions.
Support sync and async (tokio, async-std) functions.
[dependencies]
retrying = "0.1.0"
Macros
The main public interface in retrying is retrying::retry
macros.
#[retrying::retry]
fn my_function(){}
This macros has a lot of configuration options and allows developers to write fault tolerant functions without thinking about implementation of retry functionality.
⚠️ The macros generates code and adds variables with prefix retrying_
into code. To avoid variable conflicts please don't use variables with this prefix in functions with retry
macros.
Configuration option
-
Stop
This section describes configuration options that specify when method execution should stop retrying.
Config option | OS Environments | Default | Description |
---|---|---|---|
stop=attempts(u32 ) |
{PREFIX}__RETRYING__STOP__ATTEMPTS | - | Number of retries |
stop=delay(f32 ) |
{PREFIX}__RETRYING__STOP__DELAY | - | Retrying period (seconds) |
It is possible to combine several stop conditions by using the or operator(|
) operator. For example, configuration
#[retrying::retry(stop=(attempts(10)|delay(60.8)))]
fn my_function(){}
means the function should retry 10 times but doesn't make new attempt after 60 seconds.
If stop configuration is not specified then retry macros makes new attempts until function be finished without Err.
-
Wait
This section describes configuration options that specify delay between each attempt.
Config option | OS Environments | Default | Description |
---|---|---|---|
wait=fixed(f32 ) |
{PREFIX}__RETRYING__WAIT__FIXED | 0 | Number of seconds between retries |
wait=random(min=f32 , max=f32 ) |
{PREFIX}__RETRYING__WAIT__RANDOM__(MIN|MAX) | min=0,max=3600 | Randomly wait min to max seconds between retries |
wait=exponential(multiplier=f32 , min=f32 , max=f32 , exp_base=u32 ) |
{PREFIX}__RETRYING__WAIT__EXPONENTIAL__(MULTIPLIER|MIN|MAX|EXP_BASE) | multiplier=1, min=0, max=3600, exp_base=2 | Wait multiplier * exp_base^(num of retry - 1) + min seconds between each retry starting with min seconds, then up to max seconds, then max seconds afterwards |
Using only one wait option is possible.
-
Retry
This section describes configuration options that specify retrying conditions.
Config option | OS Environments | Default | Description |
---|---|---|---|
retry=if_errors(error_1, error_2, error_2) | Not applicable | - | Retry only on specific errors |
retry=if_not_errors(error_1, error_2, error_3) | Not applicable | - | Don't retry on specific errors |
Using only one retry option is possible.
Using OS environment variables for updating retry configuration
There are certain list of use cases when retry configuration requires updating configuration values in runtime. For example, It is useful when we need a different number of attempts per environment (dev, prod, stage), systems, unit tests etc.
Retrying allows overriding macros configuration in runtime using env variables with special configuration option envs_prefix
like
#[retrying::retry(<retry configurations>,envs_prefix="test")]
⚠️ Limitations
- It is possible to override only configuration value, not configuration option. It means, for example, if configuration option
stop=attempts(1))
is not defined in macros code then the OS env variable{PREFIX}__RETRYING__STOP__ATTEMPTS
doesn't affect code execution. In other words, the OS environment variable can override only the value of the configured option and it is not able to change the option itself. - Configuration option from the OS environment variable has a higher priority than options in source code.
- If OS environment variables are not set then macros uses the value from its configuration (source code).
- If OS environment variable has the wrong format (for example, non-numeric value is specified for numeric configuration) then retrying macros ignores such configuration, logs error in stderr and continues using values from code.
Example of usage:
#[retrying::retry(stop=attempts(2),envs_prefix="test")]
With above configuration macros checks in runtime the availability of OS env variable TEST__RETRYING__STOP__ATTEMPTS (case-insensitive) and if variable is set then number of retry attempt will be the value of TEST__RETRYING__STOP__ATTEMPTS. If the list of OS environment contains more than one configuration option with the same prefix then macros ignores OS env variable and take configuration value from code.
Features
tokio
- builds retrying library for using with tokio asynchronous runtime.
async_std
- builds retrying library for using with async_std asynchronous runtime.
Examples
Examples are available in ./crates/retrying/example and can be tested using cargo. Sync:
cargo run --example sync
Async tokio:
cargo run --features="tokio" --example tokio
Async async-std:
cargo run --features="async_std" --example async_std
Understanding of macros
retrying::retry
macros is not a magic and it only helps developers to avoid writing extra code.
The resulting code depends on the provided configuration and may be changed in future releases.
Examples of code generation:
#[retry(stop=(attempts(4)|duration(2)),wait=fixed(0.9))]
fn my_method(in_param: &str) -> Result<i32, ParseIntError> {
in_param.parse::<i32>()
}
Generated code:
fn my_method(in_param: &str) -> Result<i32, ParseIntError> {
let mut retrying_context = ::retrying::RetryingContext::new();
use ::retrying::stop::Stop;
let retrying_stop =
::retrying::stop::StopAttemptsOrDuration::new(4u32, 2f32);
use ::retrying::wait::Wait;
let retrying_wait = ::retrying::wait::WaitFixed::new(0.9f32);
loop {
match { in_param.parse::<i32>() } {
Ok(result) => return Ok(result),
Err(err) if !retrying_stop.stop_execution(&retrying_context) => {
match err {
std::num::ParseIntError { .. } => (),
_ => break Err(err),
};
retrying_context.add_attempt();
::retrying::sleep_sync(retrying_wait.wait_duration(&retrying_context));
}
Err(err) => break Err(err),
}
}
}
It is possible to use cargo for checking generated code. For examples, below command shows generated code for all examples,
cd ./crates/retrying
cargo rustc --profile=check --examples -- -Zunpretty=expanded
Dependencies
~0.5–11MB
~129K SLoC