Skip to content

Circuit Breaker

Definition

Circuit Breaker State Machine


States Explained

Circuit Breaker States


Implementation

// USING RESILIENCE4J

@Configuration
public class CircuitBreakerConfig {

    @Bean
    public CircuitBreakerRegistry circuitBreakerRegistry() {
        CircuitBreakerConfig config = CircuitBreakerConfig.custom()
            .failureRateThreshold(50)           // 50% failure rate trips
            .waitDurationInOpenState(Duration.ofSeconds(30))  // Wait before half-open
            .permittedNumberOfCallsInHalfOpenState(3)  // Test calls
            .slidingWindowSize(10)              // Last 10 calls
            .slidingWindowType(SlidingWindowType.COUNT_BASED)
            .build();

        return CircuitBreakerRegistry.of(config);
    }
}

@Service
public class PaymentService {

    private final CircuitBreaker circuitBreaker;
    private final PaymentGateway gateway;

    public PaymentService(CircuitBreakerRegistry registry,
                          PaymentGateway gateway) {
        this.circuitBreaker = registry.circuitBreaker("payment");
        this.gateway = gateway;
    }

    public PaymentResult processPayment(Payment payment) {
        return circuitBreaker.executeSupplier(() ->
            gateway.charge(payment)
        );
    }

    // With fallback
    public PaymentResult processPaymentWithFallback(Payment payment) {
        return Try.ofSupplier(
            CircuitBreaker.decorateSupplier(circuitBreaker,
                () -> gateway.charge(payment)))
            .recover(CallNotPermittedException.class,
                e -> PaymentResult.pending("Service temporarily unavailable"))
            .recover(Exception.class,
                e -> PaymentResult.failed(e.getMessage()))
            .get();
    }
}

// ANNOTATION-BASED (Spring)
@Service
public class PaymentService {

    @CircuitBreaker(name = "payment", fallbackMethod = "paymentFallback")
    public PaymentResult processPayment(Payment payment) {
        return gateway.charge(payment);
    }

    public PaymentResult paymentFallback(Payment payment, Exception e) {
        log.warn("Payment circuit breaker fallback", e);
        return PaymentResult.pending("Please try again later");
    }
}

Configuration

# application.yml (Resilience4j)
resilience4j:
  circuitbreaker:
    instances:
      payment:
        # When to open
        failureRateThreshold: 50
        slowCallRateThreshold: 100
        slowCallDurationThreshold: 2s

        # How long to stay open
        waitDurationInOpenState: 30s

        # Half-open behavior
        permittedNumberOfCallsInHalfOpenState: 3

        # Sliding window
        slidingWindowType: COUNT_BASED
        slidingWindowSize: 10
        minimumNumberOfCalls: 5

        # What counts as failure
        recordExceptions:
          - java.io.IOException
          - java.util.concurrent.TimeoutException
        ignoreExceptions:
          - com.example.BusinessException

  # Often combined with other patterns
  retry:
    instances:
      payment:
        maxAttempts: 3
        waitDuration: 500ms
        retryExceptions:
          - java.io.IOException

  bulkhead:
    instances:
      payment:
        maxConcurrentCalls: 10
        maxWaitDuration: 100ms

  timelimiter:
    instances:
      payment:
        timeoutDuration: 3s

Fallback Strategies

// FALLBACK STRATEGIES

// 1. Return cached data
@CircuitBreaker(name = "products", fallbackMethod = "getCachedProducts")
public List<Product> getProducts() {
    return productService.fetchAll();
}

public List<Product> getCachedProducts(Exception e) {
    return cache.get("products", List.class);
}

// 2. Return default/degraded response
@CircuitBreaker(name = "recommendations", fallbackMethod = "getDefaultRecs")
public List<Product> getRecommendations(String userId) {
    return recommendationService.getPersonalized(userId);
}

public List<Product> getDefaultRecs(String userId, Exception e) {
    return productService.getTopSelling(10);  // Generic recommendations
}

// 3. Queue for later processing
@CircuitBreaker(name = "notifications", fallbackMethod = "queueNotification")
public void sendNotification(Notification notification) {
    notificationService.send(notification);
}

public void queueNotification(Notification notification, Exception e) {
    messageQueue.enqueue("pending-notifications", notification);
    log.warn("Notification queued for later delivery");
}

// 4. Return error with guidance
@CircuitBreaker(name = "checkout", fallbackMethod = "checkoutFallback")
public Order checkout(Cart cart) {
    return orderService.createOrder(cart);
}

public Order checkoutFallback(Cart cart, Exception e) {
    throw new ServiceUnavailableException(
        "Checkout is temporarily unavailable. " +
        "Your cart has been saved. Please try again in a few minutes.");
}

Tips & Tricks

Tips and Tricks