Codeship Status for shiftcommerce/shift-circuit-breaker Maintainability Test Coverage

Shift Circuit Breaker

The Shift Circuit Breaker library implements a generic mechanism for detecting, monitoring and controlling external service calls that will most-likely fail at some point (e.g. timeout) and cause request queuing.

Although a plethora of Ruby circuit breaker libraries exist, those that are frequently updated have a dependency on Redis for persistence. We required a solution that did not depend on persisting to a shared data store, i.e. a library that stores counters in local memory. As a result, the Shift Circuit Breaker was born.

Similar to a conventional circuit breaker, when a circuit is closed it allows operations to flow through. When the number of sequential errors exceeds the error_threshold, the circuit is then opened for the defined skip_duration – no operations are executed and the provided fallback is called.

Installation

Add this line to your application's Gemfile:

  $ gem "shift-circuit-breaker"

And then execute:

  $ bundle

Or install it yourself as:

  $ gem install shift-circuit-breaker

Usage

Example usage is as follows -

  class MyClass
    CIRCUIT_BREAKER = Shift::CircuitBreaker.new(:some_identifier, 
                                                error_threshold: 10, 
                                                skip_duration: 60, 
                                                additional_exception_classes: [ 
                                                  ::Faraday::ClientError
                                                ]
                                              )

    def do_something
      CIRCUIT_BREAKER.call
        operation: -> { SomeService.new(name: 'test').perform_task },
        fallback:  -> { [ 1, 2, 3, 4, 5 ].sum }
    end
  end

Note: the operation and fallback should implement the public method #call or wrapped in a Proc/Lambda.

With regards to monitoring and logging, integration with New Relic and Sentry is included. To enable any of these features, please set the relevant configurations in an initializer (eg. shift_circuit_breaker.rb) as follows -

  require "shift/circuit_breaker"

  Shift::CircuitBreaker.configure do |config|
    config.new_relic_license_key = ENV["NEW_RELIC_LICENSE_KEY"]
    config.new_relic_app_name    = ENV["NEW_RELIC_APP_NAME"]
    config.sentry_dsn            = ENV["SENTRY_DSN"]
    config.sentry_environments   = %w[ production ]
  end

Note: both integrations can be overriden when instantiating the Shift::CircuitMonitor and Shift::CircuitLogger services, eg.

  CIRCUIT_BREAKER = Shift::CircuitBreaker.new(:some_identifier, 
                                              error_threshold: 10, 
                                              skip_duration: 60, 
                                              logger: Shift::CircuitBreaker::CircuitLogger.new(remote_logger: CUSTOM_LOGGER),
                                              monitor: Shift::CircuitBreaker::CircuitMonitor.new(monitor: CUSTOM_MONITOR)
                                            )

Contributing

Bug reports and pull requests are welcome. Please read our documentation on CONTRIBUTING.