Supervision
Write distributed systems that are resilient and self-heal. Remote calls can fail or hang indefinietly without a response. Supervision will help to isolate failure and keep individual components from bringing down the whole system. The basic idea is to wrap dangerous method call inside protected
supervisehelper that will monitor for failure and handle it according to the specified rules to prevent it from cascading.
Installation
Add this line to your application's Gemfile:
gem 'supervision'
And then execute:
$ bundle
Or install it yourself as:
$ gem install supervision
1 Usage
Supervision instance takes the following configuration options:
-
:max_failure- maximum failure count allowed before Supervision raisesCircuitBreakerOpenError. By default5 failuresare allowed. -
:call_timeout- duration time for a method before it is assumed to have failed. By default10 milliseconds. -
:reset_timeout- duration before a method is allowed to attempt a call. Subsequent calls will fail fast if failure is detected. By default100 milliseconds
Next to instantiate the Supervision in order to protect a call to external/remote service that has potential to fail do:
@supervision = Supervision.new { |arg| remote_api_call(arg) }or alternatively use supervise helper
@supervision = Supervision.supervise { |arg| remote_api_call(arg) }Once the call is wrapped you can execute it by sending call messsage with arguments like so:
@supervision.call({user: 'Piotr'})2 System
You can register more than one Supervision by using internal register system. Simply register name under which you want the circuit to be available by calling supervise_as helper:
Supervision.supervise_as(:danger) { remote_api_call }In order to retrieve registered circuit you can use hash syntax:
Supervision[:danger] # => returns registered circuitThe name under which method is registerd will be available as a method call. Consequently, to execute registered circuit do:
Supervision.danger(:foo, :bar) # => will call underlying method and pass :foo, :barr3 Mixin
Supervision can also act as a mixin and expose supervise and supervise_as accordingly. Use supervise_as if you want to be able to register supervised calls inside Supervision system. Otherwise, use supervise helper to create anonymous supervised call.
class Api
include Supervision
def remote_call
...
end
supervise_as :danger { remote_call } # => register supervision as :danger
def fetch(repository)
danger(repository)
rescue Supervision::CircuitBreakerOpenError
nil
end
end
@api = Api.new
@api.fetch('github_api')4 Callbacks
You can listen for failure and success by attaching on_failure, on_success listeners respectively:
@supervision.on_failure { notify_me }
def notify_me
puts("The circuit breaker is now open")
end5 Configuration
If you want to configure Supervision, you can either pass options directly
@supervision = Supervison.new max_failures: 2, call_timeout: 10.milli, reset_timeout: 0.1.sec do
remote_api_call
endor use configure helper
@supervision.configure do
max_failures 5
call_timeout 10.sec
reset_timeout 1.min
end6 Time
All the numeric types are extended with time related helpers to allow for more fluid parameters when creating Supervision
call_timeout: 10.milliseconds
call_timeout: 10.millis
call_timeout: 1.millisecond
call_timeout: 1.milli
call_timeout: 1.second
call_timeout: 1.sec
call_timeout: 10.secs
call_timeout: 10.seconds
call_timeout: 1.minute
call_timeout: 1.min
call_timeout: 10.minutes
call_timeout: 10.mins
call_timeout: 1.hour
call_timeout: 10.hoursContributing
- Fork it ( https://github.com/piotrmurach/supervision/fork )
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request
Copyright
Copyright (c) 2014-2016 Piotr Murach. See LICENSE for further details.