Hedging middleware for Tower services.
Hedging reduces tail latency by executing parallel redundant requests. Instead of waiting for a slow request to complete, hedging fires additional requests after a configurable delay and returns whichever completes first.
Overview
The hedging pattern is useful when:
- Tail latency (P99/P999) is critical
- Operations are idempotent and safe to retry
- You can trade increased resource usage for lower latency
Presets
use HedgeLayer;
let conservative = conservative; // 500ms delay, 2 attempts
let standard = standard; // 100ms delay, 3 attempts
let aggressive = aggressive; // 50ms delay, 5 attempts
Modes
Latency Mode (delay > 0)
Wait a specified duration before firing hedge requests. This is the default and most common mode - it only sends extra requests if the primary is slow.
use HedgeLayer;
use Duration;
// No type parameters needed! Fire a hedge request if primary takes > 100ms
let layer = builder
.delay
.max_hedged_attempts
.build;
Parallel Mode (delay = 0)
Fire all requests simultaneously and return the fastest response. Use when latency is critical and you can afford the resource cost.
use HedgeLayer;
// No type parameters needed! Fire 3 requests immediately, return fastest
let layer = builder
.no_delay
.max_hedged_attempts
.build;
Example
use ;
use HedgeLayer;
use Duration;
// Define a simple cloneable error type
;
# async
Cancellation
When one request succeeds, all other in-flight requests are cancelled by dropping their futures. This relies on the inner service supporting cooperative cancellation.
Type Requirements
Hedging has specific trait bounds that differ from other resilience patterns:
-
Req: Clone- Required because the request is cloned to send parallel requests. Each hedge attempt needs its own copy of the request. -
E: Clone- Required for error handling. When multiple attempts fail, errors need to be collected and stored to return the final error.
If your request or error types don't implement Clone, consider:
- Wrapping them in
Arc(e.g.,Arc<MyRequest>) - Using a different resilience pattern like Retry which doesn't require cloning requests