Circuit Breaker Pattern in C#: Definition, Architecture, Examples, Pros, and Cons

Circuit Breaker Pattern in C#: Definition, Architecture, Examples, Pros, and Cons

The Circuit Breaker Pattern is a resilience pattern in C# that prevents repeated calls to failing services by temporarily blocking requests after a failure threshold is reached.

The Circuit Breaker Pattern protects applications from cascading failures in distributed systems and microservices architectures. When repeated failures occur while calling an external service, API, database, or microservice, the circuit breaker automatically stops sending requests for a temporary period. This prevents resource exhaustion, improves system stability, and allows the failing service time to recover. In C#, the Circuit Breaker Pattern is commonly implemented using libraries such as Polly with ASP.NET Core and HttpClientFactory. The pattern is especially useful when working with unreliable external systems, cloud services, or network-dependent applications.

Why We Use Circuit Breaker Pattern in C#?

We use the Circuit Breaker Pattern to improve fault tolerance and prevent system-wide failures.

Main reasons include:

• Prevents cascading failures
• Protects system resources
• Improves application resilience
• Reduces unnecessary network calls
• Helps failing services recover
• Improves response times during failures
• Prevents thread exhaustion
• Works well with microservices
• Enhances fault isolation
• Supports self-healing systems

Real-World Analogy

A household electrical circuit breaker stops electricity flow when overload occurs.

Software equivalent:

Too many failed requests
-> Circuit opens
-> Requests blocked temporarily
-> Service recovers
-> Circuit closes again

Circuit Breaker States

The Circuit Breaker has three main states.

1. Closed State

Normal operation:

Requests allowed
Failures monitored

If failures exceed the threshold:

Circuit transitions to OPEN

2. Open State

Requests are blocked immediately.

No requests sent to failing service

After timeout expires:

Circuit transitions to HALF-OPEN

3. Half-Open State

Limited test requests are allowed.

If success -> CLOSED
If failure -> OPEN

Circuit Breaker Workflow

Request
   |
Success?
   |
YES ----------------> CLOSED
   |
NO
   |
Failure Threshold Reached?
   |
YES ----------------> OPEN
   |
Timeout Expires
   |
HALF-OPEN
   |
Test Request Success?
   |
YES -> CLOSED
NO  -> OPEN

When Should We Use Circuit Breaker Pattern?

The Circuit Breaker Pattern should be used when applications depend on external or unreliable systems.

Typical programming problems where Circuit Breaker is beneficial:

• External API communication
• Microservices architectures
• Database connectivity issues
• Cloud service integrations
• Payment gateways
• Distributed systems
• Network instability handling
• Long-running remote operations
• Service-to-service communication

Avoid Circuit Breaker Pattern for:

• Pure in-memory applications
• Local-only processing
• Simple CRUD systems without remote dependencies
• Very small applications

Circuit Breaker Pattern Examples in C#

Example 1: Basic Circuit Breaker Implementation in C#

// Simple Circuit Breaker Class
public class CircuitBreaker
{
    private int _failureCount;

    private readonly int _failureThreshold = 3;

    private bool _isOpen;

    private DateTime _lastFailureTime;

    private readonly TimeSpan _timeout = TimeSpan.FromSeconds(10);

    public async Task ExecuteAsync(Func<Task> action)
    {
        if (_isOpen)
        {
            if (DateTime.UtcNow - _lastFailureTime > _timeout)
            {
                _isOpen = false;
            }
            else
            {
                throw new Exception("Circuit is OPEN");
            }
        }

        try
        {
            await action();

            _failureCount = 0;
        }
        catch
        {
            _failureCount++;

            _lastFailureTime = DateTime.UtcNow;

            if (_failureCount >= _failureThreshold)
            {
                _isOpen = true;
            }

            throw;
        }
    }
}

// Usage
var circuitBreaker = new CircuitBreaker();

await circuitBreaker.ExecuteAsync(async () =>
{
    Console.WriteLine("Calling external API");

    throw new Exception("API failed");
});

Example 2: Circuit Breaker with Polly

Install Package
dotnet add package Polly

// Polly Circuit Breaker Example
using Polly;
using Polly.CircuitBreaker;

var policy = Policy
    .Handle<Exception>()
    .CircuitBreakerAsync(
        exceptionsAllowedBeforeBreaking: 3,
        durationOfBreak: TimeSpan.FromSeconds(10)
    );

try
{
    await policy.ExecuteAsync(async () =>
    {
        Console.WriteLine("Calling API");

        throw new Exception("Service unavailable");
    });
}
catch (BrokenCircuitException)
{
    Console.WriteLine("Circuit breaker is OPEN");
}

Example 3: Circuit Breaker with HttpClientFactory

// Service Registration
builder.Services.AddHttpClient("MyApiClient")
    .AddTransientHttpErrorPolicy(policy =>
        policy.CircuitBreakerAsync(
            3,
            TimeSpan.FromSeconds(15)
        ));

// Usage
public class ApiService
{
    private readonly HttpClient _httpClient;

    public ApiService(IHttpClientFactory factory)
    {
        _httpClient = factory.CreateClient("MyApiClient");
    }

    public async Task<string> GetDataAsync()
    {
        return await _httpClient.GetStringAsync(
            "https://www.howcsharp.com/api/data");
    }
}

Example 4: Half-Open State Simulation

public async Task SimulateHalfOpenAsync()
{
    bool success = DateTime.UtcNow.Second % 2 == 0;

    if (!success)
    {
        throw new Exception("Temporary failure");
    }

    Console.WriteLine("Service recovered");
}

Example 5: Circuit Breaker with Retry Pattern

Circuit Breaker is often combined with Retry Pattern.

var retryPolicy = Policy
    .Handle<Exception>()
    .RetryAsync(3);

var circuitBreakerPolicy = Policy
    .Handle<Exception>()
    .CircuitBreakerAsync(2, TimeSpan.FromSeconds(30));

var combinedPolicy = Policy.WrapAsync(
    retryPolicy,
    circuitBreakerPolicy
);

await combinedPolicy.ExecuteAsync(async () =>
{
    Console.WriteLine("Executing request");

    throw new Exception("Request failed");
});

Advantages of Circuit Breaker Pattern in C#

Advantage Description
Prevents Cascading Failures Stops failures from spreading across systems.
Improves Resilience Makes applications more fault tolerant.
Resource Protection Prevents thread and connection exhaustion.
Faster Failure Detection Fails quickly instead of waiting for repeated timeouts.
Supports Recovery Allows failing services time to recover.
Works Well with Microservices Improves distributed system stability.
Easy Integration Libraries like Polly simplify implementation.

Disadvantages (Weak Points) of Circuit Breaker Pattern in C#

Disadvantage Description
Additional Complexity Requires state management and monitoring.
Temporary Request Blocking Some valid requests may be rejected during OPEN state.
Difficult Configuration Thresholds and timeout values require tuning.
False Positives Temporary issues may incorrectly open the circuit.
Monitoring Overhead Requires logging and observability tooling.
Not a Complete Solution Should be combined with retries, timeouts, and fallbacks.

Circuit Breaker vs Similar Patterns

Feature Circuit Breaker Retry Pattern Timeout Pattern Fallback Pattern Bulkhead Pattern
Main Goal Prevent repeated failures Retry transient failures Limit wait duration Provide alternative response Isolate failures
Failure Handling Blocks requests temporarily Retries failed operations Cancels long-running calls Returns backup response Limits resource sharing
Resource Protection Excellent Low Moderate Moderate Excellent
Complexity Medium Low Low Medium Medium
Best For Unstable external services Temporary network issues Slow operations Graceful degradation Distributed systems
State Management Yes No No Optional Yes
Works with Microservices Excellent Good Good Excellent Excellent

Common Real-World Use Cases

Payment Gateway Protection

Workflow:

Payment API fails repeatedly
-> Circuit opens
-> Requests blocked temporarily

External API Communication

Workflow:

Weather API unavailable
-> Prevent repeated calls
-> Use fallback response

Microservices Communication

Workflow:

Inventory Service unavailable
-> Circuit breaker activates
-> Prevent cascading failure

Popular C# Libraries for Circuit Breaker Pattern

Library Description
Polly Most popular .NET resilience library.
Microsoft.Extensions.Http.Polly ASP.NET Core integration for Polly policies.
Steeltoe Cloud-native .NET resilience framework.
Resilience4j.NET .NET adaptation of Java resilience patterns.
YARP Reverse proxy supporting resilience middleware.

Best Practices for Circuit Breaker Pattern

• Combine with Retry Pattern
• Use timeout policies
• Add logging and monitoring
• Configure thresholds carefully
• Use fallback mechanisms
• Monitor HALF-OPEN transitions
• Avoid excessive retries
• Test failure scenarios regularly

Summary

The Circuit Breaker Pattern is a resilience and fault-tolerance pattern in C# designed to prevent cascading failures in distributed systems and microservices. It temporarily blocks requests to failing services, allowing systems to recover while protecting resources and improving stability. The pattern is commonly implemented using Polly and ASP.NET Core HttpClientFactory. Although it introduces configuration and monitoring complexity, it is essential for building reliable cloud-native and distributed applications.

Data Management Patterns in C#

23. CQRS (Command Query Responsibility Segregation) in C#
24. Event Sourcing in C#
25. Saga Pattern in C#
26. Outbox Pattern in C#

Microservice Patterns in C#

27. Circuit Breaker Pattern in C#
28. Bulkhead Pattern in C#
29. Retry with Backoff Pattern in C#