simple_circuit_breaker 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.
- data/.travis.yml +4 -0
- data/LICENSE +19 -0
- data/README.md +41 -0
- data/Rakefile +8 -0
- data/lib/simple_circuit_breaker.rb +70 -0
- data/test/simple_circuit_breaker_test.rb +79 -0
- metadata +52 -0
data/.travis.yml
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
The MIT License (MIT) Copyright (c) 2012 SoundCloud, Julius Volz, Tobias Schmidt
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
5
|
+
the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
7
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
8
|
+
so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# SimpleCircuitBreaker
|
2
|
+
|
3
|
+
## Overview
|
4
|
+
|
5
|
+
Simple Ruby implementation of the [Circuit Breaker design pattern][0].
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
failure_threshold = 3 # Trip the circuit after 3 consecutive failures.
|
11
|
+
retry_timeout = 10 # Retry on an open circuit after 10 seconds.
|
12
|
+
circuit_breaker = SimpleCircuitBreaker.new(failure_threshold, retry_timeout)
|
13
|
+
|
14
|
+
circuit_breaker.handle do
|
15
|
+
FakeClient.new.request
|
16
|
+
end
|
17
|
+
```
|
18
|
+
|
19
|
+
`SimpleCircuitBreaker#handle` raises a `SimpleCircuitBreaker::Error` when the
|
20
|
+
circuit is open.
|
21
|
+
|
22
|
+
## Testing
|
23
|
+
|
24
|
+
[][1]
|
25
|
+
|
26
|
+
Run the tests with
|
27
|
+
|
28
|
+
```bash
|
29
|
+
rake
|
30
|
+
```
|
31
|
+
|
32
|
+
## Authors
|
33
|
+
|
34
|
+
Julius Volz (julius@soundcloud.com), Tobias Schmidt (ts@soundcloud.com).
|
35
|
+
|
36
|
+
## Contributing
|
37
|
+
|
38
|
+
Pull requests welcome!
|
39
|
+
|
40
|
+
[0]: http://en.wikipedia.org/wiki/Circuit_breaker_design_pattern
|
41
|
+
[1]: http://travis-ci.org/soundcloud/simple_circuit_breaker
|
data/Rakefile
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
class SimpleCircuitBreaker
|
2
|
+
VERSION = '0.1.0'
|
3
|
+
|
4
|
+
class Error < StandardError
|
5
|
+
end
|
6
|
+
|
7
|
+
attr_reader :failure_threshold, :retry_timeout
|
8
|
+
|
9
|
+
def initialize(failure_threshold=3, retry_timeout=10)
|
10
|
+
@failure_threshold = failure_threshold
|
11
|
+
@retry_timeout = retry_timeout
|
12
|
+
reset!
|
13
|
+
end
|
14
|
+
|
15
|
+
def handle(&block)
|
16
|
+
if tripped?
|
17
|
+
raise Error, 'Circuit is open'
|
18
|
+
else
|
19
|
+
execute(&block)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def execute(&block)
|
26
|
+
begin
|
27
|
+
yield.tap { success! }
|
28
|
+
rescue Exception
|
29
|
+
fail!
|
30
|
+
raise
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def success!
|
35
|
+
if @state == :half_open
|
36
|
+
reset!
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def fail!
|
41
|
+
@failures += 1
|
42
|
+
if @failures >= @failure_threshold
|
43
|
+
@state = :open
|
44
|
+
@open_time = Time.now
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def reset!
|
49
|
+
@state = :closed
|
50
|
+
@failures = 0
|
51
|
+
end
|
52
|
+
|
53
|
+
def tripped?
|
54
|
+
@state != :closed && !try_to_close
|
55
|
+
end
|
56
|
+
|
57
|
+
def try_to_close
|
58
|
+
if timeout_exceeded?
|
59
|
+
@state = :half_open
|
60
|
+
true
|
61
|
+
else
|
62
|
+
false
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def timeout_exceeded?
|
67
|
+
@open_time + @retry_timeout < Time.now
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'minitest/spec'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require_relative '../lib/simple_circuit_breaker'
|
4
|
+
|
5
|
+
describe SimpleCircuitBreaker do
|
6
|
+
describe 'constructor' do
|
7
|
+
it 'takes two arguments' do
|
8
|
+
breaker = SimpleCircuitBreaker.new(5, 8)
|
9
|
+
|
10
|
+
breaker.failure_threshold.must_equal 5
|
11
|
+
breaker.retry_timeout.must_equal 8
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'works without explicit arguments' do
|
15
|
+
breaker = SimpleCircuitBreaker.new
|
16
|
+
|
17
|
+
breaker.failure_threshold.must_equal 3
|
18
|
+
breaker.retry_timeout.must_equal 10
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'closed circuit' do
|
23
|
+
before do
|
24
|
+
@breaker = SimpleCircuitBreaker.new(3, 10)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'is initially closed' do
|
28
|
+
foo = @breaker.handle do
|
29
|
+
42
|
30
|
+
end
|
31
|
+
|
32
|
+
foo.must_equal 42
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'opens after 3 consecutive failures' do
|
36
|
+
3.times do
|
37
|
+
begin
|
38
|
+
@breaker.handle { raise RuntimeError }
|
39
|
+
rescue RuntimeError
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
Proc.new do
|
44
|
+
@breaker.handle do
|
45
|
+
raiseRuntimeError
|
46
|
+
end
|
47
|
+
end.must_raise SimpleCircuitBreaker::Error
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe 'opened circuit' do
|
52
|
+
before do
|
53
|
+
@breaker = SimpleCircuitBreaker.new(3, 0.1)
|
54
|
+
3.times do
|
55
|
+
@breaker.handle { raise RuntimeError } rescue nil
|
56
|
+
end
|
57
|
+
|
58
|
+
lambda { @breaker.handle {} }.must_raise SimpleCircuitBreaker::Error
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'closes after timeout and subsequent success' do
|
62
|
+
sleep(0.15)
|
63
|
+
|
64
|
+
@breaker.handle { 23 }.must_equal 23
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'stays open after timeout and subsequent error' do
|
68
|
+
sleep(0.15)
|
69
|
+
|
70
|
+
Proc.new do
|
71
|
+
@breaker.handle do
|
72
|
+
raise RuntimeError
|
73
|
+
end
|
74
|
+
end.must_raise RuntimeError
|
75
|
+
|
76
|
+
lambda { @breaker.handle {} }.must_raise SimpleCircuitBreaker::Error
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
metadata
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: simple_circuit_breaker
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Julius Volz
|
9
|
+
- Tobias Schmidt
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2012-11-06 00:00:00.000000000 Z
|
14
|
+
dependencies: []
|
15
|
+
description: Simple Ruby implementation of the Circuit Breaker design pattern
|
16
|
+
email: julius@soundcloud.com ts@soundcloud.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- .travis.yml
|
22
|
+
- LICENSE
|
23
|
+
- README.md
|
24
|
+
- Rakefile
|
25
|
+
- lib/simple_circuit_breaker.rb
|
26
|
+
- test/simple_circuit_breaker_test.rb
|
27
|
+
homepage: http://github.com/soundcloud/simple_circuit_breaker
|
28
|
+
licenses: []
|
29
|
+
post_install_message:
|
30
|
+
rdoc_options: []
|
31
|
+
require_paths:
|
32
|
+
- lib
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ! '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '1.9'
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
requirements: []
|
46
|
+
rubyforge_project:
|
47
|
+
rubygems_version: 1.8.24
|
48
|
+
signing_key:
|
49
|
+
specification_version: 3
|
50
|
+
summary: Ruby Circuit Breaker implementation
|
51
|
+
test_files:
|
52
|
+
- test/simple_circuit_breaker_test.rb
|