Circuit Breaker
A circuit breaker is a fault tolerance pattern borrowed from electrical engineering. When a downstream service fails repeatedly, the circuit breaker “opens” and blocks requests for a cooldown period. This prevents your application from wasting resources on doomed calls and shields the struggling service from further overload.
How It Works
A circuit breaker has three states:
- Closed (normal): Requests pass through. Failures are counted.
- Open (tripped): Requests are immediately rejected without calling the service. A timer starts.
- Half-open (testing): After the cooldown, one request is allowed through. If it succeeds, the circuit closes. If it fails, the circuit reopens.
Closed ──(failure threshold reached)──→ Open
↑ │
│ (cooldown expires)
│ ↓
└───────(test request succeeds)─── Half-Open
Example
class CircuitBreaker {
constructor({ threshold = 5, cooldown = 30000 }) {
this.failures = 0;
this.threshold = threshold;
this.cooldown = cooldown;
this.state = 'closed';
this.nextAttempt = 0;
}
async call(fn) {
if (this.state === 'open') {
if (Date.now() < this.nextAttempt) {
throw new Error('Circuit breaker is open');
}
this.state = 'half-open';
}
try {
const result = await fn();
this.failures = 0;
this.state = 'closed';
return result;
} catch (error) {
this.failures++;
if (this.failures >= this.threshold) {
this.state = 'open';
this.nextAttempt = Date.now() + this.cooldown;
}
throw error;
}
}
}
const breaker = new CircuitBreaker({ threshold: 3, cooldown: 10000 });
const data = await breaker.call(() => fetch('https://api.example.com/data'));
When to Use Circuit Breakers
- Calling external APIs that may become unavailable
- Connecting to databases or caches that can go down
- Any dependency where repeated failures would cascade to callers
Circuit Breakers vs. Retries
Retries re-attempt a single failed request. Circuit breakers block all requests to a service after a pattern of failure. They work best together: retry individual failures, and open the circuit when retries fail consistently.