Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Circuit breaker

The Circuit Breaker is a System Management pattern from Hohpe and Woolf. It trips after repeated failures against a downstream service, then short-circuits further calls for a cool-down so the dependency can recover.

    let main_route = RouteBuilder::from("timer:cb-test?period=1000&repeatCount=15")
        .route_id("circuit-breaker-demo")
        .process(|mut exchange| async move {
            let n = std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .map(|d| d.as_secs())
                .unwrap_or(0);
            exchange.input.body = Body::Text(format!("request-{}", n));
            Ok(exchange)
        })
        .to("direct:failing-service")
        .circuit_breaker(
            CircuitBreakerConfig::new()
                .failure_threshold(3)
                .open_duration(Duration::from_secs(3)),
        )
        .to("log:cb-success?showBody=true&showCorrelationId=true")
        .error_handler(
            ErrorHandlerConfig::dead_letter_channel(
                "log:cb-fallback?showBody=true&showHeaders=true&showCorrelationId=true",
            )
            .on_exception(|_| true)
            .build(),
        )
        .build()?;
YAML equivalent
- id: circuit-breaker-demo
  from: timer:cb-test?period=1000&repeatCount=15
  circuit_breaker:
    failure_threshold: 3
    open_duration_ms: 3000
  error_handler:
    dead_letter_channel: log:cb-fallback?showBody=true&showHeaders=true&showCorrelationId=true
  steps:
    - to: direct:failing-service
    - to: log:cb-success?showBody=true&showCorrelationId=true

The breaker cycles through three states. In Closed, traffic flows. Each failed call increments a consecutive-failure counter. A successful call resets that counter to zero. When the counter reaches failure_threshold, the breaker trips into Open. In Open, the breaker rejects every call with CamelError::CircuitOpen. The route never touches the downstream service. The breaker holds Open for open_duration, then enters HalfOpen.

HalfOpen admits a single probe call. The breaker rejects concurrent callers in that window so the probe runs in isolation. A probe that succeeds closes the breaker and resets the counter. A probe that fails reopens the breaker for another full cool-down. This single-probe design stops a backlog of traffic from stampeding the service. The dependency gets one probe, not a flood, at the first sign of recovery.

Use the circuit breaker to protect a downstream service. Do not use it to repair a transient blip. Retry sends more traffic at a failing call. The circuit breaker sends none. Pair them when a flaky dependency needs a retry for transient errors but a hard stop during a sustained outage. Per ADR-0019, the breaker compiles into a gate on RouteChannelService rather than a pipeline step. Boundary rejections flow through RouteErrorHandler::handle_boundary. The route's error_handler can then route CircuitOpen to a dead-letter sink, as the example does with log:cb-fallback.

The example source is at examples/circuit-breaker.