Skip to content
intermediatePhase 48 · Distributed Systems

Circuit Breaker

Prevent cascading failures with circuit breaker pattern.

45m
0 problems
Topic Progress0%

Three States

Circuit Breaker Three States

State Diagram

Closed → Open → Half-Open → Closed
  ↑                    ↓
  └────────────────────┘

Closed:
- Normal operation
- Requests pass through
- Count failures

Open:
- Failure threshold exceeded
- Requests fail immediately
- No calls to service

Half-Open:
- Cooldown period elapsed
- Allow one test request
- Check if service recovered

State Transitions

1. Closed → Open:
   Trigger: Failure count >= threshold
   Action: Start cooldown timer

2. Open → Half-Open:
   Trigger: Cooldown period elapsed
   Action: Allow one test request

3. Half-Open → Closed:
   Trigger: Test request succeeds
   Action: Reset failure count

4. Half-Open → Open:
   Trigger: Test request fails
   Action: Reset cooldown timer

Configuration

class CircuitBreaker:
    def __init__(self, failure_threshold=5, recovery_timeout=30):
        self.failure_threshold = failure_threshold
        self.recovery_timeout = recovery_timeout
        self.failure_count = 0
        self.last_failure_time = None
        self.state = 'closed'

State Impact

State Request Behavior Failure Detection
Closed Pass through Count failures
Open Fail fast No detection
Half-Open Allow one Test recovery

Implementation

Circuit Breaker Implementation

Complete Implementation

import time
import threading

class CircuitBreaker:
    def __init__(self, failure_threshold=5, recovery_timeout=30, half_open_max=1):
        self.failure_threshold = failure_threshold
        self.recovery_timeout = recovery_timeout
        self.half_open_max = half_open_max
        
        self.failure_count = 0
        self.success_count = 0
        self.last_failure_time = None
        self.state = 'closed'
        self.lock = threading.Lock()
    
    @property
    def is_open(self):
        with self.lock:
            if self.state == 'open':
                if time.time() - self.last_failure_time > self.recovery_timeout:
                    self.state = 'half-open'
                    self.success_count = 0
                    return False
                return True
            return False
    
    def record_success(self):
        with self.lock:
            self.failure_count = 0
            if self.state == 'half-open':
                self.success_count += 1
                if self.success_count >= self.half_open_max:
                    self.state = 'closed'
    
    def record_failure(self):
        with self.lock:
            self.failure_count += 1
            self.last_failure_time = time.time()
            
            if self.state == 'half-open':
                self.state = 'open'
            elif self.failure_count >= self.failure_threshold:
                self.state = 'open'
    
    def call(self, func, fallback=None):
        if self.is_open:
            if fallback:
                return fallback()
            raise CircuitOpenError('Circuit is open')
        
        try:
            result = func()
            self.record_success()
            return result
        except Exception as e:
            self.record_failure()
            if fallback:
                return fallback()
            raise

# Usage
circuit = CircuitBreaker(failure_threshold=5, recovery_timeout=30)

try:
    result = circuit.call(lambda: call_external_service())
except CircuitOpenError:
    result = fallback_response()

With Fallback

class ResilientService:
    def __init__(self):
        self.circuit = CircuitBreaker()
    
    def get_user(self, user_id):
        try:
            return self.circuit.call(
                lambda: self.primary_db.get_user(user_id)
            )
        except CircuitOpenError:
            # Fallback to cache or secondary
            return self.cache.get_user(user_id)

Monitoring

def monitor_circuit_breaker(circuit, service_name):
    metrics = {
        'state': circuit.state,
        'failure_count': circuit.failure_count,
        'is_open': circuit.is_open
    }
    
    metrics.gauge(f'{service_name}.circuit.state', 
                  ['closed', 'open', 'half-open'].index(circuit.state))
    metrics.counter(f'{service_name}.circuit.failures', circuit.failure_count)

Best Practices

  1. Set appropriate thresholds based on service
  2. Use fallbacks when circuit is open
  3. Monitor circuit state
  4. Log state transitions
  5. Test failure scenarios

Integration with Retries

Circuit Breaker + Retry Integration

Combined Pattern

Request → Circuit Breaker Check
           ↓
         [Open] → Fail fast (no retry)
         [Closed] → Retry Logic
                      ↓
                    Attempt → Success → Return
                    Attempt → Fail → Backoff → Retry
                    All Fail → Record Failure → Circuit Open

Implementation

class ResilientCaller:
    def __init__(self, circuit_breaker, retry_policy):
        self.circuit = circuit_breaker
        self.retry = retry_policy
    
    def call(self, func, *args, **kwargs):
        # Check circuit first
        if self.circuit.is_open:
            raise CircuitOpenError('Circuit is open')
        
        # Retry with backoff
        last_exception = None
        for attempt in range(self.retry.max_retries):
            try:
                result = func(*args, **kwargs)
                self.circuit.record_success()
                return result
            except Exception as e:
                last_exception = e
                self.circuit.record_failure()
                
                if attempt < self.retry.max_retries - 1:
                    delay = self.retry.get_delay(attempt)
                    time.sleep(delay)
        
        raise last_exception

Flow with Integration

1. Request arrives
2. Circuit open? → Yes → Fail fast
3. Circuit closed? → Continue
4. Attempt call
5. Success → Record success, return
6. Failure → Record failure
7. More retries? → Wait (backoff) → Retry
8. No more retries → Raise exception
9. Circuit opens if threshold exceeded

Configuration Example

# Configuration
circuit_config = {
    'failure_threshold': 5,
    'recovery_timeout': 30
}

retry_config = {
    'max_retries': 3,
    'base_delay': 1,
    'max_delay': 30,
    'jitter': True
}

# Create components
circuit = CircuitBreaker(**circuit_config)
retry = RetryPolicy(**retry_config)
caller = ResilientCaller(circuit, retry)

# Use
result = caller.call(service_api, param1, param2)

Monitoring

def monitor_resilience(caller, service_name):
    metrics = {
        'circuit_state': caller.circuit.state,
        'failure_count': caller.circuit.failure_count,
        'retry_rate': get_retry_rate(service_name)
    }
    
    # Alert on open circuit
    if caller.circuit.is_open:
        alert(f'{service_name}: Circuit is open')

Best Practices

  1. Use circuit breaker first (fast fail)
  2. Retry only when circuit closed
  3. Use fallbacks when circuit open
  4. Monitor both metrics
  5. Test failure scenarios

Practice Problems

0/3solved
Design Circuit Breaker System

Design a scalable Circuit Breaker system. Cover high-level architecture, data model, and API design.

Solution
// Complete system design:
// - Functional + Non-functional requirements
// - Capacity estimation
// - Data model (SQL/NoSQL choice)
// - API endpoints
// - Component architecture
// - Scaling strategy
// - Monitoring & reliability
Circuit Breaker Scaling

How would you scale Circuit Breaker to handle 10x the current load? Identify bottlenecks and solutions.

Solution
// Scaling approach:
// 1. Load balancing
// 2. Database sharding/replication
// 3. Cache layer (Redis)
// 4. CDN for static assets
// 5. Async processing (queues)
// 6. Microservices decomposition
Circuit Breaker Failure Modes

Analyze potential failure modes for Circuit Breaker and design mitigation strategies.

Solution
// Failure mitigation:
// 1. Redundancy (multi-AZ)
// 2. Circuit breakers
// 3. Retry with backoff
// 4. Dead letter queues
// 5. Health checks
// 6. Graceful degradation

Quiz

1. What are the three states of a circuit breaker?

Question 1 options

2. When does circuit breaker transition from Closed to Open?

Question 2 options

3. What is the purpose of Half-Open state?

Question 3 options

4. What happens when circuit is open?

Question 4 options

5. Why combine circuit breaker with retry?

Question 5 options

Flashcards

Question

Three circuit breaker states?

Answer

Closed (normal), Open (fail fast), Half-Open (test recovery)

Question

Closed → Open transition?

Answer

When failure count reaches threshold - circuit opens to prevent more calls to failing service

Question

What is Half-Open state?

Answer

Allows one test request to check if failed service has recovered before fully closing circuit

Question

Circuit breaker + retry integration?

Answer

Check circuit first (fail fast if open), then retry with backoff if circuit is closed

Question

Why use fallback with circuit breaker?

Answer

Provide alternative response when circuit is open and primary service is unavailable

Revision Notes

Key Takeaways

  • 1.Three states: Closed, Open, Half-Open
  • 2.Closed → Open on failure threshold
  • 3.Half-Open tests recovery with one request
  • 4.Combine with retry for resilience
  • 5.Always provide fallback when circuit is open

Interview Tips

  • Draw state diagram with transitions
  • Explain each state's purpose
  • Discuss integration with retry logic
  • Mention monitoring and fallbacks

Cheat Sheet

Cheat Sheet: Circuit Breaker

Three States

  1. Closed: Normal, count failures
  2. Open: Fail fast, no calls
  3. Half-Open: Test recovery

Transitions

  • Closed → Open: threshold reached
  • Open → Half-Open: cooldown elapsed
  • Half-Open → Closed: test success
  • Half-Open → Open: test failure

Integration

Circuit Check → Retry → Fallback

Configuration

  • failure_threshold: 5
  • recovery_timeout: 30s
  • half_open_max: 1

Best Practices

  • Use fallbacks
  • Monitor state
  • Log transitions