javaadvanced

Circuit Breaker Pattern

Implement a circuit breaker with states (closed, open, half-open), failure counting, and auto-recovery.

java
import java.time.Instant;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;

public class CircuitBreaker {
    enum State { CLOSED, OPEN, HALF_OPEN }

    private final int failureThreshold;
    private final long resetTimeoutMs;
    private final AtomicInteger failureCount = new AtomicInteger(0);
    private final AtomicReference<State> state = new AtomicReference<>(State.CLOSED);
    private volatile Instant lastFailureTime = Instant.MIN;

    public CircuitBreaker(int failureThreshold, long resetTimeoutMs) {
        this.failureThreshold = failureThreshold;
        this.resetTimeoutMs = resetTimeoutMs;
    }

    public <T> T execute(Supplier<T> action, Supplier<T> fallback) {
        if (!allowRequest()) {
            System.out.println("Circuit OPEN — using fallback");
            return fallback.get();
        }

        try {
            T result = action.get();
            onSuccess();
            return result;
        } catch (Exception e) {
            onFailure();
            return fallback.get();
        }
    }

    private boolean allowRequest() {
        return switch (state.get()) {
            case CLOSED -> true;
            case OPEN -> {
                if (Instant.now().toEpochMilli() - lastFailureTime.toEpochMilli() > resetTimeoutMs) {
                    state.set(State.HALF_OPEN);
                    System.out.println("Circuit HALF_OPEN — testing");
                    yield true;
                }
                yield false;
            }
            case HALF_OPEN -> true;
        };
    }

    private void onSuccess() {
        failureCount.set(0);
        if (state.get() == State.HALF_OPEN) {
            state.set(State.CLOSED);
            System.out.println("Circuit CLOSED — recovered");
        }
    }

    private void onFailure() {
        lastFailureTime = Instant.now();
        int failures = failureCount.incrementAndGet();
        if (failures >= failureThreshold) {
            state.set(State.OPEN);
            System.out.println("Circuit OPEN — threshold reached (" + failures + ")");
        }
    }

    public State getState() { return state.get(); }

    public static void main(String[] args) throws Exception {
        CircuitBreaker breaker = new CircuitBreaker(3, 2000);
        var rng = new java.util.Random();

        for (int i = 0; i < 10; i++) {
            String result = breaker.execute(
                () -> {
                    if (rng.nextBoolean()) throw new RuntimeException("Fail");
                    return "OK";
                },
                () -> "FALLBACK"
            );
            System.out.printf("Request %d: %s (state: %s)%n", i, result, breaker.getState());
            Thread.sleep(500);
        }
    }
}

Use Cases

  • Protecting services from cascading failures
  • Graceful degradation with fallback responses
  • Self-healing connection management

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.