circuit_breaker 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/README.txt +1 -0
- data/circuit_breaker.gemspec +1 -1
- data/lib/circuit_breaker/circuit_handler.rb +9 -2
- data/lib/circuit_breaker/version.rb +1 -1
- data/spec/circuit_breaker_spec.rb +33 -3
- metadata +79 -111
data/History.txt
CHANGED
data/README.txt
CHANGED
data/circuit_breaker.gemspec
CHANGED
@@ -23,6 +23,11 @@ class CircuitBreaker::CircuitHandler
|
|
23
23
|
#
|
24
24
|
attr_accessor :invocation_timeout
|
25
25
|
|
26
|
+
#
|
27
|
+
# The exceptions which should be ignored if happens, they are not counted as failures
|
28
|
+
#
|
29
|
+
attr_accessor :excluded_exceptions
|
30
|
+
|
26
31
|
#
|
27
32
|
# Optional logger.
|
28
33
|
#
|
@@ -31,12 +36,14 @@ class CircuitBreaker::CircuitHandler
|
|
31
36
|
DEFAULT_FAILURE_THRESHOLD = 5
|
32
37
|
DEFAULT_FAILURE_TIMEOUT = 5
|
33
38
|
DEFAULT_INVOCATION_TIMEOUT = 30
|
39
|
+
DEFAULT_EXCLUDED_EXCEPTIONS= []
|
34
40
|
|
35
41
|
def initialize(logger = nil)
|
36
42
|
@logger = logger
|
37
43
|
@failure_threshold = DEFAULT_FAILURE_THRESHOLD
|
38
44
|
@failure_timeout = DEFAULT_FAILURE_TIMEOUT
|
39
45
|
@invocation_timeout = DEFAULT_INVOCATION_TIMEOUT
|
46
|
+
@excluded_exceptions = DEFAULT_EXCLUDED_EXCEPTIONS
|
40
47
|
end
|
41
48
|
|
42
49
|
#
|
@@ -61,8 +68,8 @@ class CircuitBreaker::CircuitHandler
|
|
61
68
|
out = method[*args]
|
62
69
|
on_success(circuit_state)
|
63
70
|
end
|
64
|
-
rescue Exception
|
65
|
-
on_failure(circuit_state)
|
71
|
+
rescue Exception => e
|
72
|
+
on_failure(circuit_state) unless @excluded_exceptions.include?(e.class)
|
66
73
|
raise
|
67
74
|
end
|
68
75
|
return out
|
@@ -3,6 +3,9 @@ require 'logger'
|
|
3
3
|
|
4
4
|
describe CircuitBreaker do
|
5
5
|
|
6
|
+
class SpecificException < Exception; end
|
7
|
+
class NotFoundException < Exception; end
|
8
|
+
|
6
9
|
class TestClass
|
7
10
|
|
8
11
|
include CircuitBreaker
|
@@ -36,9 +39,17 @@ describe CircuitBreaker do
|
|
36
39
|
"unresponsive method returned"
|
37
40
|
end
|
38
41
|
|
42
|
+
def raise_specific_error_method
|
43
|
+
if @failure == true
|
44
|
+
raise SpecificException.new "SPECIFIC FAIL"
|
45
|
+
end
|
46
|
+
|
47
|
+
raise NotFoundException.new "NOT FOUND FAIL"
|
48
|
+
end
|
49
|
+
|
39
50
|
# Register this method with the circuit breaker...
|
40
51
|
#
|
41
|
-
circuit_method :call_external_method, :second_method, :unresponsive_method
|
52
|
+
circuit_method :call_external_method, :second_method, :unresponsive_method, :raise_specific_error_method
|
42
53
|
|
43
54
|
#
|
44
55
|
# Define what needs to be set for configuration...
|
@@ -48,11 +59,13 @@ describe CircuitBreaker do
|
|
48
59
|
handler.failure_threshold = 5
|
49
60
|
handler.failure_timeout = 5
|
50
61
|
handler.invocation_timeout = 1
|
62
|
+
handler.excluded_exceptions = [NotFoundException]
|
51
63
|
end
|
52
64
|
|
53
65
|
end
|
54
66
|
|
55
67
|
before(:each) do
|
68
|
+
TestClass.circuit_handler.failure_threshold = 5
|
56
69
|
@test_object = TestClass.new()
|
57
70
|
end
|
58
71
|
|
@@ -113,9 +126,26 @@ describe CircuitBreaker do
|
|
113
126
|
@test_object.circuit_state.failure_count.should == 1
|
114
127
|
end
|
115
128
|
|
116
|
-
it 'should
|
129
|
+
it 'should increment the failure count when the method takes too long to return' do
|
117
130
|
lambda { @test_object.unresponsive_method }.should raise_error(CircuitBreaker::CircuitBrokenException)
|
118
|
-
@test_object.circuit_state.
|
131
|
+
@test_object.circuit_state.closed?.should == true
|
132
|
+
@test_object.circuit_state.failure_count.should == 1
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "and some exceptions not indicates a circuit problem" do
|
136
|
+
it 'should not increment the failure count when a failure of a specific type occurs' do
|
137
|
+
@test_object.fail!
|
138
|
+
|
139
|
+
lambda { @test_object.raise_specific_error_method }.should raise_error(SpecificException)
|
140
|
+
@test_object.circuit_state.closed?.should == true
|
141
|
+
@test_object.circuit_state.failure_count.should == 1
|
142
|
+
|
143
|
+
@test_object.succeed!
|
144
|
+
|
145
|
+
lambda { @test_object.raise_specific_error_method }.should raise_error(NotFoundException)
|
146
|
+
@test_object.circuit_state.closed?.should == true
|
147
|
+
@test_object.circuit_state.failure_count.should == 1
|
148
|
+
end
|
119
149
|
end
|
120
150
|
|
121
151
|
end
|
metadata
CHANGED
@@ -1,112 +1,87 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: circuit_breaker
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.0
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 1
|
8
|
-
- 0
|
9
|
-
- 1
|
10
|
-
version: 1.0.1
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Will Sargent
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2013-04-13 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
21
15
|
name: aasm
|
22
|
-
|
23
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
24
17
|
none: false
|
25
|
-
requirements:
|
26
|
-
- -
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
|
29
|
-
segments:
|
30
|
-
- 0
|
31
|
-
version: "0"
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
32
22
|
type: :runtime
|
33
|
-
version_requirements: *id001
|
34
|
-
- !ruby/object:Gem::Dependency
|
35
|
-
name: rake
|
36
23
|
prerelease: false
|
37
|
-
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
25
|
none: false
|
39
|
-
requirements:
|
40
|
-
- -
|
41
|
-
- !ruby/object:Gem::Version
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
46
38
|
type: :development
|
47
|
-
version_requirements: *id002
|
48
|
-
- !ruby/object:Gem::Dependency
|
49
|
-
name: rspec
|
50
39
|
prerelease: false
|
51
|
-
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
41
|
none: false
|
53
|
-
requirements:
|
54
|
-
- -
|
55
|
-
- !ruby/object:Gem::Version
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
60
54
|
type: :development
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
include CircuitBreaker
|
85
|
-
|
86
|
-
def call_remote_service() ...
|
87
|
-
|
88
|
-
circuit_method :call_remote_service
|
89
|
-
|
90
|
-
# Optional
|
91
|
-
circuit_handler do |handler|
|
92
|
-
handler.logger = Logger.new(STDOUT)
|
93
|
-
handler.failure_threshold = 5
|
94
|
-
handler.failure_timeout = 5
|
95
|
-
end
|
96
|
-
|
97
|
-
# Optional
|
98
|
-
circuit_handler_class MyCustomCircuitHandler
|
99
|
-
end
|
100
|
-
email:
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: ! "CircuitBreaker is a relatively simple Ruby mixin that will wrap\n
|
63
|
+
a call to a given service in a circuit breaker pattern.\n\n The circuit starts off
|
64
|
+
\"closed\" meaning that all calls will go through.\n However, consecutive failures
|
65
|
+
are recorded and after a threshold is reached,\n the circuit will \"trip\", setting
|
66
|
+
the circuit into an \"open\" state.\n\n In an \"open\" state, every call to the
|
67
|
+
service will fail by raising\n CircuitBrokenException.\n\n The circuit will remain
|
68
|
+
in an \"open\" state until the failure timeout has\n elapsed.\n\n After the failure_timeout
|
69
|
+
has elapsed, the circuit will go into\n a \"half open\" state and the call will
|
70
|
+
go through. A failure will\n immediately pop the circuit open again, and a success
|
71
|
+
will close the\n circuit and reset the failure count.\n\n require 'circuit_breaker'\n
|
72
|
+
\ class TestService\n\n include CircuitBreaker\n\n def call_remote_service()
|
73
|
+
...\n\n circuit_method :call_remote_service\n\n # Optional\n circuit_handler
|
74
|
+
do |handler|\n handler.logger = Logger.new(STDOUT)\n handler.failure_threshold
|
75
|
+
= 5\n handler.failure_timeout = 5\n end\n\n # Optional\n circuit_handler_class
|
76
|
+
MyCustomCircuitHandler\n end"
|
77
|
+
email:
|
101
78
|
- will.sargent@gmail.com
|
102
79
|
executables: []
|
103
|
-
|
104
80
|
extensions: []
|
105
|
-
|
106
|
-
extra_rdoc_files:
|
81
|
+
extra_rdoc_files:
|
107
82
|
- History.txt
|
108
83
|
- README.txt
|
109
|
-
files:
|
84
|
+
files:
|
110
85
|
- .gitignore
|
111
86
|
- .rspec
|
112
87
|
- Gemfile
|
@@ -123,39 +98,32 @@ files:
|
|
123
98
|
- spec/spec_helper.rb
|
124
99
|
homepage: http://github.com/wsargent/circuit_breaker
|
125
100
|
licenses: []
|
126
|
-
|
127
101
|
post_install_message:
|
128
|
-
rdoc_options:
|
102
|
+
rdoc_options:
|
129
103
|
- --main
|
130
104
|
- README.txt
|
131
105
|
- --charset=UTF-8
|
132
|
-
require_paths:
|
106
|
+
require_paths:
|
133
107
|
- lib
|
134
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
135
109
|
none: false
|
136
|
-
requirements:
|
137
|
-
- -
|
138
|
-
- !ruby/object:Gem::Version
|
139
|
-
|
140
|
-
|
141
|
-
- 0
|
142
|
-
version: "0"
|
143
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ! '>='
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
115
|
none: false
|
145
|
-
requirements:
|
146
|
-
- -
|
147
|
-
- !ruby/object:Gem::Version
|
148
|
-
|
149
|
-
segments:
|
150
|
-
- 0
|
151
|
-
version: "0"
|
116
|
+
requirements:
|
117
|
+
- - ! '>='
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
152
120
|
requirements: []
|
153
|
-
|
154
121
|
rubyforge_project: will_sargent
|
155
|
-
rubygems_version: 1.8.
|
122
|
+
rubygems_version: 1.8.23
|
156
123
|
signing_key:
|
157
124
|
specification_version: 3
|
158
|
-
summary: CircuitBreaker is a relatively simple Ruby mixin that will wrap a call to
|
159
|
-
|
125
|
+
summary: CircuitBreaker is a relatively simple Ruby mixin that will wrap a call to
|
126
|
+
a given service in a circuit breaker pattern
|
127
|
+
test_files:
|
160
128
|
- spec/circuit_breaker_spec.rb
|
161
129
|
- spec/spec_helper.rb
|