A circuit breaker’s "closed" state doesn’t mean it’s just a closed electrical loop; it means the system is actively monitoring for faults while allowing normal current flow.

Let’s watch a common circuit breaker pattern in action, using Netflix’s Hystrix (though the concepts apply broadly to other implementations like Resilience4j or Go’s go-circuitbreaker):

Imagine a service UserService that depends on UserAuthService to validate credentials. If UserAuthService becomes slow or unresponsive, UserService could end up with a flood of requests waiting for a response that never comes, eventually exhausting its own resources and failing.

Here’s how a circuit breaker, when closed, prevents this cascading failure:

// Assume this is within UserService's request handling logic

// Configure the circuit breaker to trip after 5 consecutive failures
// and allow a test request after 10 seconds of being open.
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
    .failureThreshold(5) // Trip after 5 failures
    .waitDurationInOpenState(Duration.ofSeconds(10)) // Stay open for 10 seconds
    .build();

CircuitBreakerRegistry registry = CircuitBreakerRegistry.of(config);
CircuitBreaker authServiceBreaker = registry.circuitBreaker("userAuthService");

// --- Inside a request handling method ---
Supplier<User> userSupplier = () -> callUserAuthService(userId); // Your actual call

try {
    User user = authServiceBreaker.executeSupplier(userSupplier);
    // Process user data...
    System.out.println("Successfully authenticated user: " + userId);
} catch (CircuitBreakerOpenException e) {
    System.err.println("Circuit breaker is open for userAuthService. Falling back.");
    // Handle fallback logic (e.g., return cached user, throw specific error)
} catch (Exception e) {
    // This catch block handles exceptions *from* callUserAuthService
    // that are *not* CircuitBreakerOpenException. The circuit breaker
    // will count these as failures.
    System.err.println("Error calling userAuthService: " + e.getMessage());
    // Potentially re-throw or handle as a service-level error
}

In this scenario, authServiceBreaker starts in a CLOSED state. When callUserAuthService(userId) is invoked via authServiceBreaker.executeSupplier(), the circuit breaker allows the call to proceed to UserAuthService.

If callUserAuthService throws an exception (e.g., TimeoutException, ConnectException, or a custom AuthenticationFailedException), the circuit breaker records this as a failure. It maintains a sliding window of recent calls and their outcomes. If the number of failures within this window exceeds the configured failureThreshold (5 in our example), the circuit breaker transitions to the OPEN state.

Once OPEN, any subsequent calls to authServiceBreaker.executeSupplier() will immediately throw a CircuitBreakerOpenException without ever attempting to call the underlying UserAuthService. This is the core protection mechanism: it prevents the client service (UserService) from wasting resources on a failing dependency. The OPEN state is temporary, lasting for the waitDurationInOpenState (10 seconds).

After the wait duration, the circuit breaker enters a HALF-OPEN state. In this state, it allows one test request to pass through. If this single request succeeds, the breaker transitions back to CLOSED, assuming the dependency has recovered. If the test request fails, it immediately returns to OPEN for another waitDurationInOpenState.

The "closed" state is therefore the active monitoring phase. It’s not passive; it’s constantly counting failures. The goal is to keep it CLOSED for as long as possible by ensuring downstream services are healthy. When it does trip OPEN, it’s a signal that a dependency is unhealthy, and the system is actively protecting itself.

What most people miss is that the circuit breaker doesn’t just count raw exceptions. It often has configurable what counts as a failure. For instance, you might want to ignore 404 Not Found responses from a REST API as failures because that’s expected behavior, but treat 500 Internal Server Error or 503 Service Unavailable as critical failures that should contribute to tripping the breaker. Many implementations allow you to define custom predicates for what constitutes a failure.

The next logical step is understanding how to implement robust fallback mechanisms when the circuit breaker is open.

Want structured learning?

Take the full Circuit-breaker course →