simple_circuit_breaker 0.1.0 → 0.2.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.
- data/README.md +28 -3
- data/lib/simple_circuit_breaker.rb +10 -8
- data/simple_circuit_breaker.gemspec +18 -0
- data/test/simple_circuit_breaker_test.rb +32 -5
- metadata +3 -2
data/README.md
CHANGED
@@ -4,6 +4,11 @@
|
|
4
4
|
|
5
5
|
Simple Ruby implementation of the [Circuit Breaker design pattern][0].
|
6
6
|
|
7
|
+
This implementation aims to be as simple as possible. It does not have external
|
8
|
+
dependencies and only handles the core circuit breaker functionality. Wrapping
|
9
|
+
backend calls in timeouts and other exception handling is left to the user of
|
10
|
+
the library.
|
11
|
+
|
7
12
|
## Usage
|
8
13
|
|
9
14
|
```ruby
|
@@ -11,13 +16,26 @@ failure_threshold = 3 # Trip the circuit after 3 consecutive failures.
|
|
11
16
|
retry_timeout = 10 # Retry on an open circuit after 10 seconds.
|
12
17
|
circuit_breaker = SimpleCircuitBreaker.new(failure_threshold, retry_timeout)
|
13
18
|
|
19
|
+
# By default, all exceptions will trip the circuit.
|
14
20
|
circuit_breaker.handle do
|
15
|
-
|
21
|
+
FooClient.new.request
|
22
|
+
end
|
23
|
+
|
24
|
+
# Setting explicit exceptions that trip the circuit:
|
25
|
+
circuit_breaker.handle FooError, BarError do
|
26
|
+
FooClient.new.request
|
16
27
|
end
|
17
28
|
```
|
18
29
|
|
19
|
-
`SimpleCircuitBreaker#handle` raises a `SimpleCircuitBreaker::
|
20
|
-
circuit is open.
|
30
|
+
`SimpleCircuitBreaker#handle` raises a `SimpleCircuitBreaker::CircuitOpenError`
|
31
|
+
when the circuit is open. Otherwise, it re-raises any exceptions that occur in
|
32
|
+
the block.
|
33
|
+
|
34
|
+
## Installation
|
35
|
+
|
36
|
+
```bash
|
37
|
+
gem install simple_circuit_breaker
|
38
|
+
```
|
21
39
|
|
22
40
|
## Testing
|
23
41
|
|
@@ -33,9 +51,16 @@ rake
|
|
33
51
|
|
34
52
|
Julius Volz (julius@soundcloud.com), Tobias Schmidt (ts@soundcloud.com).
|
35
53
|
|
54
|
+
## Alternatives
|
55
|
+
|
56
|
+
* [Circuit Breaker][2]: heavily customizable circuit handler
|
57
|
+
* [CircuitB][3]: supports keeping global circuit state in memcached
|
58
|
+
|
36
59
|
## Contributing
|
37
60
|
|
38
61
|
Pull requests welcome!
|
39
62
|
|
40
63
|
[0]: http://en.wikipedia.org/wiki/Circuit_breaker_design_pattern
|
41
64
|
[1]: http://travis-ci.org/soundcloud/simple_circuit_breaker
|
65
|
+
[2]: https://github.com/wsargent/circuit_breaker
|
66
|
+
[3]: https://github.com/alg/circuit_b
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class SimpleCircuitBreaker
|
2
|
-
VERSION = '0.
|
2
|
+
VERSION = '0.2.0'
|
3
3
|
|
4
|
-
class
|
4
|
+
class CircuitOpenError < StandardError
|
5
5
|
end
|
6
6
|
|
7
7
|
attr_reader :failure_threshold, :retry_timeout
|
@@ -12,21 +12,23 @@ class SimpleCircuitBreaker
|
|
12
12
|
reset!
|
13
13
|
end
|
14
14
|
|
15
|
-
def handle(&block)
|
15
|
+
def handle(*exceptions, &block)
|
16
16
|
if tripped?
|
17
|
-
raise
|
17
|
+
raise CircuitOpenError, 'Circuit is open'
|
18
18
|
else
|
19
|
-
execute(&block)
|
19
|
+
execute(exceptions, &block)
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
23
|
protected
|
24
24
|
|
25
|
-
def execute(&block)
|
25
|
+
def execute(exceptions, &block)
|
26
26
|
begin
|
27
27
|
yield.tap { success! }
|
28
|
-
rescue Exception
|
29
|
-
|
28
|
+
rescue Exception => exception
|
29
|
+
if exceptions.empty? || exceptions.include?(exception.class)
|
30
|
+
fail!
|
31
|
+
end
|
30
32
|
raise
|
31
33
|
end
|
32
34
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require './lib/simple_circuit_breaker'
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'simple_circuit_breaker'
|
5
|
+
s.version = SimpleCircuitBreaker::VERSION
|
6
|
+
s.platform = Gem::Platform::RUBY
|
7
|
+
s.summary = 'Ruby Circuit Breaker implementation'
|
8
|
+
s.description = 'Simple Ruby implementation of the Circuit Breaker design pattern'
|
9
|
+
s.authors = ['Julius Volz', 'Tobias Schmidt']
|
10
|
+
s.email = 'julius@soundcloud.com ts@soundcloud.com'
|
11
|
+
s.homepage = 'http://github.com/soundcloud/simple_circuit_breaker'
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.test_files = `git ls-files -- test/*`.split("\n")
|
15
|
+
s.require_path = 'lib'
|
16
|
+
|
17
|
+
s.required_ruby_version = '>= 1.9'
|
18
|
+
end
|
@@ -32,7 +32,7 @@ describe SimpleCircuitBreaker do
|
|
32
32
|
foo.must_equal 42
|
33
33
|
end
|
34
34
|
|
35
|
-
it 'opens after 3 consecutive failures' do
|
35
|
+
it 'opens after 3 consecutive failures with no explicit handled exceptions' do
|
36
36
|
3.times do
|
37
37
|
begin
|
38
38
|
@breaker.handle { raise RuntimeError }
|
@@ -42,9 +42,36 @@ describe SimpleCircuitBreaker do
|
|
42
42
|
|
43
43
|
Proc.new do
|
44
44
|
@breaker.handle do
|
45
|
-
|
45
|
+
raise RuntimeError
|
46
|
+
end
|
47
|
+
end.must_raise SimpleCircuitBreaker::CircuitOpenError
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'opens after 3 consecutive failures for handled exception' do
|
51
|
+
3.times do
|
52
|
+
begin
|
53
|
+
@breaker.handle(RuntimeError) { raise RuntimeError }
|
54
|
+
rescue RuntimeError
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
Proc.new do
|
59
|
+
@breaker.handle(RuntimeError) do
|
60
|
+
raise RuntimeError
|
61
|
+
end
|
62
|
+
end.must_raise SimpleCircuitBreaker::CircuitOpenError
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'doesn\'t open after 3 consecutive failures for non-handled exception' do
|
66
|
+
class FooError < Exception
|
67
|
+
end
|
68
|
+
|
69
|
+
4.times do
|
70
|
+
begin
|
71
|
+
@breaker.handle(FooError) { raise RuntimeError }
|
72
|
+
rescue RuntimeError
|
46
73
|
end
|
47
|
-
end
|
74
|
+
end
|
48
75
|
end
|
49
76
|
end
|
50
77
|
|
@@ -55,7 +82,7 @@ describe SimpleCircuitBreaker do
|
|
55
82
|
@breaker.handle { raise RuntimeError } rescue nil
|
56
83
|
end
|
57
84
|
|
58
|
-
lambda { @breaker.handle {} }.must_raise SimpleCircuitBreaker::
|
85
|
+
lambda { @breaker.handle {} }.must_raise SimpleCircuitBreaker::CircuitOpenError
|
59
86
|
end
|
60
87
|
|
61
88
|
it 'closes after timeout and subsequent success' do
|
@@ -73,7 +100,7 @@ describe SimpleCircuitBreaker do
|
|
73
100
|
end
|
74
101
|
end.must_raise RuntimeError
|
75
102
|
|
76
|
-
lambda { @breaker.handle {} }.must_raise SimpleCircuitBreaker::
|
103
|
+
lambda { @breaker.handle {} }.must_raise SimpleCircuitBreaker::CircuitOpenError
|
77
104
|
end
|
78
105
|
end
|
79
106
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple_circuit_breaker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-11-
|
13
|
+
date: 2012-11-07 00:00:00.000000000 Z
|
14
14
|
dependencies: []
|
15
15
|
description: Simple Ruby implementation of the Circuit Breaker design pattern
|
16
16
|
email: julius@soundcloud.com ts@soundcloud.com
|
@@ -23,6 +23,7 @@ files:
|
|
23
23
|
- README.md
|
24
24
|
- Rakefile
|
25
25
|
- lib/simple_circuit_breaker.rb
|
26
|
+
- simple_circuit_breaker.gemspec
|
26
27
|
- test/simple_circuit_breaker_test.rb
|
27
28
|
homepage: http://github.com/soundcloud/simple_circuit_breaker
|
28
29
|
licenses: []
|