circuit_breakage 1.0.0 → 1.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.
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: