circuit_breakage 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 44c83ed5565612433627a4466aa4486ebdf25543
4
- data.tar.gz: a0f65eb73e9dd27f832adf19d860e979112efb9e
3
+ metadata.gz: cba4ea6a897024a9a64f381298b155806e47ade7
4
+ data.tar.gz: bda065d0f7132b76891004752add0cc948bcfc6e
5
5
  SHA512:
6
- metadata.gz: 1eaece305ff4a9fb83f1cd4f1a138930b28bde2724bd70c8d0d6fae16a67afa0a94e8514034fe9272133530fb7cd5ad349a264316490ddbf026c2e02e28ea879
7
- data.tar.gz: 79ca29de3a040028ff01023c07889ce12fcf88b249fbb694082ce87e48737e3083bbfbd59ba1b875027d3e0965891c68215a0888d6e8ccb0e1fd324b58f6bef1
6
+ metadata.gz: 04e698f1eff6b98ad18a4ad6359c96db40d7756a3bec6c04c1e44f78aecf5f05781cd1f506631139535c1ee9f8c47585a915750e8d44e3dcafa79eb0cf4196c7
7
+ data.tar.gz: 9d5cfd0d6bf4e8b3ba7ef1f349e28f27ab92b9951c3dde89a94bc30e7ff0b5bb43bc3eabcdd79600a3f60a445c1a6112d5748a9098cf30e036a210f9f626fadd
data/README.md CHANGED
@@ -18,10 +18,16 @@ proc = ->(*args) do
18
18
  end
19
19
 
20
20
  breaker = CircuitBreakage::Breaker.new(proc)
21
+
22
+ # These options are required.
21
23
  breaker.failure_threshold = 3 # only 3 failures before tripping circuit
22
24
  breaker.duration = 10 # 10 seconds before retry
23
25
  breaker.timeout = 0.5 # 500 milliseconds allowed before auto-fail
24
26
 
27
+ # These options are, uh, optional.
28
+ breaker.only_trip_on = [ExpensiveFailureException]
29
+ breaker.never_trip_on = [CheapUnimportantFailureException]
30
+
25
31
  begin
26
32
  breaker.call(*some_args) # args are passed through to the proc
27
33
  rescue CircuitBreakage::CircuitOpen
@@ -10,16 +10,21 @@ module CircuitBreakage
10
10
  class Breaker
11
11
  attr_accessor :failure_count, :last_failed, :state, :block
12
12
  attr_accessor :failure_threshold, :duration, :timeout, :last_exception
13
+ attr_accessor :only_trip_on, :never_trip_on
13
14
 
14
- DEFAULT_FAILURE_THRESHOLD = 5 # Number of failures required to trip circuit
15
- DEFAULT_DURATION = 300 # Number of seconds the circuit stays tripped
16
- DEFAULT_TIMEOUT = 10 # Number of seconds before the call times out
15
+ DEFAULT_FAILURE_THRESHOLD = 5 # Number of failures required to trip circuit
16
+ DEFAULT_DURATION = 300 # Number of seconds the circuit stays tripped
17
+ DEFAULT_TIMEOUT = 10 # Number of seconds before the call times out
18
+ DEFAULT_ONLY_TRIP_ON = [Exception] # Exceptions that trigger the breaker
19
+ DEFAULT_NEVER_TRIP_ON = [] # Exceptions that won't trigger the breaker
17
20
 
18
21
  def initialize(block=nil)
19
22
  self.block = block
20
23
  self.failure_threshold = DEFAULT_FAILURE_THRESHOLD
21
24
  self.duration = DEFAULT_DURATION
22
25
  self.timeout = DEFAULT_TIMEOUT
26
+ self.only_trip_on = DEFAULT_ONLY_TRIP_ON
27
+ self.never_trip_on = DEFAULT_NEVER_TRIP_ON
23
28
  self.failure_count ||= 0
24
29
  self.last_failed ||= 0
25
30
  self.state ||= 'closed'
@@ -55,7 +60,8 @@ module CircuitBreakage
55
60
  handle_success
56
61
 
57
62
  return ret_value
58
- rescue Exception => e
63
+ rescue *self.only_trip_on => e
64
+ raise if never_trip_on.any? { |t| e.instance_of?(t) }
59
65
  handle_failure(e)
60
66
  end
61
67
 
@@ -1,3 +1,3 @@
1
1
  module CircuitBreakage
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -1,4 +1,7 @@
1
1
  module CircuitBreakage
2
+ class VerySpecificException < StandardError
3
+ end
4
+
2
5
  describe Breaker do
3
6
  let(:breaker) { Breaker.new(block) }
4
7
  let(:block) { ->(x) { return x } }
@@ -78,6 +81,85 @@ module CircuitBreakage
78
81
  expect { breaker.call(arg) }.to raise_exception(CircuitBreakage::CircuitTimeout)
79
82
  end
80
83
  end
84
+
85
+ context 'with specific exceptions defined' do
86
+ before do
87
+ breaker.only_trip_on = [VerySpecificException]
88
+ end
89
+
90
+ context 'and the call fails with one of the specific exceptions' do
91
+ let(:block) { ->(_) { raise VerySpecificException } }
92
+
93
+ it { is_expected.to change { breaker.failure_count }.by(1) }
94
+ it { is_expected.to change { breaker.last_failed } }
95
+ it { is_expected.to change { breaker.last_exception }.from(nil) }
96
+
97
+ it 'raises the exception that caused the failure' do
98
+ expect { breaker.call(arg) }.to raise_exception(VerySpecificException)
99
+ end
100
+ end
101
+
102
+ context 'and the call fails with a different exception (including timeouts)' do
103
+ let(:block) { ->(_) { raise Timeout::Error } }
104
+
105
+ it { is_expected.not_to change { breaker.failure_count } }
106
+ it { is_expected.not_to change { breaker.last_failed } }
107
+ it { is_expected.not_to change { breaker.last_exception } }
108
+
109
+ it 'raises the exception that caused the failure' do
110
+ expect { breaker.call(arg) }.to raise_exception(Timeout::Error)
111
+ end
112
+ end
113
+ end
114
+
115
+ context 'with specific exceptions excluded' do
116
+ before do
117
+ breaker.never_trip_on = [VerySpecificException]
118
+ end
119
+
120
+ context 'and the call fails with one of the specific exceptions' do
121
+ let(:block) { ->(_) { raise VerySpecificException } }
122
+
123
+ it { is_expected.not_to change { breaker.failure_count } }
124
+ it { is_expected.not_to change { breaker.last_failed } }
125
+ it { is_expected.not_to change { breaker.last_exception } }
126
+
127
+ it 'raises the exception that caused the failure' do
128
+ expect { breaker.call(arg) }.to raise_exception(VerySpecificException)
129
+ end
130
+ end
131
+
132
+ context 'and the call fails with a different exception' do
133
+ let(:block) { ->(_) { raise RuntimeError } }
134
+
135
+ it { is_expected.to change { breaker.failure_count }.by(1) }
136
+ it { is_expected.to change { breaker.last_failed } }
137
+ it { is_expected.to change { breaker.last_exception }.from(nil) }
138
+
139
+ it 'raises the exception that caused the failure' do
140
+ expect { breaker.call(arg) }.to raise_exception(RuntimeError)
141
+ end
142
+ end
143
+ end
144
+
145
+ context 'with overlapping whitelisted and blacklisted exceptions' do
146
+ before do
147
+ breaker.only_trip_on = [StandardError]
148
+ breaker.never_trip_on = [VerySpecificException] # inherits from StandardError
149
+ end
150
+
151
+ context 'and the call fails with an overlapped exception' do
152
+ let(:block) { ->(_) { raise VerySpecificException } }
153
+
154
+ it { is_expected.not_to change { breaker.failure_count } }
155
+ it { is_expected.not_to change { breaker.last_failed } }
156
+ it { is_expected.not_to change { breaker.last_exception } }
157
+
158
+ it 'raises the exception that caused the failure' do
159
+ expect { breaker.call(arg) }.to raise_exception(VerySpecificException)
160
+ end
161
+ end
162
+ end
81
163
  end
82
164
 
83
165
  context 'when the circuit is open' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: circuit_breakage
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Hyland
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-19 00:00:00.000000000 Z
11
+ date: 2016-03-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -108,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
108
108
  version: '0'
109
109
  requirements: []
110
110
  rubyforge_project:
111
- rubygems_version: 2.2.2
111
+ rubygems_version: 2.4.5
112
112
  signing_key:
113
113
  specification_version: 4
114
114
  summary: Provides a simple circuit breaker pattern.
@@ -116,4 +116,3 @@ test_files:
116
116
  - spec/breaker_spec.rb
117
117
  - spec/redis_backed_breaker_spec.rb
118
118
  - spec/spec_helper.rb
119
- has_rdoc: