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.
Add this line to your application's Gemfile:
gem 'protoboard'
And then execute:
$ bundle
Or install it yourself as:
$ gem install protoboard
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
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 benil
.error
returns the error raised when the circuit fail, it will benil
if no error occurredcircuit
returns a circuit instance which has the options configured inregister_circuits
(name
,service
,open_after
andcool_off_after
)fail?
returnstrue
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
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
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 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 tocall
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 tocall
and receives one argument. It will be called after each circuit execution.
datastore =
Sets the datastore(:redis or :memory). The default option is :memoryredis_host=
Sets the redis hostredis_port=
Sets the redis port
Bug reports and pull requests are welcome on GitHub at https://github.com/VAGAScom/protoboard.
The gem is available as open source under the terms of the MIT License.