Skip to main content

tower_resilience_reconnect/
lib.rs

1//! Automatic reconnection middleware for Tower services.
2//!
3//! This crate provides reconnection functionality for Tower services with configurable
4//! backoff strategies, connection state management, and comprehensive event system.
5//!
6//! # Features
7//!
8//! - **Automatic reconnection**: Detect connection failures and reconnect automatically
9//! - **Flexible backoff**: Reuse `IntervalFunction` from retry module (exponential, linear, fixed)
10//! - **Connection state tracking**: Monitor connection health and reconnection attempts
11//! - **Event system**: Observability through reconnection events
12//! - **Idempotency control**: Optional retry of original request after reconnection
13//!
14//! # Reconnect vs Retry: When to Use Each
15//!
16//! ## Use Reconnect When:
17//!
18//! - Managing **persistent connections** (TCP, WebSocket, database connections, Redis)
19//! - You need **connection state tracking** (Connected/Disconnected/Reconnecting)
20//! - Errors indicate the **connection itself is broken** (not just a failed request)
21//! - You want to **reconnect automatically** but control whether to retry the operation
22//!
23//! ## Use Retry When:
24//!
25//! - Handling **transient operation failures** on a working connection
26//! - Errors are **request-level** (rate limiting, temporary server errors, timeouts)
27//! - You want to **retry the same operation** without reconnecting
28//! - Connection is fine, just the specific request failed
29//!
30//! ## Composing Both (Persistent Connections):
31//!
32//! For services with persistent connections (Redis, databases, gRPC), you often want BOTH:
33//!
34//! ```rust,no_run
35//! # use tower::ServiceBuilder;
36//! # use tower_resilience_reconnect::{ReconnectLayer, ReconnectConfig, ReconnectPolicy};
37//! # use std::time::Duration;
38//! # async fn example() {
39//! # let redis_connection = tower::service_fn(|_req: ()| async { Ok::<_, std::io::Error>(()) });
40//! let service = ServiceBuilder::new()
41//!     // Outer: Retry transient request errors
42//!     // (future: use tower_resilience_retry when available)
43//!     // Inner: Reconnect on connection failures
44//!     .layer(ReconnectLayer::new(
45//!         ReconnectConfig::builder()
46//!             .policy(ReconnectPolicy::exponential(
47//!                 Duration::from_millis(100),
48//!                 Duration::from_secs(5),
49//!             ))
50//!             .retry_on_reconnect(false)  // Let outer retry layer handle it
51//!             .build()
52//!     ))
53//!     .service(redis_connection);
54//! # }
55//! ```
56//!
57//! This provides:
58//! - **Connection resilience** via reconnect layer (handles broken pipes, connection resets)
59//! - **Operation resilience** via retry layer (handles rate limits, temporary errors)
60//! - **Clear separation** of concerns
61//! - **Fine-grained control** over idempotency
62//!
63//! # Examples
64//!
65//! ## Basic Reconnect with Exponential Backoff
66//!
67//! ```rust
68//! use tower_resilience_reconnect::{ReconnectLayer, ReconnectConfig, ReconnectPolicy};
69//! use std::time::Duration;
70//!
71//! // Create reconnect configuration
72//! let config = ReconnectConfig::builder()
73//!     .policy(ReconnectPolicy::exponential(
74//!         Duration::from_millis(100),
75//!         Duration::from_secs(5),
76//!     ))
77//!     .max_attempts(10)
78//!     .retry_on_reconnect(true)  // Safe for idempotent operations
79//!     .build();
80//!
81//! // Create the layer
82//! let reconnect_layer = ReconnectLayer::new(config);
83//! ```
84//!
85//! ## Non-Idempotent Operations
86//!
87//! For operations that should not be automatically retried (e.g., Redis INCR, LPUSH):
88//!
89//! ```rust
90//! use tower_resilience_reconnect::{ReconnectLayer, ReconnectConfig, ReconnectPolicy};
91//! use std::time::Duration;
92//!
93//! let config = ReconnectConfig::builder()
94//!     .policy(ReconnectPolicy::exponential(
95//!         Duration::from_millis(100),
96//!         Duration::from_secs(5),
97//!     ))
98//!     .retry_on_reconnect(false)  // Reconnect but DON'T retry the operation
99//!     .build();
100//!
101//! let layer = ReconnectLayer::new(config);
102//!
103//! // User handles the error and decides whether retry is safe:
104//! // - Was the operation executed before the connection died?
105//! // - Can we safely retry without duplicating side effects?
106//! ```
107
108mod config;
109mod layer;
110mod policy;
111mod service;
112mod state;
113
114pub use config::{ReconnectConfig, ReconnectConfigBuilder, ReconnectPredicate};
115pub use layer::ReconnectLayer;
116pub use policy::ReconnectPolicy;
117pub use service::ReconnectService;
118pub use state::{ConnectionState, ReconnectState};
119
120// Re-export backoff strategies from retry crate for convenience
121pub use tower_resilience_retry::{
122    ExponentialBackoff, ExponentialRandomBackoff, FixedInterval, IntervalFunction,
123};