Expand description
§keen-retry
§Introduction
The keen-retry crate is designed to provide a zero-cost, flexible, and robust way to implement retry logic in Rust applications, while also
supporting adding resiliency to libraries. Whether you are developing an API, a high-performance system, a distributed application, or a simple
tool, keen-retry offers a comprehensive set of features to handle transient failures gracefully, ensuring your application remains resilient,
reliable and is able to provide rich diagnostic messages, indispensable for addressing the root cause of failures.
§Features
- Zero-Cost Abstraction: Leverages Rust’s powerful type system and compile-time optimizations to offer retry capabilities with no runtime overhead.
- Clear Error Discrimination: Retrying operations that fail due to non-transient errors is futile, can waste resources and may ruin the application performance.
- Instrumentation and Logging: Comprehensive logging and instrumentation features for observing and debugging retry operations.
- Composable Retry Logic: Easily chainable and composable retry operations, allowing for clean and maintainable code.
- Async/Await Support: First-class support for asynchronous programming, compatible with Tokio and other async runtimes.
- Flexible Backoff Strategies: Includes various backoff strategies, from simple constant delays to sophisticated exponential backoff with jitter, suitable for different scenarios and needs.
§Quick Start
§Integrate your Library / API
The first step is to have every retryable operation from your Library or API returning the enriched RetryResult type, which clearly discriminates between Ok, Fatal and Transient variants:
/// Wrapper around [Self::connect_to_server_raw()], enabling `keen-retry` on it
pub async fn connect_to_server(&self) -> RetryProcedureResult<ConnectionErrors> {
self.connect_to_server_raw().await
.map_or_else(|error| match error.is_fatal() {
true => RetryResult::Fatal { input: (), error },
false => RetryResult::Transient { input: (), error },
},
|_| RetryResult::Ok { reported_input: (), output: () })
}§Usage
Now, in the application, you may use it via the zero-cost functional API:
let resolved = connect_to_server()
.retry_with(|_| connect_to_server())
.<one-of-the-backoff-strategies>(...)
.<instrumentation-facilities>(...)
.<mapping-of-outputs-and-errors>(...);§The keen-retry Diagram

For more details, please refer to tests/use_cases.rs, which contains advanced
demonstrations such as how to add a fully fledged instrumentation (as seen in production applications),
how to compose nested retry logics and how to implement the versatile “Partial Completion with Continuation
Closure” design pattern.
§Performance Analysis
keen-retry has been rigorously benchmarked to ensure it adheres to the zero-cost abstraction principle, crucial in systems programming.
Our benchmarks, available at benches/zero_cost_abstractions.rs, demonstrate the efficiency of the crate.

§The Book
For a deep dive into the applicable Design Patterns, principles, strategies, and best practices for using keen-retry effectively,
be sure to explore our companion keen-retry crate’s Book, which serves as a definitive guide, providing insights and practical
examples to harness the full potential of keen-retry in various software development scenarios.
Modules§
- keen_
retry_ async_ executor - Resting place for KeenRetryAsyncExecutor.
Keep this in sync with ../keen_retry_executor.rs - keen_
retry_ executor - Resting place for KeenRetryExecutor.\
Enums§
- Exponential
Jitter - Configuration options for the “Exponential with Random Jitter” backoff strategy
- Resolved
Result - Contains all possibilities for finished retryable operations – conversible to
Result<>– and some nice facilities for instrumentation (like building a succinct report of the retry errors).
This “Final Result” is a “Second Level” of result for an operation: it represents operations that where enabled to pass through thekeen-retryretrying logic.
See also crate::RetryResult, for the “First Level” of results. - Retry
Result - An extension over the original std
Result<Ok, Err>, introducing a third kind: Transient failures – which are elligible for retry attempts: this may be considered the “First Level” of results, mapping directly from raw operation results.
Considering zero-copy, bothTransient&Fatalvariants will contain the original input payload, which is consumed by anOkoperation; TheOkoperation, on the other hand, has the outcome result and may have an excerpt of the input, for instrumentation purposes.
See also crate::RetryResult, for the “Second Level” of results – after passing through some possible retry re-attempts.
Functions§
- errors_
to_ occurrences_ count - Consumes both
retry_errorsandfatal_error(from failed ResolvedResult and returns back a hashmap of error occurrence counts in the form: - exponential_
jitter_ from_ exponent - Generates an iterator suitable for usage in backoff strategies for operations that recruit external / shared resources – such as network services.
Its elements progress exponentially from the given
initial_backoff_milliswith theexponentratio applied to each progression, up tore_attemptssteps – each of which may be added / subtracted byjitter_ratio*backoff_millis``.\ As a special case, if theinitial_backoff_millis` starts with 0, the first element in the geometric progression will be 0 and the rest of the progression will continue as if it had started with 1 – allowing for zero backoff on the first attempt, which might make sense in highly distributed systems with really low fault rates. See also exponential_jitter_from_range() - exponential_
jitter_ from_ range - Generates an iterator suitable for usage in backoff strategies for operations that recruit external / shared resources – such as network services.
Its elements progress exponentially from the given
range_millisstart range, going from the first to the last element inre_attemptssteps – each of which may be added / subtracted byjitter_ratio*backoff_millis.
Notice that this method calculates theexponentfrom the given parameters.
As a special case, if the range – which is expressed in milliseconds – starts with 0, the first element in the geometric progression will be 0 and the rest of the progression will continue as if it had started with 1 – allowing for zero backoff on the first attempt, which might make sense in highly distributed systems with really low fault rates.
See also exponential_jitter_from_exponent() - loggable_
retry_ errors - Builds an as-short-as-possible list of
retry_errorsoccurrences (out of order), providedErrorTypeimplements theDebugtrait.
Type Aliases§
- Retry
Consumer Result - Suggar type for when an operation doesn’t produce outputs
- Retry
Procedure Result - Suggar type for when an operation doesn’t consume its inputs nor produce outputs
- Retry
Producer Result - Suggar type for when an operation doesn’t consume its inputs
