Enhance your application's resilience with the circuit breaker pattern


Created by Stan on 08-04-2023


In modern software systems, applications often rely on external services or APIs for various tasks. These dependencies can sometimes become the weakest link in your application, causing performance issues or complete failures if the external service experiences downtime. One way to protect your application from these issues is to implement the Circuit Breaker pattern. In this blog post, we'll discuss the Circuit Breaker pattern and demonstrate how to implement it in Ruby using code samples.

What is the circuit breaker pattern?

The Circuit Breaker pattern is a design pattern that helps improve the resilience of an application by monitoring the status of external services and preventing excessive resource consumption during failures. When the circuit breaker detects that a service is unavailable or experiencing high error rates, it "trips" or "opens" the circuit, preventing further requests to the failing service. After a specified period, the circuit breaker allows a limited number of requests to pass through to check if the service has recovered. If the service is still unavailable, the circuit remains open; otherwise, it returns to the "closed" state, allowing requests to flow normally.

Implementing a circuit breaker in ruby

Let's create a simple circuit breaker class in Ruby to demonstrate its implementation:

# circuit_breaker.rb
class CircuitBreaker
  attr_reader :failure_threshold, :recovery_timeout

  def initialize(failure_threshold: 5, recovery_timeout: 60)
    @failure_threshold = failure_threshold
    @recovery_timeout = recovery_timeout
    @state = :closed
    @failure_count = 0
    @last_failure_time = nil
  end

  def call
    return handle_open_circuit if open?

    begin
      result = yield
      reset_failure_count if closed?
      result
    rescue StandardError => e
      handle_failure(e)
    end
  end

  private

  def handle_failure(error)
    @failure_count += 1
    @last_failure_time = Time.now

    if @failure_count >= failure_threshold
      open_circuit
      puts "Circuit is now open: #{error.message}"
    else
      raise error
    end
  end

  def handle_open_circuit
    if Time.now - @last_failure_time >= recovery_timeout
      begin
        result = yield
        close_circuit
        puts "Circuit is now closed"
        result
      rescue StandardError => e
        open_circuit
        puts "Circuit remains open: #{e.message}"
      end
    else
      puts "Circuit is open; request not allowed"
    end
  end

  def open?
    @state == :open
  end

  def closed?
    @state == :closed
  end

  def open_circuit
    @state = :open
  end

  def close_circuit
    @state = :closed
    reset_failure_count
  end

  def reset_failure_count
    @failure_count = 0
  end
end

In this example, we create a CircuitBreaker class that takes two parameters: failure_threshold and recovery_timeout. The call method wraps the code block and manages the circuit state. If the circuit is closed and the wrapped code raises an exception, the failure count increases. When the failure count reaches the threshold, the circuit opens. In the open state, the circuit breaker prevents further requests and checks the recovery timeout. After the recovery timeout, it allows a limited number of requests to pass through to test the service availability.

Calling the circuit breaker in your application

Now let's see how to use the CircuitBreaker class in a Ruby application. Imagine we have a simple client that fetches data from an external API:

# api_client.rb
require 'net/http'

class ApiClient
  def fetch_data(endpoint)
    uri = URI(endpoint)
    response = Net::HTTP.get_response(uri)

    if response.is_a?(Net::HTTPSuccess)
      JSON.parse(response.body)
    else
      raise "API request failed with status: #{response.code}"
    end
  end
end

We can now use the CircuitBreaker class to protect our application from failures in the ApiClient:

# app.rb
require_relative 'circuit_breaker'
require_relative 'api_client'

circuit_breaker = CircuitBreaker.new(failure_threshold: 3, recovery_timeout: 30)
api_client = ApiClient.new
endpoint = "https://api.example.com/data"

10.times do
  circuit_breaker.call do
    begin
      data = api_client.fetch_data(endpoint)
      puts "Data fetched successfully: #{data}"
    rescue => e
      puts "Error: #{e.message}"
    end
  end

  sleep(5)
end

In this example, we create an instance of the CircuitBreaker with a failure threshold of 3 and a recovery timeout of 30 seconds. We then make 10 requests to the external API using the ApiClient wrapped in the CircuitBreaker. If the API experiences issues, the circuit breaker will open after three consecutive failures, preventing further requests for 30 seconds.

Enhancing the circuit breaker using libraries

The simple circuit breaker implementation shown in this article can be extended with additional features, such as monitoring, logging, or exponential backoff for recovery. Alternatively, you can use existing Ruby libraries that implement the circuit breaker pattern, such as Yammer's circuitbox or Shopify's semian gems.

The Circuit Breaker pattern is an essential tool for building resilient and fault-tolerant applications, especially when dealing with external services or APIs. In this article, we explored the concept of the Circuit Breaker pattern and demonstrated how to implement a basic version in Ruby. By incorporating this pattern into your Ruby applications, you can better manage failures and prevent cascading issues that could impact your application's performance and user experience. As you continue to develop your applications, consider adopting the Circuit Breaker pattern to enhance your application's resilience and stability.



Related Posts

Architecture

Posted on 07 Apr, 2023
Using DDD to build a bookstore

Architecture

Posted on 08 Apr, 2023
How to create a binary search tree in Ruby

Architecture

Posted on 10 Apr, 2023
Rate limiting in Ruby using Sinatra and Redis