Project

protoboard

0.0
No release in over 3 years
Low commit activity in last 3 years
Protoboard abstracts the way you use Circuit Breaker allowing you to easily use it with any Ruby Object
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 1.16
~> 10.0
~> 3.2
~> 3.0

Runtime

 Project Readme

Protoboard

Build Status Maintainability Test Coverage
Protoboard abstracts the way you use Circuit Breaker allowing you to easily use it with any Ruby Object, under the hood it uses the gem stoplight to create the circuits.

Installation

Add this line to your application's Gemfile:

gem 'protoboard'

And then execute:

$ bundle

Or install it yourself as:

$ gem install protoboard

Usage

The usage is really simple, just include Protoboard::CircuitBreaker and register your circuit.

class MyFooService
  include Protoboard::CircuitBreaker

  register_circuits [:some_method],
    options: {
      service: 'my_cool_service',
      open_after: 2,
      cool_off_after: 3
    }

  def some_method
    # Something that can break
  end
end

You can also define a fallback and callbacks for the circuit.

class MyFooService
  include Protoboard::CircuitBreaker

  register_circuits [:some_method],
    fallback: -> (error) { 'Do Something' }
    on_before: [->(ce) { Something.notify("Circuit #{ce.circuit.name}") }, ->(_) {}],
    on_after: [->(ce) { Something.notify("It fails with #{ce.error}") if ce.fail? }],
    options: {
      service: 'my_cool_service',
      open_after: 2,
      cool_off_after: 3
    }
  def some_method
    # Something that can break
  end
end

Also if you want to add more than one method in the same class and customize the circuit name:

class MyFooService
  include Protoboard::CircuitBreaker

  register_circuits({ some_method: 'my_custom_name', other_method: 'my_other_custom_name' },
    options: {
      service: 'my_service_name',
      open_after: 2,
      cool_off_after: 3
    })
  def some_method
    'OK'
  end
end

And you can add singleton methods to be wrapped by a circuit:

class MyFooService
  include Protoboard::CircuitBreaker

  register_circuits [:some_method, :some_singleton_method],
    singleton_methods: [:some_singleton_method],
    options: {
      service: 'my_cool_service',
      open_after: 2,
      cool_off_after: 3
    }

  def self.some_singleton_method
    # Something that can break
  end

  def some_method
    # Something that can break
  end
end

Callbacks

Any callback should receive one argument that it will be an instance of CircuitExecution class, that object will respond to the following methods:

  • state returns the current state of the circuit execution (:not_started, :success or :fail)
  • value returns the result value of the circuit execution, if the circuit fail the value will be nil.
  • error returns the error raised when the circuit fail, it will be nil if no error occurred
  • circuit returns a circuit instance which has the options configured in register_circuits (name, service, open_after and cool_off_after)
  • fail? returns true if the execution failed

P.S: In before calbacks the state will always be :not_started and in after callbacks the state can be either :fail or :success

Check Services and Circuits Status

If you want to check the services and circuits registered in Protoboard you can use Protoboard::CircuitBreaker.services_healthcheck, it will return a hash with the status of all circuits:

{
  'services' => {
    'my_service_name' => {
      'circuits' => {
        'some_namespace/my_service_name/SomeClass#some_method' => 'OK',
        'some_namespace/my_custom_name' => 'NOT_OK'
      }
    }
  }
}

PS: You can also disable the project namespace to be shown in Protoboard::CircuitBreaker.services_healthcheck passing the option with_namespace: false

Configuration

In configuration you can customize the adapter options and set callbacks and configurations for all the Protoboard Circuits:


Protoboard.configure do |config|
  config.adapter = Protoboard::Adapters::StoplightAdapter
  config.namespace = 'Foo'

  config.adapter.configure do |adapter|
    adapter.data_store = :redis # Default is :memory 
    adapter.redis_host = 'localhost'
    adapter.redis_port = 1234
  end
  
  #Global callbacks
  config.callbacks.before = [->(_) {}]
  config.callbacks.after = [MyCallableObject.new, ->(_) {}]
end

The available options are:

  • adapter = Sets the adapter, Protoboard::Adapters::StoplightAdapter is the default
  • namespace = It's used to name the circuit avoiding naming colision with other projects
  • callbacks.before = Receives an array of callables, lambdas, procs or any object that responds to call and receives one argument. It will be called before each circuit execution.
  • callbacks.after = Receives an array of callables, lambdas, procs or any object that responds to call and receives one argument. It will be called after each circuit execution.

StoplightAdapter Options

  • datastore = Sets the datastore(:redis or :memory). The default option is :memory
  • redis_host= Sets the redis host
  • redis_port= Sets the redis port

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/VAGAScom/protoboard.

License

The gem is available as open source under the terms of the MIT License.