circuitbox 1.0.3 → 2.0.0.pre3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +68 -122
  3. data/lib/circuitbox.rb +12 -56
  4. data/lib/circuitbox/circuit_breaker.rb +133 -154
  5. data/lib/circuitbox/circuit_breaker/logger_messages.rb +31 -0
  6. data/lib/circuitbox/configuration.rb +55 -0
  7. data/lib/circuitbox/errors/error.rb +1 -2
  8. data/lib/circuitbox/errors/open_circuit_error.rb +0 -1
  9. data/lib/circuitbox/errors/service_failure_error.rb +2 -1
  10. data/lib/circuitbox/excon_middleware.rb +15 -24
  11. data/lib/circuitbox/faraday_middleware.rb +38 -61
  12. data/lib/circuitbox/memory_store.rb +84 -0
  13. data/lib/circuitbox/memory_store/container.rb +28 -0
  14. data/lib/circuitbox/memory_store/monotonic_time.rb +11 -0
  15. data/lib/circuitbox/notifier/active_support.rb +19 -0
  16. data/lib/circuitbox/notifier/null.rb +11 -0
  17. data/lib/circuitbox/timer/monotonic.rb +17 -0
  18. data/lib/circuitbox/timer/null.rb +9 -0
  19. data/lib/circuitbox/timer/simple.rb +13 -0
  20. data/lib/circuitbox/version.rb +1 -1
  21. metadata +81 -149
  22. data/.gitignore +0 -20
  23. data/.ruby-version +0 -1
  24. data/.travis.yml +0 -8
  25. data/Gemfile +0 -6
  26. data/Rakefile +0 -18
  27. data/benchmark/circuit_store_benchmark.rb +0 -114
  28. data/circuitbox.gemspec +0 -38
  29. data/lib/circuitbox/memcache_store.rb +0 -31
  30. data/lib/circuitbox/notifier.rb +0 -34
  31. data/test/circuit_breaker_test.rb +0 -449
  32. data/test/circuitbox_test.rb +0 -54
  33. data/test/excon_middleware_test.rb +0 -131
  34. data/test/faraday_middleware_test.rb +0 -175
  35. data/test/integration/circuitbox_cross_process_open_test.rb +0 -56
  36. data/test/integration/faraday_middleware_test.rb +0 -78
  37. data/test/integration_helper.rb +0 -48
  38. data/test/notifier_test.rb +0 -21
  39. data/test/service_failure_error_test.rb +0 -30
  40. data/test/test_helper.rb +0 -15
data/.gitignore DELETED
@@ -1,20 +0,0 @@
1
- *.gem
2
- .vagrant
3
- *.rbc
4
- .bundle
5
- .config
6
- .yardoc
7
- Gemfile.lock
8
- InstalledFiles
9
- _yardoc
10
- coverage
11
- doc/
12
- lib/bundler/man
13
- pkg
14
- rdoc
15
- spec/reports
16
- test/tmp
17
- test/reports
18
- test/version_tmp
19
- tmp
20
- .idea
@@ -1 +0,0 @@
1
- 2.1.5
@@ -1,8 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 1.9.3
4
- - 2.0.0
5
- - 2.1
6
- - 2.2
7
- before_install:
8
- - gem install bundler
data/Gemfile DELETED
@@ -1,6 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in circuitbox.gemspec
4
- gemspec
5
-
6
- gem "pry"
data/Rakefile DELETED
@@ -1,18 +0,0 @@
1
- require 'rake/testtask'
2
- require "bundler/gem_version_tasks"
3
-
4
- Rake::TestTask.new do |t|
5
- t.libs << 'test'
6
- t.test_files = FileList['test/**/*_test.rb']
7
- end
8
-
9
- desc "run the circuitbox benchmark scripts"
10
- task :benchmark do
11
- benchmark_scripts = FileList.new("./benchmark/*_benchmark.rb")
12
- benchmark_scripts.each do |script|
13
- system "bundle exec ruby #{script}"
14
- end
15
- end
16
-
17
- desc "Run tests"
18
- task :default => :test
@@ -1,114 +0,0 @@
1
- require 'circuitbox'
2
- require 'benchmark'
3
- require 'pstore'
4
- require 'tempfile'
5
- require 'tmpdir'
6
- require 'lmdb'
7
- require 'pry'
8
-
9
-
10
- class Circuitbox
11
- class CircuitBreaker
12
- # silence the circuitbreaker logger
13
- DEV_NULL = (RUBY_PLATFORM =~ /mswin|mingw/ ? "NUL" : "/dev/null")
14
- def logger
15
- @_dev_null_logger ||= Logger.new DEV_NULL
16
- end
17
- end
18
- end
19
-
20
- def service
21
- # 10% success rate to make the circuitbreaker flip flop
22
- if rand(10) <= 0
23
- "success"
24
- else
25
- raise RuntimeError, "fail"
26
- end
27
- end
28
-
29
- def run_flip_flopping circuit
30
- circuit.run { service }
31
- circuit.try_close_next_time if circuit.open?
32
- end
33
-
34
- def without_gc
35
- GC.start
36
- GC.disable
37
- yield
38
- GC.enable
39
- end
40
-
41
- def benchmark_circuitbox_method_with_reporter method, reporter
42
- without_gc { send(method, reporter) }
43
- Circuitbox.reset
44
- end
45
-
46
- def circuit_with_cache cache
47
- Circuitbox.circuit :performance, CIRCUIT_OPTIONS.merge(cache: cache)
48
- end
49
-
50
- CIRCUIT_OPTIONS = {
51
- exceptions: [RuntimeError],
52
- sleep_window: 0,
53
- time_window: 1
54
- }
55
-
56
- RUNS = 10000
57
-
58
- def circuit_store_memory_one_process reporter
59
- circuit = circuit_with_cache Moneta.new(:Memory)
60
-
61
- reporter.report "memory:" do
62
- RUNS.times { run_flip_flopping circuit }
63
- end
64
-
65
- circuit.circuit_store.close
66
- end
67
-
68
- def circuit_store_pstore_one_process reporter
69
- Tempfile.create("test_circuit_store_pstore_one_process") do |dbfile|
70
- circuit = circuit_with_cache Moneta.new(:PStore, file: dbfile)
71
-
72
- reporter.report "pstore:" do
73
- RUNS.times { run_flip_flopping circuit }
74
- end
75
-
76
- circuit.circuit_store.close
77
- end
78
- end
79
-
80
- def circuit_store_lmdb_one_process reporter
81
- Dir.mktmpdir("test_circuit_store_lmdb_one_process") do |dbdir|
82
- circuit = circuit_with_cache Moneta.new(:LMDB, dir: dbdir, db: "circuitbox_lmdb")
83
-
84
- reporter.report "lmdb:" do
85
- RUNS.times { run_flip_flopping circuit }
86
- end
87
-
88
- circuit.circuit_store.close
89
- end
90
- end
91
-
92
- def circuit_store_daybreak_one_process reporter
93
- Tempfile.create("test_circuit_store_daybreak_one_process") do |dbfile|
94
- circuit = circuit_with_cache Moneta.new(:Daybreak, file: dbfile)
95
-
96
- reporter.report "daybreak:" do
97
- RUNS.times { run_flip_flopping circuit }
98
- end
99
-
100
- circuit.circuit_store.close
101
- end
102
- end
103
-
104
- Benchmark.bm(8) do |x|
105
- benchmark_circuitbox_method_with_reporter :circuit_store_memory_one_process, x
106
- benchmark_circuitbox_method_with_reporter :circuit_store_lmdb_one_process, x
107
- benchmark_circuitbox_method_with_reporter :circuit_store_pstore_one_process, x
108
- benchmark_circuitbox_method_with_reporter :circuit_store_daybreak_one_process, x
109
- end
110
-
111
-
112
-
113
-
114
-
@@ -1,38 +0,0 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'circuitbox/version'
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = "circuitbox"
8
- spec.version = Circuitbox::VERSION
9
- spec.authors = ["Fahim Ferdous"]
10
- spec.email = ["fahimfmf@gmail.com"]
11
- spec.description = %q{A robust circuit breaker that manages failing external services.}
12
- spec.summary = %q{A robust circuit breaker that manages failing external services.}
13
- spec.homepage = ""
14
- spec.license = "MIT"
15
-
16
- spec.files = `git ls-files`.split($/)
17
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ["lib"]
20
-
21
- spec.add_development_dependency "bundler"
22
- spec.add_development_dependency "rake"
23
- spec.add_development_dependency "rack"
24
- spec.add_development_dependency "gimme"
25
- spec.add_development_dependency "minitest"
26
- spec.add_development_dependency "mocha"
27
- spec.add_development_dependency "typhoeus"
28
- spec.add_development_dependency "timecop"
29
- spec.add_development_dependency "faraday"
30
- spec.add_development_dependency "excon"
31
- spec.add_development_dependency "logger"
32
- spec.add_development_dependency "bundler-gem_version_tasks"
33
- spec.add_development_dependency "lmdb"
34
- spec.add_development_dependency "daybreak"
35
-
36
- spec.add_dependency "activesupport"
37
- spec.add_dependency "moneta"
38
- end
@@ -1,31 +0,0 @@
1
- module ActiveSupport
2
- module Cache
3
- class MemcacheStore
4
- def initialize(cache)
5
- @cache = cache
6
- end
7
-
8
- def read(key, options = {})
9
- @cache.get(key, options)
10
- rescue Memcached::NotFound
11
- nil
12
- end
13
-
14
- def increment(key)
15
- @cache.incr(key)
16
- end
17
-
18
- def write(key, value, options = {})
19
- if expires_in = options.delete(:expires_in)
20
- options[:expiry] = expires_in.to_i
21
- end
22
-
23
- @cache.set(key, value, options)
24
- end
25
-
26
- def delete(key)
27
- @cache.delete(key)
28
- end
29
- end
30
- end
31
- end
@@ -1,34 +0,0 @@
1
- class Circuitbox
2
- class Notifier
3
- def initialize(service, partition=nil)
4
- @service = service
5
- @partition = partition
6
- end
7
-
8
- def notify(event)
9
- return unless notification_available?
10
- ActiveSupport::Notifications.instrument("circuit_#{event}", circuit: circuit_name)
11
- end
12
-
13
- def notify_warning(message)
14
- return unless notification_available?
15
- ActiveSupport::Notifications.instrument("circuit_warning", { circuit: circuit_name, message: message})
16
- end
17
-
18
- def metric_gauge(gauge, value)
19
- return unless notification_available?
20
- ActiveSupport::Notifications.instrument("circuit_gauge", { circuit: circuit_name, gauge: gauge.to_s, value: value })
21
- end
22
-
23
- private
24
- def circuit_name
25
- circuit_name = @service.to_s
26
- circuit_name += ":#{@partition}" if @partition
27
- circuit_name
28
- end
29
-
30
- def notification_available?
31
- defined? ActiveSupport::Notifications
32
- end
33
- end
34
- end
@@ -1,449 +0,0 @@
1
- require 'test_helper'
2
- require 'ostruct'
3
-
4
- class CircuitBreakerTest < Minitest::Test
5
- SUCCESSFUL_RESPONSE_STRING = "Success!"
6
- RequestFailureError = Timeout::Error
7
- class ConnectionError < StandardError; end;
8
- class SomeOtherError < StandardError; end;
9
-
10
- def setup
11
- Circuitbox::CircuitBreaker.reset
12
- end
13
-
14
- describe 'initialize' do
15
- it 'force sleep_window to equal time_window if it is too short' do
16
- circuit = Circuitbox::CircuitBreaker.new(:yammer,
17
- :sleep_window => 1,
18
- :time_window => 10
19
- )
20
- assert_equal circuit.option_value(:sleep_window),
21
- circuit.option_value(:time_window),
22
- 'sleep_window has not been corrected properly'
23
- end
24
- end
25
-
26
- def test_goes_into_half_open_state_on_sleep
27
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
28
- circuit.send(:open!)
29
- assert circuit.send(:half_open?)
30
- end
31
-
32
-
33
- describe 'ratio' do
34
- def cb_options
35
- {
36
- sleep_window: 300,
37
- volume_threshold: 5,
38
- error_threshold: 33,
39
- timeout_seconds: 1
40
- }
41
- end
42
-
43
- def setup
44
- Circuitbox::CircuitBreaker.reset
45
- @circuit = Circuitbox::CircuitBreaker.new(:yammer, cb_options)
46
- end
47
-
48
-
49
- it 'open the circuit on 100% failure' do
50
- run_counter = 0
51
- 10.times do
52
- @circuit.run do
53
- run_counter += 1
54
- raise RequestFailureError
55
- end
56
- end
57
- assert_equal 6, run_counter, 'the circuit did not open after 6 failures (5 failures + 10%)'
58
- end
59
-
60
- it 'keep circuit closed on 0% failure' do
61
- run_counter = 0
62
- 10.times do
63
- @circuit.run do
64
- run_counter += 1
65
- 'sucess'
66
- end
67
- end
68
- assert_equal 10, run_counter, 'run block was not executed 10 times'
69
- end
70
-
71
- it 'open the circuit even after 1 success' do
72
- run_counter = 0
73
- 5.times do
74
- @circuit.run do
75
- run_counter += 1
76
- raise RequestFailureError
77
- end
78
- end
79
-
80
- # one success
81
- @circuit.run { 'success'}
82
- assert_equal 5, @circuit.failure_count, 'the total count of failures is not 5'
83
-
84
- 5.times do
85
- @circuit.run do
86
- run_counter += 1
87
- raise RequestFailureError
88
- end
89
- end
90
- assert_equal 5, run_counter, 'the circuit did not open after 5 failures (5 failures + 10%)'
91
- end
92
-
93
- it 'keep circuit closed when failure ratio do not exceed limit' do
94
- run_counter = 0
95
- 7.times do
96
- @circuit.run do
97
- run_counter += 1
98
- 'sucess'
99
- end
100
- end
101
- assert_equal 0, @circuit.failure_count, 'some errors were counted'
102
-
103
- 3.times do
104
- @circuit.run do
105
- run_counter += 1
106
- raise RequestFailureError
107
- end
108
- end
109
- assert_equal 10, run_counter, 'block was not executed 10 times'
110
- assert @circuit.error_rate < 33, 'error_rate pass over 33%'
111
- end
112
-
113
- it 'circuit open when failure ratio exceed limit' do
114
- run_counter = 0
115
- 10.times do
116
- @circuit.run do
117
- run_counter += 1
118
- 'sucess'
119
- end
120
- end
121
- assert_equal 0, @circuit.failure_count, 'some errors were counted'
122
-
123
- 10.times do
124
- @circuit.run do
125
- run_counter += 1
126
- raise RequestFailureError
127
- end
128
- end
129
- # 5 failure on 15 run is 33%
130
- assert_equal 15, run_counter, 'block was not executed 10 times'
131
- assert @circuit.error_rate >= 33, 'error_rate pass over 33%'
132
- end
133
-
134
- end
135
-
136
- describe 'exceptions' do
137
- before do
138
- Circuitbox::CircuitBreaker.reset
139
- @circuit = Circuitbox::CircuitBreaker.new(:yammer, exceptions: [SomeOtherError])
140
- end
141
-
142
- it 'raises exception when circuit is open' do
143
- @circuit.stubs(open_flag?: true)
144
- -> { @circuit.run! {} }.must_raise Circuitbox::OpenCircuitError
145
- end
146
-
147
- it 'raises exception when service fails' do
148
- err = -> { @circuit.run! { raise SomeOtherError } }.must_raise Circuitbox::ServiceFailureError
149
- err.original.must_be_instance_of SomeOtherError
150
- end
151
- end
152
-
153
- describe 'closing the circuit after sleep' do
154
- def cb_options
155
- {
156
- sleep_window: 1,
157
- time_window: 2,
158
- volume_threshold: 5,
159
- error_threshold: 5,
160
- timeout_seconds: 1
161
- }
162
- end
163
-
164
- def setup
165
- @circuit = Circuitbox::CircuitBreaker.new(:yammer, cb_options)
166
- end
167
-
168
- it 'close the circuit after sleeping time' do
169
- # lets open the circuit
170
- (cb_options[:error_threshold] + 1).times { @circuit.run { raise RequestFailureError } }
171
- run_count = 0
172
- @circuit.run { run_count += 1 }
173
- assert_equal 0, run_count, 'circuit is not open'
174
- # it is + 2 on purpose, because + 1 is flaky here
175
- sleep cb_options[:sleep_window] + 2
176
-
177
- @circuit.run { run_count += 1 }
178
- assert_equal 1, run_count, 'circuit is not closed'
179
- end
180
- end
181
-
182
- describe "when in half open state" do
183
- before do
184
- Circuitbox::CircuitBreaker.reset
185
- @circuit = Circuitbox::CircuitBreaker.new(:yammer)
186
- end
187
-
188
- it "opens circuit on next failed request" do
189
- @circuit.stubs(half_open?: true)
190
- @circuit.expects(:open!)
191
- @circuit.run { raise RequestFailureError }
192
- end
193
-
194
- it "closes circuit on successful request" do
195
- @circuit.send(:half_open!)
196
- @circuit.run { 'success' }
197
- assert !@circuit.send(:half_open?)
198
- assert !@circuit.send(:open?)
199
- end
200
- end
201
-
202
- def test_should_use_timeout_class_if_exceptions_are_not_defined
203
- circuit = Circuitbox::CircuitBreaker.new(:yammer, timeout_seconds: 45)
204
- circuit.expects(:timeout).with(45).once
205
- emulate_circuit_run(circuit, :success, StandardError)
206
- end
207
-
208
- def test_should_not_use_timeout_class_if_custom_exceptions_are_defined
209
- circuit = Circuitbox::CircuitBreaker.new(:yammer, exceptions: [ConnectionError])
210
- circuit.expects(:timeout).never
211
- emulate_circuit_run(circuit, :success, StandardError)
212
- end
213
-
214
- def test_should_return_response_if_it_doesnt_timeout
215
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
216
- response = emulate_circuit_run(circuit, :success, SUCCESSFUL_RESPONSE_STRING)
217
- assert_equal SUCCESSFUL_RESPONSE_STRING, response
218
- end
219
-
220
- def test_timeout_seconds_run_options_overrides_circuit_options
221
- circuit = Circuitbox::CircuitBreaker.new(:yammer, timeout_seconds: 60)
222
- circuit.expects(:timeout).with(30).once
223
- circuit.run(timeout_seconds: 30) { true }
224
- end
225
-
226
- def test_catches_connection_error_failures_if_defined
227
- circuit = Circuitbox::CircuitBreaker.new(:yammer, :exceptions => [ConnectionError])
228
- response = emulate_circuit_run(circuit, :failure, ConnectionError)
229
- assert_equal nil, response
230
- end
231
-
232
- def test_doesnt_catch_out_of_scope_exceptions
233
- circuit = Circuitbox::CircuitBreaker.new(:yammer, :exceptions => [ConnectionError, RequestFailureError])
234
-
235
- assert_raises SomeOtherError do
236
- emulate_circuit_run(circuit, :failure, SomeOtherError)
237
- end
238
- end
239
-
240
- def test_records_response_failure
241
- circuit = Circuitbox::CircuitBreaker.new(:yammer, :exceptions => [RequestFailureError])
242
- circuit.expects(:log_event).with(:failure)
243
- emulate_circuit_run(circuit, :failure, RequestFailureError)
244
- end
245
-
246
- def test_records_response_success
247
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
248
- circuit.expects(:log_event).with(:success)
249
- emulate_circuit_run(circuit, :success, SUCCESSFUL_RESPONSE_STRING)
250
- end
251
-
252
- def test_does_not_send_request_if_circuit_is_open
253
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
254
- circuit.stubs(:open? => true)
255
- circuit.expects(:yield).never
256
- response = emulate_circuit_run(circuit, :failure, RequestFailureError)
257
- assert_equal nil, response
258
- end
259
-
260
- def test_returns_nil_response_on_failed_request
261
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
262
- response = emulate_circuit_run(circuit, :failure, RequestFailureError)
263
- assert_equal nil, response
264
- end
265
-
266
- def test_puts_circuit_to_sleep_once_opened
267
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
268
- circuit.stubs(:open? => true)
269
-
270
- assert !circuit.send(:open_flag?)
271
- emulate_circuit_run(circuit, :failure, RequestFailureError)
272
- assert circuit.send(:open_flag?)
273
-
274
- circuit.expects(:open!).never
275
- emulate_circuit_run(circuit, :failure, RequestFailureError)
276
- end
277
-
278
- def test_open_is_true_if_open_flag
279
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
280
- circuit.stubs(:open_flag? => true)
281
- assert circuit.open?
282
- end
283
-
284
- def test_open_checks_if_volume_threshold_has_passed
285
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
286
- circuit.stubs(:open_flag? => false)
287
-
288
- circuit.expects(:passed_volume_threshold?).once
289
- circuit.open?
290
- end
291
-
292
- def test_open_checks_error_rate_threshold
293
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
294
- circuit.stubs(:open_flag? => false,
295
- :passed_volume_threshold? => true)
296
-
297
- circuit.expects(:passed_rate_threshold?).once
298
- circuit.open?
299
- end
300
-
301
- def test_open_is_false_if_awake_and_under_rate_threshold
302
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
303
- circuit.stubs(:open_flag? => false,
304
- :passed_volume_threshold? => false,
305
- :passed_rate_threshold => false)
306
-
307
- assert !circuit.open?
308
- end
309
-
310
- def test_error_rate_threshold_calculation
311
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
312
- circuit.stubs(:failure_count => 3, :success_count => 2)
313
- assert circuit.send(:passed_rate_threshold?)
314
-
315
- circuit.stubs(:failure_count => 2, :success_count => 3)
316
- assert !circuit.send(:passed_rate_threshold?)
317
- end
318
-
319
- def test_logs_and_retrieves_success_events
320
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
321
- 5.times { circuit.send(:log_event, :success) }
322
- assert_equal 5, circuit.send(:success_count)
323
- end
324
-
325
- def test_logs_and_retrieves_failure_events
326
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
327
- 5.times { circuit.send(:log_event, :failure) }
328
- assert_equal 5, circuit.send(:failure_count)
329
- end
330
-
331
- def test_logs_events_by_minute
332
- circuit = Circuitbox::CircuitBreaker.new(:yammer)
333
-
334
- Timecop.travel(Time.now.change(sec: 5))
335
- 4.times { circuit.send(:log_event, :success) }
336
- assert_equal 4, circuit.send(:success_count)
337
-
338
- Timecop.travel(1.minute.from_now)
339
- 7.times { circuit.send(:log_event, :success) }
340
- assert_equal 7, circuit.send(:success_count)
341
-
342
- Timecop.travel(30.seconds.from_now)
343
- circuit.send(:log_event, :success)
344
- assert_equal 8, circuit.send(:success_count)
345
-
346
- Timecop.travel(50.seconds.from_now)
347
- assert_equal 0, circuit.send(:success_count)
348
- end
349
-
350
- describe 'notifications' do
351
-
352
- def setup
353
- Circuitbox::CircuitBreaker.reset
354
- end
355
-
356
- def circuit
357
- Circuitbox::CircuitBreaker.new(:yammer, :notifier_class => @notifier)
358
- end
359
-
360
-
361
- it 'notifies on open circuit' do
362
- @notifier = gimme_notifier
363
- c = circuit
364
- 10.times { c.run { raise RequestFailureError }}
365
- assert @notifier.notified?, 'no notification sent'
366
- end
367
-
368
- it 'notifies on close circuit' do
369
- @notifier = gimme_notifier
370
- c = circuit
371
- 5.times { c.run { raise RequestFailureError }}
372
- clear_notified!
373
- 10.times { c.run { 'success' }}
374
- assert @notifier.notified?, 'no notification sent'
375
- end
376
-
377
- it 'notifies warning if sleep_window is shorter than time_window' do
378
- @notifier = gimme_notifier
379
- Circuitbox::CircuitBreaker.new(:yammer,
380
- :notifier_class => @notifier,
381
- :sleep_window => 1,
382
- :time_window => 10
383
- )
384
- assert @notifier.notified?, 'no notification sent'
385
- end
386
-
387
- it 'DO NOT notifies warning if sleep_window is longer than time_window' do
388
- @notifier = gimme_notifier
389
- Circuitbox::CircuitBreaker.new(:yammer,
390
- :notifier_class => @notifier,
391
- :sleep_window => 11,
392
- :time_window => 10
393
- )
394
- assert_equal false, @notifier.notified?, 'no notification sent'
395
- end
396
-
397
-
398
- it 'notifies error_rate on error_rate calculation' do
399
- @notifier = gimme_notifier(metric: :error_rate, metric_value: 0.0)
400
- 10.times { circuit.run {'success' }}
401
- assert @notifier.notified?, 'no notification sent'
402
- end
403
-
404
- it 'notifies failure_count on error_rate calculation' do
405
- @notifier = gimme_notifier(metric: :failure_count, metric_value: 1)
406
- 10.times { circuit.run { raise RequestFailureError }}
407
- assert @notifier.notified?, 'no notification sent'
408
- end
409
-
410
- it 'notifies success_count on error_rate calculation' do
411
- @notifier = gimme_notifier(metric: :success_count, metric_value: 6)
412
- 10.times { circuit.run { 'success' }}
413
- assert @notifier.notified?, 'no notification sent'
414
- end
415
-
416
- def clear_notified!
417
- @notified = false
418
- end
419
-
420
- def gimme_notifier(opts={})
421
- clear_notified!
422
- metric = opts.fetch(:metric,:error_rate)
423
- metric_value = opts.fetch(:metric_value, 0.0)
424
- warning_msg = opts.fetch(:warning_msg, '')
425
- fake_notifier = gimme
426
- give(fake_notifier).notify(:open) { @notified=true }
427
- give(fake_notifier).notify(:close) { @notified=true }
428
- give(fake_notifier).notify_warning(Gimme::Matchers::Anything.new) { @notified = true }
429
- give(fake_notifier).metric_gauge(metric, metric_value) { @notified=true }
430
- fake_notifier_class = gimme
431
- give(fake_notifier_class).new(:yammer,nil) { fake_notifier }
432
- give(fake_notifier_class).notified? { @notified }
433
- fake_notifier_class
434
- end
435
- end
436
-
437
- def emulate_circuit_run(circuit, response_type, response_value)
438
- circuit.run do
439
- case response_type
440
- when :failure
441
- raise response_value
442
- when :success
443
- response_value
444
- end
445
- end
446
- rescue RequestFailureError
447
- nil
448
- end
449
- end