From cdbcef7e923ccd5c623cadc319186eba04824668 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 5 Feb 2026 03:38:21 +0000 Subject: [PATCH 1/4] Update documentation versions and add Resilience Patterns recipe - Update `version = "0.1"` to `version = "0.1.275"` in `rustapi-extras`, `rustapi-rs`, and `rustapi-toon` documentation. - Add `docs/cookbook/src/recipes/resilience.md` covering Circuit Breaker, Retry, and Timeout layers. - Update `docs/cookbook/src/learning/README.md` to link to the new Resilience recipe in the "Enterprise Platform" path. - Add "Resilience Patterns" to `docs/cookbook/src/SUMMARY.md`. Co-authored-by: Tuntii <121901995+Tuntii@users.noreply.github.com> --- crates/rustapi-extras/src/lib.rs | 2 +- crates/rustapi-rs/src/lib.rs | 2 +- crates/rustapi-toon/src/lib.rs | 2 +- docs/cookbook/src/SUMMARY.md | 1 + docs/cookbook/src/learning/README.md | 4 +- docs/cookbook/src/recipes/resilience.md | 123 ++++++++++++++++++++++++ 6 files changed, 130 insertions(+), 4 deletions(-) create mode 100644 docs/cookbook/src/recipes/resilience.md diff --git a/crates/rustapi-extras/src/lib.rs b/crates/rustapi-extras/src/lib.rs index 2b2d2c8..d71992a 100644 --- a/crates/rustapi-extras/src/lib.rs +++ b/crates/rustapi-extras/src/lib.rs @@ -36,7 +36,7 @@ //! //! ```toml //! [dependencies] -//! rustapi-extras = { version = "0.1", features = ["jwt", "cors", "csrf"] } +//! rustapi-extras = { version = "0.1.275", features = ["jwt", "cors", "csrf"] } //! ``` #![warn(missing_docs)] diff --git a/crates/rustapi-rs/src/lib.rs b/crates/rustapi-rs/src/lib.rs index d117d4d..dc51a6a 100644 --- a/crates/rustapi-rs/src/lib.rs +++ b/crates/rustapi-rs/src/lib.rs @@ -54,7 +54,7 @@ //! //! ```toml //! [dependencies] -//! rustapi-rs = { version = "0.1", features = ["jwt", "cors"] } +//! rustapi-rs = { version = "0.1.275", features = ["jwt", "cors"] } //! ``` // Re-export core functionality diff --git a/crates/rustapi-toon/src/lib.rs b/crates/rustapi-toon/src/lib.rs index 78b4aa0..c8a1b41 100644 --- a/crates/rustapi-toon/src/lib.rs +++ b/crates/rustapi-toon/src/lib.rs @@ -30,7 +30,7 @@ //! //! ```toml //! [dependencies] -//! rustapi-rs = { version = "0.1", features = ["toon"] } +//! rustapi-rs = { version = "0.1.275", features = ["toon"] } //! ``` //! //! ### Toon Extractor diff --git a/docs/cookbook/src/SUMMARY.md b/docs/cookbook/src/SUMMARY.md index bf3b5d2..5ae3673 100644 --- a/docs/cookbook/src/SUMMARY.md +++ b/docs/cookbook/src/SUMMARY.md @@ -35,6 +35,7 @@ - [Custom Middleware](recipes/custom_middleware.md) - [Real-time Chat](recipes/websockets.md) - [Production Tuning](recipes/high_performance.md) + - [Resilience Patterns](recipes/resilience.md) - [Deployment](recipes/deployment.md) - [HTTP/3 (QUIC)](recipes/http3_quic.md) - [Automatic Status Page](recipes/status_page.md) diff --git a/docs/cookbook/src/learning/README.md b/docs/cookbook/src/learning/README.md index 880d721..1d0280a 100644 --- a/docs/cookbook/src/learning/README.md +++ b/docs/cookbook/src/learning/README.md @@ -117,7 +117,7 @@ Build robust, observable, and secure systems. | Step | Feature | Description | |------|---------|-------------| | 1 | **Observability** | Set up [OpenTelemetry and Structured Logging](../crates/rustapi_extras.md#observability) | -| 2 | **Resilience** | Implement [Circuit Breakers and Retries](../crates/rustapi_extras.md#resilience) | +| 2 | **Resilience** | Implement [Circuit Breakers and Retries](../recipes/resilience.md) | | 3 | **Advanced Security** | Add [OAuth2 and Security Headers](../crates/rustapi_extras.md#advanced-security) | | 4 | **Optimization** | Configure [Caching and Deduplication](../crates/rustapi_extras.md#optimization) | | 5 | **Background Jobs** | Implement [Reliable Job Queues](../crates/rustapi_jobs.md) | @@ -125,6 +125,7 @@ Build robust, observable, and secure systems. **Related Cookbook Recipes:** - [rustapi-extras: The Toolbox](../crates/rustapi_extras.md) - [rustapi-jobs: The Workhorse](../crates/rustapi_jobs.md) +- [Resilience Patterns](../recipes/resilience.md) --- @@ -242,6 +243,7 @@ Each example includes: | [Custom Middleware](../recipes/custom_middleware.md) | `middleware-chain` | | [Real-time Chat](../recipes/websockets.md) | `websocket` | | [Production Tuning](../recipes/high_performance.md) | `microservices-advanced` | +| [Resilience Patterns](../recipes/resilience.md) | `microservices` | | [Deployment](../recipes/deployment.md) | `serverless-lambda` | --- diff --git a/docs/cookbook/src/recipes/resilience.md b/docs/cookbook/src/recipes/resilience.md new file mode 100644 index 0000000..01a248d --- /dev/null +++ b/docs/cookbook/src/recipes/resilience.md @@ -0,0 +1,123 @@ +# Resilience Patterns + +Building robust applications requires handling failures gracefully. RustAPI provides a suite of middleware to help your service survive partial outages, latency spikes, and transient errors. + +These patterns are essential for the "Enterprise Platform" learning path and microservices architectures. + +## Prerequisites + +Add `rustapi-extras` to your `Cargo.toml` with the necessary features: + +```toml +[dependencies] +rustapi-rs = { version = "0.1.275", features = ["full"] } +# OR cherry-pick features +# rustapi-extras = { version = "0.1.275", features = ["circuit-breaker", "retry", "timeout"] } +``` + +## Circuit Breaker + +The Circuit Breaker pattern prevents your application from repeatedly trying to execute an operation that's likely to fail. It gives the failing service time to recover. + +### How it works +1. **Closed**: Requests flow normally. +2. **Open**: After `failure_threshold` is reached, requests fail immediately with `503 Service Unavailable`. +3. **Half-Open**: After `timeout` passes, a limited number of test requests are allowed. If they succeed, the circuit closes. + +### Usage + +```rust +use rustapi_rs::prelude::*; +use rustapi_extras::circuit_breaker::CircuitBreakerLayer; +use std::time::Duration; + +fn main() { + let app = RustApi::new() + .layer( + CircuitBreakerLayer::new() + .failure_threshold(5) // Open after 5 failures + .timeout(Duration::from_secs(30)) // Wait 30s before retrying + .success_threshold(2) // Require 2 successes to close + ) + .route("/", get(handler)); + + // ... run app +} +``` + +## Retry with Backoff + +Transient failures (network blips, temporary timeouts) can often be resolved by simply retrying the request. The `RetryLayer` handles this automatically with configurable backoff strategies. + +### Strategies +- **Exponential**: `base * 2^attempt` (Recommended for most cases) +- **Linear**: `base * attempt` +- **Fixed**: Constant delay + +### Usage + +```rust +use rustapi_rs::prelude::*; +use rustapi_extras::retry::{RetryLayer, RetryStrategy}; +use std::time::Duration; + +fn main() { + let app = RustApi::new() + .layer( + RetryLayer::new() + .max_attempts(3) + .initial_backoff(Duration::from_millis(100)) + .max_backoff(Duration::from_secs(5)) + .strategy(RetryStrategy::Exponential) + .retryable_statuses(vec![500, 502, 503, 504, 429]) + ) + .route("/", get(handler)); + + // ... run app +} +``` + +> **Warning**: Be careful when combining Retries with non-idempotent operations (like `POST` requests that charge a credit card). The middleware safely handles cloning requests, but your business logic must support it. + +## Timeouts + +Never let a request hang indefinitely. The `TimeoutLayer` enforces a hard limit on request duration, returning `408 Request Timeout` if exceeded. + +### Usage + +```rust +use rustapi_rs::prelude::*; +use rustapi_extras::timeout::TimeoutLayer; +use std::time::Duration; + +fn main() { + let app = RustApi::new() + // Fail if handler takes longer than 5 seconds + .layer(TimeoutLayer::from_secs(5)) + .route("/", get(slow_handler)); + + // ... run app +} +``` + +## Combining Layers (The Resilience Stack) + +Order matters! You typically want the Timeout to be the "outermost" constraint (or innermost, depending on perspective), followed by Circuit Breaker, then Retry. + +In RustAPI (Tower) middleware, layers wrap around each other. The order you call `.layer()` wraps the *previous* service. + +**Recommended Order:** +1. **Retry** (Inner): Retries specific failures from the handler. +2. **Circuit Breaker** (Middle): Stops retrying if the system is overloaded. +3. **Timeout** (Outer): Enforces global time limit including all retries. + +```rust +let app = RustApi::new() + // 3. Timeout (applies to the whole operation) + .layer(TimeoutLayer::from_secs(10)) + // 2. Circuit Breaker (protects upstream) + .layer(CircuitBreakerLayer::new()) + // 1. Retry (handles transient errors) + .layer(RetryLayer::new()) + .route("/", get(handler)); +``` From 430c865fb05bf1eca8cfac228f5b20eafa540786 Mon Sep 17 00:00:00 2001 From: Tunay <121901995+Tuntii@users.noreply.github.com> Date: Thu, 5 Feb 2026 19:32:59 +0300 Subject: [PATCH 2/4] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/cookbook/src/recipes/resilience.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/cookbook/src/recipes/resilience.md b/docs/cookbook/src/recipes/resilience.md index 01a248d..aa9fe27 100644 --- a/docs/cookbook/src/recipes/resilience.md +++ b/docs/cookbook/src/recipes/resilience.md @@ -113,11 +113,11 @@ In RustAPI (Tower) middleware, layers wrap around each other. The order you call ```rust let app = RustApi::new() - // 3. Timeout (applies to the whole operation) - .layer(TimeoutLayer::from_secs(10)) - // 2. Circuit Breaker (protects upstream) - .layer(CircuitBreakerLayer::new()) // 1. Retry (handles transient errors) .layer(RetryLayer::new()) + // 2. Circuit Breaker (protects upstream) + .layer(CircuitBreakerLayer::new()) + // 3. Timeout (applies to the whole operation) + .layer(TimeoutLayer::from_secs(10)) .route("/", get(handler)); ``` From 0b56c017af8e6cb21e6b58faf2f129c8b3850512 Mon Sep 17 00:00:00 2001 From: Tunay <121901995+Tuntii@users.noreply.github.com> Date: Thu, 5 Feb 2026 19:33:06 +0300 Subject: [PATCH 3/4] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/cookbook/src/recipes/resilience.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cookbook/src/recipes/resilience.md b/docs/cookbook/src/recipes/resilience.md index aa9fe27..0767873 100644 --- a/docs/cookbook/src/recipes/resilience.md +++ b/docs/cookbook/src/recipes/resilience.md @@ -6,7 +6,7 @@ These patterns are essential for the "Enterprise Platform" learning path and mic ## Prerequisites -Add `rustapi-extras` to your `Cargo.toml` with the necessary features: +Add the resilience features to your `Cargo.toml`. For example: ```toml [dependencies] From 7f18d9da00fae367beabc804aa749b840fcbcb0b Mon Sep 17 00:00:00 2001 From: Tunay <121901995+Tuntii@users.noreply.github.com> Date: Thu, 5 Feb 2026 19:33:30 +0300 Subject: [PATCH 4/4] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/cookbook/src/recipes/resilience.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cookbook/src/recipes/resilience.md b/docs/cookbook/src/recipes/resilience.md index 0767873..5736185 100644 --- a/docs/cookbook/src/recipes/resilience.md +++ b/docs/cookbook/src/recipes/resilience.md @@ -102,7 +102,7 @@ fn main() { ## Combining Layers (The Resilience Stack) -Order matters! You typically want the Timeout to be the "outermost" constraint (or innermost, depending on perspective), followed by Circuit Breaker, then Retry. +Order matters! Timeout should be the "outermost" constraint, followed by Circuit Breaker, then Retry. In RustAPI (Tower) middleware, layers wrap around each other. The order you call `.layer()` wraps the *previous* service.