Bulkhead pattern for Tower services.
The bulkhead pattern isolates resources to prevent cascading failures.
This implementation uses semaphore-based concurrency limiting to control
the maximum number of concurrent calls to a service.
Basic Example
use tower::ServiceBuilder;
use tower_resilience_bulkhead::BulkheadConfig;
use std::time::Duration;
# async fn example() {
let layer = BulkheadConfig::builder()
.max_concurrent_calls(10)
.name("my-bulkhead")
.build();
let service = ServiceBuilder::new()
.layer(layer)
.service_fn(|req: String| async move {
Ok::<_, ()>(req)
});
# }
Example with Timeout
Configure a maximum wait duration for requests when the bulkhead is at capacity:
use tower::ServiceBuilder;
use tower_resilience_bulkhead::{BulkheadConfig, BulkheadError};
use std::time::Duration;
# async fn example() {
let layer = BulkheadConfig::builder()
.max_concurrent_calls(5)
.max_wait_duration(Some(Duration::from_secs(2)))
.name("timeout-bulkhead")
.build();
let service = ServiceBuilder::new()
.layer(layer)
.service_fn(|req: String| async move {
Ok::<_, ()>(req)
});
# }
Example with Event Listeners
Monitor bulkhead behavior using event listeners:
use tower::ServiceBuilder;
use tower_resilience_bulkhead::BulkheadConfig;
use std::time::Duration;
# async fn example() {
let layer = BulkheadConfig::builder()
.max_concurrent_calls(10)
.name("monitored-bulkhead")
.on_call_permitted(|concurrent| {
println!("Call permitted ({} concurrent)", concurrent);
})
.on_call_rejected(|max| {
println!("Call rejected (max {} concurrent)", max);
})
.on_call_finished(|duration| {
println!("Call finished in {:?}", duration);
})
.build();
let service = ServiceBuilder::new()
.layer(layer)
.service_fn(|req: String| async move {
Ok::<_, ()>(req)
});
# }
Error Handling
The bulkhead passes through the inner service's errors directly.
Use event listeners to track bulkhead rejections:
use tower_resilience_bulkhead::BulkheadConfig;
use tower::ServiceBuilder;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
# async fn example() {
let rejections = Arc::new(AtomicUsize::new(0));
let r = rejections.clone();
let layer = BulkheadConfig::builder()
.max_concurrent_calls(5)
.on_call_rejected(move |_| {
r.fetch_add(1, Ordering::SeqCst);
})
.build();
let service = ServiceBuilder::new()
.layer(layer)
.service_fn(|req: String| async move {
Ok::<_, ()>(req)
});
println!("Rejections: {}", rejections.load(Ordering::SeqCst));
# }