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 ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
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
+ [![Build Status](https://secure.travis-ci.org/soundcloud/simple_circuit_breaker.png)][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,8 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ task :default => :test
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.pattern = 'test/*_test.rb'
8
+ end
@@ -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