simple_circuit_breaker 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|