Skip to main content

Crate tower_resilience_retry

Crate tower_resilience_retry 

Source
Expand description

Enhanced retry middleware for Tower services.

This crate provides advanced retry functionality beyond Tower’s built-in retry, with flexible backoff strategies, retry predicates, and comprehensive event system.

§Features

  • IntervalFunction abstraction: Pluggable backoff strategies
    • Fixed interval
    • Exponential backoff with configurable multiplier
    • Exponential random backoff with randomization factor
    • Custom function-based backoff
  • Per-request configuration: Extract max attempts from the request
  • Retry predicates: Control which errors should be retried
  • Event system: Observability through retry events
  • Flexible configuration: Builder API with sensible defaults

§Examples

§Basic Retry with Exponential Backoff

use tower_resilience_retry::RetryLayer;
use tower::ServiceBuilder;
use std::time::Duration;

// Create retry layer with exponential backoff
let retry_layer = RetryLayer::<String, String, MyError>::builder()
    .max_attempts(5)
    .exponential_backoff(Duration::from_millis(100))
    .on_retry(|attempt, delay| {
        println!("Retry attempt {} after {:?}", attempt, delay);
    })
    .build();

// Apply to a service
let service = ServiceBuilder::new()
    .layer(retry_layer)
    .service(tower::service_fn(|req: String| async move {
        Ok::<_, MyError>(format!("Response: {}", req))
    }));

§Per-Request Max Attempts

Extract retry configuration from the request itself:

use tower_resilience_retry::RetryLayer;
use tower::ServiceBuilder;
use std::time::Duration;

#[derive(Clone)]
struct MyRequest {
    is_idempotent: bool,
    data: String,
}

// Idempotent requests can retry more aggressively
let retry_layer = RetryLayer::<MyRequest, (), MyError>::builder()
    .max_attempts_fn(|req: &MyRequest| {
        if req.is_idempotent { 5 } else { 1 }
    })
    .exponential_backoff(Duration::from_millis(100))
    .build();

§Fallback After Retry Exhaustion

When retries are exhausted, you can provide a fallback response using standard error handling:

use tower_resilience_retry::RetryLayer;
use tower::{Service, ServiceBuilder, ServiceExt};
use std::time::Duration;

let retry_layer = RetryLayer::<String, String, MyError>::builder()
    .max_attempts(3)
    .exponential_backoff(Duration::from_millis(100))
    .build();

let mut service = ServiceBuilder::new()
    .layer(retry_layer)
    .service(tower::service_fn(|req: String| async move {
        Err::<String, MyError>(MyError) // Always fails
    }));

// Handle retry exhaustion with fallback
let result = service.ready().await?.call("request".to_string()).await
    .unwrap_or_else(|_| "Fallback: Service unavailable".to_string());

§Fallback with Cached Data

use tower_resilience_retry::RetryLayer;
use tower::{Service, ServiceBuilder, ServiceExt};
use std::time::Duration;
use std::sync::Arc;
use std::collections::HashMap;

let cache = Arc::new(std::sync::RwLock::new(HashMap::new()));
cache.write().unwrap().insert("key", "cached value");

let retry_layer = RetryLayer::<String, String, MyError>::builder()
    .max_attempts(3)
    .exponential_backoff(Duration::from_millis(50))
    .build();

let mut service = ServiceBuilder::new()
    .layer(retry_layer)
    .service(tower::service_fn(|req: String| async move {
        Err::<String, MyError>(MyError)
    }));

let cache_clone = Arc::clone(&cache);
let result = service.ready().await?.call("key".to_string()).await
    .unwrap_or_else(|_| {
        cache_clone.read().unwrap()
            .get("key")
            .map(|s| s.to_string())
            .unwrap_or_else(|| "Default value".to_string())
    });

Structs§

AimdBudget
AIMD (Additive Increase Multiplicative Decrease) retry budget.
ExponentialBackoff
Exponential backoff with configurable multiplier.
ExponentialRandomBackoff
Exponential backoff with randomization to prevent thundering herd.
FixedInterval
Fixed interval backoff - returns the same duration for every retry.
FnInterval
Function-based interval implementation.
Retry
A Tower Service that retries failed requests.
RetryBudgetBuilder
Builder for creating retry budgets.
RetryConfig
Configuration for the retry middleware.
RetryConfigBuilder
Builder for RetryConfig.
RetryLayer
A Tower Layer that applies retry logic to a service.
RetryPolicy
Policy for retry behavior.
TokenBucketBudget
Token bucket retry budget.

Enums§

MaxAttemptsSource
Source for determining the maximum number of retry attempts.
RetryEvent
Events emitted by the retry middleware.

Traits§

IntervalFunction
Abstraction for computing retry intervals.
RetryBudget
A budget that controls how many retries are allowed.

Type Aliases§

ResponsePredicate
Determines whether a successful response should be retried.
RetryPredicate
Determines whether an error should be retried.