Circuit Breaker for Rails applications

This is a popular resiliency pattern that protects applications when the external services that they’re dependent on is unavailable. A visual representation of the pattern is attached below:

Circuit Breaker pattern

There are three states in the Circuit Breaker:

Closed: The external service is available. The server makes the necessary request to the service and response obtained is sent back to the client.

Open: The external service is unavailable so no request is made by the server. Error message formulated by the server is sent back to the client.

Half-Open: The defined period of open state is complete. Any incoming request will be sent to the external service. If the request succeeds, the state is updated to ‘Closed’, if the request fails, the state is updated to ‘Open

circuitbox gem

This is a well maintained gem that can be used to implement the circuit breaker pattern. To configure this, it is important to determine the following parameters which vary based on each application:

volume_threshold: The number of incoming requests to consider before evaluating the circuit breaker state.

time_window: The time period over which the circuit breaker state is evaluated.

error_threshold: The error rate beyond which the circuit breaker state is updated to ‘Open’. This would be a ratio of the failed requests for the volume_threshold to time_window.

sleep_window: The time period after which the circuit breaker state is updated to ‘Half-Open’.

Dependency on Moneta

The circuitbox gem expects the configured cache to be Moneta compatible. Based on your application requirements (multi-process/threads), the appropriate backend can be configured.

Backend and feature support matrix can be found here: https://github.com/moneta-rb/moneta#backend-feature-matrix

NOTE: It is important to set expiry while initializing the cache since this affects the ‘Half-Open’ state

Sample code

class MyApplicationCircuitBreaker
def default_options
{
exceptions: [Net::OpenTimeout,Net::ReadTimeout],
sleep_window: 300,
time_window: 60,
volume_threshold: 10,
cache: Moneta.new(:Memory, expires: true),
error_threshold: 50
}
end
def circuit
Circuitbox.circuit(:my_service_name, default_options)
end
def request
circuit.run do
#code that would raise the exception configured in options
Net::HTTP.get("example.com", timeout: 0.1)
end
end
end

Next Steps

Per service configuration: The sample code currently supports default options only, but in a large system talking to different services, the circuit breaker options may vary based on the interaction with the external service. The type of exception and error rate can be configured on a per service basis.

Notifier: The notifier option can be configured to receive notifications when the circuit is open/closed and use this notification for custom code like logging/metrics.

Logger: The logger option can also be configured to obtain the circuitbox logs for the open/closed state transitions.

References

Full-Stack Developer

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store