Waldemar's Blog

Circuit Breaker Design Pattern

Introduction

One of my personal favourite patterns, the Circuit Breaker Design Pattern, is used to prevent an application from repeatedly trying to execute an operation that is likely to fail. This can lead to performance degradation, system failure, or missed data. In this post, I’m going to discuss the Circuit Breaker Design Pattern in detail, including its purpose, how it works, and its benefits.

Purpose of the Circuit Breaker Design Pattern

The purpose of the Circuit Breaker Design Pattern is to provide a way to handle errors and failures in distributed systems. It is essential to have a mechanism in place to handle these errors because they can cause cascading failures that can bring down an entire system. The Circuit Breaker Design Pattern provides a way to detect and handle these errors in a way that is both safe and efficient.

Think about it this way, if you have an external API that is crucial to your execution path, you’d need a way of postponing further execution if you detect that API is down. That’s where this pattern comes in.

How the Circuit Breaker Design Pattern Works

The Circuit Breaker Design Pattern works by isolating the code that is likely to fail and providing an alternative response when the code fails. When the Circuit Breaker detects that the code is failing, it trips and redirects the request to the alternative response or to a holding pattern. If the developer knows that all incoming events will need to be handled by the failing code, they can use something like a lock or semaphores to hold up the execution of further events until they know the path is clear.

There are three states that a Circuit Breaker can be in closed, open, and half-open. When the Circuit Breaker is closed, the code is allowed to execute normally. When the Circuit Breaker is open, the code is blocked, and requests are redirected to the alternative response or placed in a holding pattern. When the Circuit Breaker is half-open, the code is allowed to execute again, but the Circuit Breaker will trip again if the code fails, essentially testing the water to see if things have been rectified.

Here’s a simple flow diagram of what the execution path looks like:

Flow Diagram

This is very simplified and it uses a holding pattern to prevent further execution of events. The question it raises is “How would the circuit breaker ever get closed in the first place?” which (for the sake of brevity) I don’t show in this diagram but it would be part of either the regular execution path or a look ahead health check service that can determine when the path ahead is ready for execution again.

Benefits of the Circuit Breaker Design Pattern

The Circuit Breaker Design Pattern provides several benefits to developers. First, it improves the overall stability and reliability of the application by preventing cascading failures. Second, it reduces the load on the system by avoiding repeated requests to a failing service. Third, it provides a way to gracefully handle errors and failures, which improves the user experience.

Code Example in C#

Let’s look at a very simple coding example, in C#:

public class CircuitBreaker
{
    private readonly int failureThreshold;
    private readonly TimeSpan timeout;
    private readonly object lockObject = new object();

    private int failures;
    private DateTime? lastFailureTime;

    public CircuitBreaker(int failureThreshold, TimeSpan timeout)
    {
        this.failureThreshold = failureThreshold;
        this.timeout = timeout;
    }

    public bool IsClosed
    {
        get
        {
            lock (lockObject)
            {
                if (failures >= failureThreshold)
                {
                    if (lastFailureTime.HasValue && lastFailureTime.Value.Add(timeout) < DateTime.Now)
                    {
                        failures = 0;
                    }
                    else
                    {
                        return false;
                    }
                }

                return true;
            }
        }
    }

    public void Trip()
    {
        lock (lockObject)
        {
            failures++;
            lastFailureTime = DateTime.Now;
        }
    }

    public void Reset()
    {
        lock (lockObject)
        {
            failures = 0;
            lastFailureTime = null;
        }
    }
}

Here we have the CircuitBreaker class keeping track of the number of failures and the last time a failure occurred. If the number of failures exceeds a threshold, and the last failure occurs within a specified timeout period, the CircuitBreaker trips and blocks further requests. Otherwise, the CircuitBreaker remains closed and allows requests to pass through.

If you’re looking for a more thorough implementation of the pattern (along with some others), look no further than Polly, which is a “.NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner.” It’s a great project and something I recommend frequently.

Conclusion

The Circuit Breaker Design Pattern is a powerful tool for improving the reliability and stability of distributed systems. By isolating failing code and providing an alternative response, the Circuit Breaker can prevent cascading failures and improve the overall performance of the system. If you are developing a distributed system, consider using the Circuit Breaker Design Pattern to improve its reliability and stability.

#coding #computerScience #designPattern