disyuntor 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7ab5e6badbc47e7babf6bd794c93b14c826458bd
4
+ data.tar.gz: a8b0a6b6fc72713b987ae48fb091364471568d08
5
+ SHA512:
6
+ metadata.gz: 3bd3266ebe4e42789dbeac531495e08544a2959ed4b62b39e900a312f6cb1084bbab73f8f02d21aac1df44303d614f077ddb8324642187f9f866e84a31ef2264
7
+ data.tar.gz: 5d2350086bb03289c4510998d7fa806fd3e7c443c7b2615f77750432a8e2304c37dda443f6d8454a8470d2942981d8ddecfce77131e0dfa5c5f809d083137046
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Leandro López
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,50 @@
1
+ # Simple Circuit Breaker Pattern in Ruby
2
+
3
+ [![Join the chat at https://gitter.im/inkel/disyuntor](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/inkel/disyuntor?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
4
+
5
+ This gem implements a very simple class to deal with the Circuit Breaker Pattern as described by [Michael T. Nygard](http://www.michaelnygard.com/) in his amazing and highly recommended [Release It! - Design and Deploy Production-Ready Software](http://www.amazon.com/Release-It-Production-Ready-Pragmatic-Programmers/dp/0978739213).
6
+
7
+ ## Usage
8
+
9
+ ```ruby
10
+ require "disyuntor"
11
+
12
+ options = {
13
+ # Trip circuit after 10 errors
14
+ threshold: 10,
15
+ # Wait 5 seconds before trying again
16
+ timeout: 5
17
+ }
18
+
19
+ circuit_breaker = Disyuntor.new(options)
20
+
21
+ res = circuit_breaker.try do
22
+ # …your potentially failing operation…
23
+ end
24
+ ```
25
+
26
+ By default, when the circuit is open, `Disyuntor#try` will fail with a `Disyuntor::CircuitOpenError`. This behavior can be changed by passing a `Proc` in the `on_circuit_open` option or method.
27
+
28
+ If you want to use it as a [`Rack`](https://github.com/rack/rack) middleware, add the following in your `config.ru`:
29
+
30
+ ```ruby
31
+ require "rack/disyuntor"
32
+
33
+ use Rack::Disyuntor, threshold: 10, timeout: 5
34
+ ```
35
+
36
+ This will start responding with `[503, { "Content-Type" => "text/plain", ["Service Unavailable"]]` when the circuit is open.
37
+
38
+ ## Custom actions when circuit is open
39
+
40
+ Every time the circuit is open, the `#on_circuit_open` method is called, passing the circuit as its argument. This allows customizing the failure mode of your circuit:
41
+
42
+ ```ruby
43
+ circuit_breaker = Disyuntor.new(threshold: 3, timeout: 5)
44
+
45
+ circuit_breaker.on_circuit_open do |c|
46
+ "Circuit was open at #{c.opened_at}"
47
+ end
48
+ ```
49
+
50
+ The value of the block will be returned as the value of `Disyuntor#try` when it fails.
@@ -0,0 +1,20 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require_relative "lib/disyuntor/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "disyuntor"
7
+ s.version = Disyuntor::VERSION
8
+ s.summary = "Circuit Breaker Pattern in Ruby"
9
+ s.description = "Simple implementation of Michael T. Nygard's Circuit Breaker Pattern"
10
+ s.authors = ["Leandro López"]
11
+ s.email = ["inkel.ar@gmail.com"]
12
+ s.homepage = "http://github.com/inkel/disyuntor"
13
+ s.license = "MIT"
14
+
15
+ s.required_ruby_version = '>= 2.0'
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+
19
+ s.add_dependency "micromachine", "1.2.0"
20
+ end
@@ -0,0 +1,92 @@
1
+ require "micromachine"
2
+
3
+ class Disyuntor
4
+ CircuitOpenError = Class.new(RuntimeError)
5
+
6
+ attr_reader :failures, :opened_at, :threshold, :timeout
7
+
8
+ def initialize(threshold: 5, timeout: 10, &block)
9
+ @threshold = threshold
10
+ @timeout = timeout
11
+
12
+ @on_circuit_open = if block_given?
13
+ block
14
+ else
15
+ Proc.new{ fail CircuitOpenError }
16
+ end
17
+
18
+ close!
19
+ end
20
+
21
+ def states
22
+ @states ||= MicroMachine.new(:closed).tap do |fsm|
23
+ fsm.when(:trip, :half_open => :open, :closed => :open)
24
+ fsm.when(:reset, :half_open => :closed, :closed => :closed)
25
+ fsm.when(:try, :open => :half_open)
26
+
27
+ fsm.on(:open) do
28
+ @opened_at = Time.now.to_i
29
+ end
30
+
31
+ fsm.on(:closed) do
32
+ @opened_at = nil
33
+ @failures = 0
34
+ end
35
+ end
36
+ end
37
+
38
+ def close! () states.trigger!(:reset) end
39
+ def open! () states.trigger!(:trip) end
40
+ def half_open! () states.trigger!(:try) end
41
+
42
+ def state () states.state end
43
+
44
+ def closed? () state == :closed end
45
+ def open? () state == :open end
46
+ def half_open? () state == :half_open end
47
+
48
+ def timed_out?
49
+ open? && Time.now.to_i > (@opened_at + @timeout)
50
+ end
51
+
52
+ def try(&block)
53
+ half_open! if timed_out?
54
+
55
+ case
56
+ when closed? then on_circuit_closed(&block)
57
+ when half_open? then on_circuit_half_open(&block)
58
+ when open? then on_circuit_open
59
+ else
60
+ fail RuntimeError, "Invalid state! #{state}"
61
+ end
62
+ end
63
+
64
+ def on_circuit_closed(&block)
65
+ ret = block.call
66
+ rescue
67
+ @failures += 1
68
+ open! if @failures > @threshold
69
+ raise
70
+ else
71
+ close!
72
+ ret
73
+ end
74
+
75
+ def on_circuit_half_open(&block)
76
+ ret = block.call
77
+ rescue
78
+ open!
79
+ raise
80
+ else
81
+ close!
82
+ ret
83
+ end
84
+
85
+ def on_circuit_open(&block)
86
+ if block_given?
87
+ @on_circuit_open = block
88
+ else
89
+ @on_circuit_open.(self)
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,3 @@
1
+ class Disyuntor
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,20 @@
1
+ require "rack"
2
+ require_relative "../disyuntor"
3
+
4
+ class Rack::Disyuntor
5
+ def initialize(app, options={})
6
+ @app = app
7
+
8
+ options[:on_circuit_open] ||= -> { circuit_open_response }
9
+
10
+ @circuit_breaker = Disyuntor.new(options)
11
+ end
12
+
13
+ def call(env)
14
+ @circuit_breaker.try { @app.call(env) }
15
+ end
16
+
17
+ def circuit_open_response
18
+ [503, { "Content-Type" => "text/plain" }, ["Service Unavailable"]]
19
+ end
20
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: disyuntor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Leandro López
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-09-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: micromachine
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 1.2.0
27
+ description: Simple implementation of Michael T. Nygard's Circuit Breaker Pattern
28
+ email:
29
+ - inkel.ar@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - LICENSE
35
+ - README.md
36
+ - disyuntor.gemspec
37
+ - lib/disyuntor.rb
38
+ - lib/disyuntor/version.rb
39
+ - lib/rack/disyuntor.rb
40
+ homepage: http://github.com/inkel/disyuntor
41
+ licenses:
42
+ - MIT
43
+ metadata: {}
44
+ post_install_message:
45
+ rdoc_options: []
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '2.0'
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubyforge_project:
60
+ rubygems_version: 2.2.2
61
+ signing_key:
62
+ specification_version: 4
63
+ summary: Circuit Breaker Pattern in Ruby
64
+ test_files: []