Circuit Breaker Pattern Explained — Complete Beginner's Guide
In this tutorial, you will learn about Circuit Breaker Pattern Explained. We cover key concepts, practical examples, and best practices to help you master this topic.
The circuit breaker pattern prevents cascading failures in Distributed Systems by monitoring for failures and stopping requests to a failing service before the failure spreads.
What You'll Learn
By the end of this tutorial, you will understand what the circuit breaker pattern is, its three states, why it matters, and how it differs from retry strategies.
Why It Matters
Without circuit breakers, a single failing service can cascade failures across your entire system. DodaTech uses circuit breakers in every microservice to contain failures.
Real-World Use
DodaZIP's conversion API uses circuit breakers around its file processing service. When the processor fails 5 times in a row, the circuit opens and subsequent requests fail immediately, preventing timeouts from accumulating.
Circuit Breaker Learning Path
flowchart LR
A[Retry Strategies] --> B[Circuit Breaker]
B --> C[Three States]
B --> D[Implementation]
B --> E{You Are Here}
style E fill:#f90,color:#fff
What Is a Circuit Breaker?
A circuit breaker is like an electrical circuit breaker in your home. When too much current flows (too many failures), the breaker trips and stops the flow. Someone must reset it (half-open state) to test if the fault is cleared.
class SimpleCircuitBreaker {
constructor() {
this.state = "closed";
this.failureCount = 0;
this.threshold = 5;
this.resetTimeout = 30000;
this.nextAttempt = Date.now();
}
async call(fn) {
if (this.state === "open") {
if (Date.now() > this.nextAttempt) {
this.state = "half-open";
} else {
throw new Error("Circuit breaker is open");
}
}
try {
const result = await fn();
this.state = "closed";
this.failureCount = 0;
return result;
} catch (err) {
this.failureCount++;
if (this.failureCount >= this.threshold) {
this.state = "open";
this.nextAttempt = Date.now() + this.resetTimeout;
}
throw err;
}
}
}
Circuit Breaker vs Retry
Retry and circuit breaker solve different problems and work best together.
| Aspect | Retry | Circuit Breaker |
|---|---|---|
| Purpose | Handle transient failures | Prevent cascading failures |
| Behavior | Try again | Stop trying |
| Duration | Seconds | Minutes |
| Scope | Single call | All calls to a service |
Why Circuit Breakers Matter
Without circuit breakers, a slow or failing downstream service causes all upstream services to accumulate waiting requests. This exhausts thread pools and memory, eventually taking down the entire system.
// Without circuit breaker: each request waits 30s for timeout
// 100 concurrent requests = 100 threads blocked for 30s
// Thread pool exhausts -> server stops accepting requests
// Cascading failure: every service that depends on this one also fails
Common Mistakes
Setting threshold too high -- A threshold of 100 failures means 99 failures happen before protection kicks in. Start with 3-5.
Opening circuit on every error -- Only open the circuit for errors that indicate real trouble (timeouts, connection failures), not client errors.
Not logging state transitions -- Circuit breaker state changes are critical events. Log every open, close, and half-open transition.
Using a single circuit for all services -- Different downstream services need different circuit breakers with different thresholds.
Not implementing half-open probes -- Without half-open, a circuit stays open forever or flips open/closed rapidly.
Practice Questions
What are the three states of a circuit breaker? Closed (normal operation), Open (rejecting requests), Half-open (testing recovery).
How does a circuit breaker prevent cascading failures? By failing fast when a downstream service is unhealthy, preventing upstream resources from being consumed by waiting requests.
What is the difference between circuit breaker and retry? Retry recovers from transient failures. Circuit breaker stops requests to prevent cascading failures from persistent problems.
Challenge: Implement a circuit breaker that tracks failure rate over a time window.
class RateBasedCircuit {
constructor(threshold = 0.5, windowMs = 60000) {
this.threshold = threshold;
this.windowMs = windowMs;
this.results = [];
}
record(success) {
this.results.push({ success, time: Date.now() });
this.cleanup();
}
get failureRate() {
const recent = this.results.filter(r => Date.now() - r.time < this.windowMs);
if (recent.length < 10) return 0;
return recent.filter(r => !r.success).length / recent.length;
}
}
FAQ
Mini Project
Build a basic circuit breaker with configurable threshold, reset timeout, state change events, and logging.
class CircuitBreaker {
constructor(options = {}) {
this.threshold = options.threshold || 5;
this.resetTimeout = options.resetTimeout || 30000;
this.state = "closed";
this.failureCount = 0;
this.nextAttempt = Date.now();
this.onStateChange = options.onStateChange || (() => {});
}
async exec(fn) {
if (this.state === "open") {
if (Date.now() >= this.nextAttempt) {
this.transitionTo("half-open");
} else {
throw new Error("Circuit open, request rejected");
}
}
try {
const result = await fn();
if (this.state === "half-open") {
this.transitionTo("closed");
}
this.failureCount = 0;
return result;
} catch (err) {
this.failureCount++;
if (this.failureCount >= this.threshold || this.state === "half-open") {
this.transitionTo("open");
this.nextAttempt = Date.now() + this.resetTimeout;
}
throw err;
}
}
transitionTo(newState) {
const oldState = this.state;
this.state = newState;
this.onStateChange({ from: oldState, to: newState, time: new Date().toISOString() });
console.log(`Circuit breaker: ${oldState} -> ${newState}`);
}
getState() {
return {
state: this.state,
failureCount: this.failureCount,
nextAttempt: new Date(this.nextAttempt).toISOString()
};
}
}
const cb = new CircuitBreaker({
threshold: 3,
resetTimeout: 10000,
onStateChange: (event) => console.log("State change:", event)
});
async function test() {
const failingFn = () => Promise.reject(new Error("fail"));
const successFn = () => Promise.resolve("ok");
for (let i = 0; i < 5; i++) {
try { await cb.exec(failingFn); }
catch (e) { console.log(`Call ${i + 1}:`, e.message); }
}
console.log("State:", cb.getState());
}
What's Next
Now that you understand circuit breaker basics, explore the three states in detail. Then learn about implementing circuit breakers in Node.js.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro