Circuit Breaker Visualizer

Runs in browser

Visualize the Circuit Breaker pattern

Circuit State

CLOSED

Circuit is healthy. Requests flow through normally.

Failures: 0 / 5

Scenarios

Parameters

How to Use

Send requests to observe state transitions.

Watch for:

  • Closed → Open after failures
  • Open → Half-Open after timeout
  • Half-Open → Closed or Open

State Machine

CLOSED
Requests allowed
OPEN
Requests blocked
HALF-OPEN
Testing service

Request Log

0 total | 0 ok | 0 fail | 0 rejected

No requests yet. Click "Send Request" to start.

Circuit State
CLOSED
Success Rate
0%
Rejected
0
Failure Threshold
0/5

The Definitive Guide to the Circuit Breaker Pattern

The Circuit Breaker pattern is one of the most fundamental stability patterns in modern distributed systems and microservice architectures. Without it, a single slow database query in an upstream dependency can cause a catastrophic, system-wide cascading failure that takes down your entire company's infrastructure in minutes.

Named after the electrical switch designed to protect an electrical circuit from damage caused by overcurrent/overload, an architectural circuit breaker does the exact same thing for software. It "trips" (opens) when a downstream service is failing, immediately halting all outbound requests to that service. This achieves two critical goals: 1) It prevents your own service from exhausting its thread pools waiting on timeouts, and 2) It halts the barrage of traffic hitting the downstream component, giving it breathing room to recover.


1. The State Machine: Closed, Open, Half-Open

The circuit breaker conceptually acts as a proxy state machine wrapped around a network call. It transitions between three distinct states based on mathematical thresholds and time delays.

Closed (Normal Operation)

When the circuit is CLOSED, electricity (traffic) flows normally. The proxy allows all requests to pass through to the downstream service. The circuit breaker actively monitors the responses, keeping a running tally of successes, failures, and timeouts. If the failure rate (e.g., >50% of requests fail within a 10-second sliding window) exceeds a configured threshold, the breaker "trips" and transitions to the OPEN state.

Open (Tripped)

When the circuit is OPEN, the wire is physically cut. All requests from your application fail instantly (Fast Failure) and do not utilize network resources. The downstream service is spared the load. The proxy returns a CircuitOpenException or serves a cached/fallback response. The circuit remains in this state for a pre-configured "Wait Duration" (e.g., 60 seconds) to allow the downstream system to recover. Upon expiration, it transitions to HALF-OPEN.

Half-Open (Testing)

When the circuit is HALF-OPEN, the breaker cautiously tests the waters. It allows a strictly limited number of probe requests (e.g., 5 requests) to pass through to the downstream service. If these requests succeed, the breaker assumes the downstream has recovered and snaps back to CLOSED. If any probe requests fail, it assumes the downstream is still broken and snaps back to OPEN, resetting the recovery timer.


2. The Anatomy of a Cascading Failure

Why are circuit breakers absolutely mandatory at scale? Consider a simple architecture: API Gateway -> Order Service -> Payment Service.

  1. The Payment Service database gets slow due to a missing index. Queries take 10 seconds instead of 10ms.
  2. The Order Service sends thousands of requests to Payment. Because they take 10 seconds, Order Service thread pools start filling up with threads waiting for responses.
  3. Within 30 seconds, the Order Service has exhausted all 200 of its Tomcat/Node worker threads. It can no longer accept *any* incoming requests, even those that don't need Payment.
  4. The API Gateway sees Order Service is timing out. It retries. This makes it worse. Then the API Gateway exhausts its own thread pools waiting for Order Service.
  5. Result: A small slowdown in a non-critical database has taken down your entire site.

The Fix: Fast Failure. If the Order Service had a Circuit Breaker around the Payment client, it would have sensed the 10-second latencies and tripped to OPEN. Further calls to the Payment service would fail in 1 millisecond. The Order Service's thread pool remains free, the rest of the app stays alive, and the Payment service isn't bombarded with retries while it struggles.

💡 Retries vs. Circuit Breakers

Retries and Circuit Breakers are complementary. Retries handle transient errors (a single dropped packet). Circuit Breakers handle systemic failures (a dead database). Without a circuit breaker, aggressive retries create a "Retry Storm" (a self-inflicted DDoS attack) that guarantees a recovering service is immediately knocked back offline.


3. Calculating the Failure Rate: Sliding Windows

How does the proxy know when to trip the circuit? It uses a continuous sliding window to evaluate the health of the downstream service over a rolling period. Modern implementations use two types of windows:

Count-based Sliding Window

The breaker evaluates the last N requests (e.g., the last 100 requests). It maintains a circular array of size 100. If 50% of the items in the array represent failures or timeouts, the breaker trips. This is deterministic and easy to configure.

Time-based Sliding Window

The breaker aggregates results within the last T seconds (e.g., the last 10 seconds). It uses a sequence of buckets (one per second) to aggregate success/failure counts. As time passes, the oldest bucket falls out of the window and a new one is added.

Minimum Volume: Most implementations require a minimum number of requests before evaluating the failure rate. For example, if min-requests=20, and the first 5 requests fail (100% failure rate), the breaker WILL NOT trip because there isn't enough statistical data to prove systemic failure.


4. Fallbacks & Graceful Degradation

Failing fast is great for the servers, but bad for the user. A robust circuit breaker implementation pairs the Open state with a Fallback mechanism. When the circuit trips, the fallback function is executed.

  • Silent Failure (Null): Return empty arrays or nulls instead of throwing an error. e.g., If the "Recommendations" microservice fails, the homepage simply renders without the "Recommended For You" carousel, rather than returning an HTTP 500 blank page.
  • Stale Cache: Return the last known good response from an in-memory or Redis cache, even if the TTL is expired.
  • Custom Defaults: Return hardcoded default values.
  • Primary/Secondary: If the primary service is down, fallback to making a network call to a backup service or a different datacenter.

5. Real-World Implementations: Code vs. Mesh

Approach 1: Application-Level Libraries

Historically, circuit breakers were imported directly into your application code. Netflix revolutionized this with Hystrix (Java) in 2012, decorating functions with annotations. While Hystrix is now deprecated, its spiritual successor Resilience4j is the standard in the JVM ecosystem.

// Resilience4j Example (Java)
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
  .failureRateThreshold(50)
  .waitDurationInOpenState(Duration.ofMillis(10000))
  .slidingWindowSize(20)
  .build();

Supplier<String> decoratedSupplier = CircuitBreaker
  .decorateSupplier(circuitBreaker, backendService::doWork);

// Execute with fallback
Try.ofSupplier(decoratedSupplier).recover(throwable -> "Fallback Data");

Other language equivalents include Polly for .NET, Opossum for Node.js, and gobreaker for Go.

Approach 2: The Service Mesh (Sidecar Proxy)

In Kubernetes environments, injecting resilience code into 50 different microservices in 5 different languages is a maintenance nightmare. Enter the Service Mesh (like Istio/Envoy or Linkerd).

Instead of modifying application code, a lightweight Outlier Detection proxy (Envoy) sits as a sidecar next to your container. Your app makes a dumb HTTP call to localhost, and Envoy intercepts it. Envoy natively implements Circuit Breaking (Outlier Detection). If the target is failing, Envoy returns a 503 instantly before the packet ever leaves the pod.

# Istio DestinationRule Example
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: payment-service
spec:
  host: payment-service.default.svc.cluster.local
  trafficPolicy:
    outlierDetection:
      consecutive5xxErrors: 5
      interval: 10s
      baseEjectionTime: 30s

Comprehensive References & Standards