Protoboard
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:
statereturns the current state of the circuit execution (:not_started,:successor:fail)valuereturns the result value of the circuit execution, if the circuit fail the value will benil.errorreturns the error raised when the circuit fail, it will benilif no error occurredcircuitreturns a circuit instance which has the options configured inregister_circuits(name,service,open_afterandcool_off_after)fail?returnstrueif 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::StoplightAdapteris the defaultnamespace =It's used to name the circuit avoiding naming colision with other projectscallbacks.before =Receives an array of callables, lambdas, procs or any object that responds tocalland 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 tocalland receives one argument. It will be called after each circuit execution.
StoplightAdapter Options
datastore =Sets the datastore(:redis or :memory). The default option is :memoryredis_host=Sets the redis hostredis_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.