circuitbox 1.1.1 → 2.0.0.pre4

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.
Files changed (37) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +53 -187
  3. data/lib/circuitbox/circuit_breaker/logger_messages.rb +31 -0
  4. data/lib/circuitbox/circuit_breaker.rb +134 -154
  5. data/lib/circuitbox/configuration.rb +51 -0
  6. data/lib/circuitbox/errors/error.rb +3 -2
  7. data/lib/circuitbox/errors/open_circuit_error.rb +3 -1
  8. data/lib/circuitbox/errors/service_failure_error.rb +5 -1
  9. data/lib/circuitbox/excon_middleware.rb +23 -30
  10. data/lib/circuitbox/faraday_middleware.rb +43 -63
  11. data/lib/circuitbox/memory_store/container.rb +30 -0
  12. data/lib/circuitbox/memory_store/monotonic_time.rb +13 -0
  13. data/lib/circuitbox/memory_store.rb +85 -0
  14. data/lib/circuitbox/notifier/active_support.rb +19 -0
  15. data/lib/circuitbox/notifier/null.rb +13 -0
  16. data/lib/circuitbox/timer.rb +51 -0
  17. data/lib/circuitbox/version.rb +3 -1
  18. data/lib/circuitbox.rb +14 -54
  19. metadata +106 -117
  20. data/.gitignore +0 -20
  21. data/.ruby-version +0 -1
  22. data/.travis.yml +0 -9
  23. data/Gemfile +0 -6
  24. data/Rakefile +0 -30
  25. data/benchmark/circuit_store_benchmark.rb +0 -114
  26. data/circuitbox.gemspec +0 -48
  27. data/lib/circuitbox/notifier.rb +0 -34
  28. data/test/circuit_breaker_test.rb +0 -436
  29. data/test/circuitbox_test.rb +0 -45
  30. data/test/excon_middleware_test.rb +0 -131
  31. data/test/faraday_middleware_test.rb +0 -175
  32. data/test/integration/circuitbox_cross_process_open_test.rb +0 -56
  33. data/test/integration/faraday_middleware_test.rb +0 -78
  34. data/test/integration_helper.rb +0 -48
  35. data/test/notifier_test.rb +0 -21
  36. data/test/service_failure_error_test.rb +0 -23
  37. data/test/test_helper.rb +0 -15
@@ -1,175 +0,0 @@
1
- require 'test_helper'
2
- require 'circuitbox/faraday_middleware'
3
-
4
- class SentialException < StandardError; end
5
-
6
- class Circuitbox
7
- class FaradayMiddlewareTest < Minitest::Test
8
-
9
- attr_reader :app
10
-
11
- def setup
12
- @app = gimme
13
- end
14
-
15
- def test_default_identifier
16
- env = { url: "sential" }
17
- assert_equal "sential", FaradayMiddleware.new(app).identifier.call(env)
18
- end
19
-
20
- def test_overwrite_identifier
21
- middleware = FaradayMiddleware.new(app, identifier: "sential")
22
- assert_equal middleware.identifier, "sential"
23
- end
24
-
25
- def test_overwrite_default_value_generator_lambda
26
- stub_circuitbox
27
- env = { url: "url" }
28
- give(circuitbox).circuit("url", anything) { circuit }
29
- give(circuit).run!(anything) { raise Circuitbox::Error }
30
- default_value_generator = lambda { |response| :sential }
31
- middleware = FaradayMiddleware.new(app,
32
- circuitbox: circuitbox,
33
- default_value: default_value_generator)
34
- assert_equal :sential, middleware.call(env)
35
- end
36
-
37
- def test_default_value_generator_lambda_passed_error
38
- stub_circuitbox
39
- env = { url: "url" }
40
- give(circuitbox).circuit("url", anything) { circuit }
41
- give(circuit).run!(anything) { raise Circuitbox::Error.new("error text") }
42
- default_value_generator = lambda { |_,error| error.message }
43
- middleware = FaradayMiddleware.new(app,
44
- circuitbox: circuitbox,
45
- default_value: default_value_generator)
46
- assert_equal "error text", middleware.call(env)
47
- end
48
-
49
- def test_overwrite_default_value_generator_static_value
50
- stub_circuitbox
51
- env = { url: "url" }
52
- give(circuitbox).circuit("url", anything) { circuit }
53
- give(circuit).run!(anything) { raise Circuitbox::Error }
54
- middleware = FaradayMiddleware.new(app, circuitbox: circuitbox, default_value: :sential)
55
- assert_equal :sential, middleware.call(env)
56
- end
57
-
58
- def test_default_exceptions
59
- middleware = FaradayMiddleware.new(app)
60
- assert_includes middleware.exceptions, Faraday::Error::TimeoutError
61
- assert_includes middleware.exceptions, FaradayMiddleware::RequestFailed
62
- end
63
-
64
- def test_overridde_success_response
65
- env = { url: "url" }
66
- app = gimme
67
- give(app).call(anything) { Faraday::Response.new(status: 500) }
68
- error_response = lambda { |response| false }
69
- response = FaradayMiddleware.new(app, open_circuit: error_response).call(env)
70
- assert_kind_of Faraday::Response, response
71
- assert_equal response.status, 500
72
- assert response.finished?
73
- refute response.success?
74
- end
75
-
76
- def test_default_success_response
77
- env = { url: "url" }
78
- app = gimme
79
- give(app).call(anything) { Faraday::Response.new(status: 500) }
80
- response = FaradayMiddleware.new(app).call(env)
81
- assert_kind_of Faraday::Response, response
82
- assert_equal response.status, 503
83
- assert response.finished?
84
- refute response.success?
85
- end
86
-
87
- def test_default_open_circuit_does_not_trip_on_400
88
- env = { url: "url" }
89
- app = gimme
90
- give(app).call(anything) { Faraday::Response.new(status: 400) }
91
- response = FaradayMiddleware.new(app).call(env)
92
- assert_kind_of Faraday::Response, response
93
- assert_equal response.status, 400
94
- assert response.finished?
95
- refute response.success?
96
- end
97
-
98
- def test_default_open_circuit_does_trip_on_nil
99
- env = { url: "url" }
100
- app = gimme
101
- give(app).call(anything) { Faraday::Response.new(status: nil) }
102
- response = FaradayMiddleware.new(app).call(env)
103
- assert_kind_of Faraday::Response, response
104
- assert_equal response.status, 503
105
- assert response.finished?
106
- refute response.success?
107
- end
108
-
109
- def test_overwrite_exceptions
110
- middleware = FaradayMiddleware.new(app, exceptions: [SentialException])
111
- assert_includes middleware.exceptions, SentialException
112
- end
113
-
114
- def test_pass_circuit_breaker_run_options
115
- stub_circuitbox
116
- give(circuit).run!(:sential)
117
- give(circuitbox).circuit("url", anything) { circuit }
118
- env = { url: "url", circuit_breaker_run_options: :sential }
119
- middleware = FaradayMiddleware.new(app, circuitbox: circuitbox)
120
- middleware.call(env)
121
- verify(circuit, 1.times).run!(:sential)
122
- end
123
-
124
- def test_pass_circuit_breaker_options
125
- stub_circuitbox
126
- env = { url: "url" }
127
- expected_circuit_breaker_options = {
128
- sential: :sential,
129
- exceptions: FaradayMiddleware::DEFAULT_EXCEPTIONS
130
- }
131
- give(circuitbox).circuit("url", expected_circuit_breaker_options) { circuit }
132
- options = { circuitbox: circuitbox, circuit_breaker_options: { sential: :sential } }
133
- middleware = FaradayMiddleware.new(app, options)
134
- middleware.call(env)
135
-
136
- verify(circuitbox, 1.times).circuit("url", expected_circuit_breaker_options)
137
- end
138
-
139
- def test_overwrite_circuitbreaker_default_value
140
- stub_circuitbox
141
- env = { url: "url", circuit_breaker_default_value: :sential }
142
- give(circuitbox).circuit("url", anything) { circuit }
143
- give(circuit).run!(anything) { raise Circuitbox::Error }
144
- middleware = FaradayMiddleware.new(app, circuitbox: circuitbox)
145
- assert_equal middleware.call(env), :sential
146
- end
147
-
148
- def test_return_value_closed_circuit
149
- stub_circuitbox
150
- env = { url: "url" }
151
- give(circuit).run!(anything) { :sential }
152
- give(circuitbox).circuit("url", anything) { circuit }
153
- middleware = FaradayMiddleware.new(app, circuitbox: circuitbox)
154
- assert_equal middleware.call(env), :sential
155
- end
156
-
157
- def test_return_null_response_for_open_circuit
158
- stub_circuitbox
159
- env = { url: "url" }
160
- give(circuit).run!(anything) { raise Circuitbox::Error }
161
- give(circuitbox).circuit("url", anything) { circuit }
162
- response = FaradayMiddleware.new(app, circuitbox: circuitbox).call(env)
163
- assert_kind_of Faraday::Response, response
164
- assert_equal response.status, 503
165
- assert response.finished?
166
- refute response.success?
167
- end
168
-
169
- attr_reader :circuitbox, :circuit
170
- def stub_circuitbox
171
- @circuitbox = gimme
172
- @circuit = gimme
173
- end
174
- end
175
- end
@@ -1,56 +0,0 @@
1
- require "integration_helper"
2
- require "tempfile"
3
- require "typhoeus/adapters/faraday"
4
- require "pstore"
5
-
6
- class Circuitbox
7
- class CrossProcessTest < Minitest::Test
8
- include IntegrationHelpers
9
-
10
- attr_reader :connection, :failure_url, :dbfile
11
-
12
- @@only_once = false
13
- def setup
14
- if !@@only_once
15
- @dbfile = Tempfile.open("circuitbox_test_cross_process")
16
- end
17
-
18
- @connection = Faraday.new do |c|
19
- c.use FaradayMiddleware, identifier: "circuitbox_test_cross_process",
20
- circuit_breaker_options: { cache: Moneta.new(:PStore, file: dbfile) }
21
- c.adapter :typhoeus # support in_parallel
22
- end
23
- @failure_url = "http://127.0.0.1:4713"
24
-
25
- if !@@only_once
26
- pid = fork do
27
- Rack::Handler::WEBrick.run(lambda { |env| [500, {}, ["Failure"]] },
28
- Port: 4713,
29
- AccessLog: [],
30
- Logger: WEBrick::Log.new(DEV_NULL))
31
- end
32
- sleep 0.5
33
- Minitest.after_run { Process.kill "KILL", pid }
34
- end
35
- end
36
-
37
- def teardown
38
- Circuitbox.reset
39
- end
40
-
41
- def test_circuit_opens_cross_process
42
- # Open the circuit via a different process
43
- pid = fork do
44
- con = Faraday.new do |c|
45
- c.use FaradayMiddleware, identifier: "circuitbox_test_cross_process",
46
- circuit_breaker_options: { cache: Moneta.new(:PStore, file: dbfile) }
47
- c.adapter :typhoeus
48
- end
49
- open_circuit(con)
50
- end
51
- Process.wait pid
52
- response = connection.get(failure_url)
53
- assert response.original_response.nil?, "opening the circuit from a different process should be respected in the main process"
54
- end
55
- end
56
- end
@@ -1,78 +0,0 @@
1
- require "integration_helper"
2
- require "typhoeus/adapters/faraday"
3
-
4
- class Circuitbox
5
-
6
- class FaradayMiddlewareTest < Minitest::Test
7
- include IntegrationHelpers
8
-
9
- attr_reader :connection, :success_url, :failure_url
10
-
11
- @@only_once = false
12
- def setup
13
- @connection = Faraday.new do |c|
14
- c.use FaradayMiddleware
15
- c.adapter :typhoeus # support in_parallel
16
- end
17
- @success_url = "http://localhost:4711"
18
- @failure_url = "http://localhost:4712"
19
-
20
- if !@@only_once
21
- FakeServer.create(4711, ['200', {'Content-Type' => 'text/plain'}, ["Success!"]])
22
- FakeServer.create(4712, ['500', {'Content-Type' => 'text/plain'}, ["Failure!"]])
23
- end
24
- end
25
-
26
- def teardown
27
- Circuitbox.reset
28
- end
29
-
30
- def test_circuit_does_not_open_for_below_threshhold_failed_requests
31
- 5.times { connection.get(failure_url) }
32
- assert_equal connection.get(success_url).status, 200
33
- end
34
-
35
- def test_failure_circuit_response
36
- failure_response = connection.get(failure_url)
37
- assert_equal failure_response.status, 503
38
- assert_match failure_response.original_response.body, "Failure!"
39
- end
40
-
41
- def test_open_circuit_response
42
- open_circuit
43
- open_circuit_response = connection.get(failure_url)
44
- assert_equal open_circuit_response.status, 503
45
- assert_nil open_circuit_response.original_response
46
- assert_kind_of Circuitbox::OpenCircuitError, open_circuit_response.original_exception
47
- end
48
-
49
- def test_closed_circuit_response
50
- result = connection.get(success_url)
51
- assert result.success?
52
- end
53
-
54
- def test_parallel_requests_closed_circuit_response
55
- response_1, response_2 = nil
56
- connection.in_parallel do
57
- response_1 = connection.get(success_url)
58
- response_2 = connection.get(success_url)
59
- end
60
-
61
- assert response_1.success?
62
- assert response_2.success?
63
- end
64
-
65
- def test_parallel_requests_open_circuit_response
66
- open_circuit
67
- response_1, response_2 = nil
68
- connection.in_parallel do
69
- response_1 = connection.get(failure_url)
70
- response_2 = connection.get(failure_url)
71
- end
72
-
73
- assert_equal response_1.status, 503
74
- assert_equal response_2.status, 503
75
- end
76
-
77
- end
78
- end
@@ -1,48 +0,0 @@
1
- require "test_helper"
2
- require "faraday"
3
- require "circuitbox/faraday_middleware"
4
- require "rack"
5
-
6
- class FakeServer
7
- def self.instance
8
- @@instance ||= FakeServer.new
9
- # if the FakeServer is used kill all of them after the tests are done
10
- Minitest.after_run { FakeServer.shutdown }
11
- @@instance
12
- end
13
-
14
- def initialize
15
- @servers = []
16
- end
17
-
18
- def self.create(port, result)
19
- FakeServer.instance.create(port, result)
20
- end
21
-
22
- def self.shutdown
23
- FakeServer.instance.shutdown
24
- end
25
-
26
- def shutdown
27
- @servers.map { |server| server.exit }
28
- @servers = []
29
- end
30
-
31
- def create(port, result)
32
- @servers << Thread.new do
33
- Rack::Handler::WEBrick.run(Proc.new { |env| result },
34
- Port: port,
35
- AccessLog: [],
36
- Logger: WEBrick::Log.new(DEV_NULL))
37
- end
38
- sleep 0.5 # wait for the server to spin up
39
- end
40
- end
41
-
42
- module IntegrationHelpers
43
- def open_circuit(c = connection)
44
- volume_threshold = Circuitbox::CircuitBreaker::DEFAULTS[:volume_threshold]
45
- (volume_threshold + 1).times { c.get(failure_url) }
46
- end
47
- end
48
-
@@ -1,21 +0,0 @@
1
- require 'test_helper'
2
- require 'circuitbox/notifier'
3
- require 'active_support/notifications'
4
-
5
-
6
- class NotifierTest < Minitest::Test
7
- def test_sends_notification_on_notify
8
- ActiveSupport::Notifications.expects(:instrument).with("circuit_open", circuit: 'yammer:12')
9
- Circuitbox::Notifier.new(:yammer, 12).notify(:open)
10
- end
11
-
12
- def test_sends_warning_notificaiton_notify_warning
13
- ActiveSupport::Notifications.expects(:instrument).with("circuit_warning", { circuit: 'yammer:12', message: 'hello'})
14
- Circuitbox::Notifier.new(:yammer, 12).notify_warning('hello')
15
- end
16
-
17
- def test_sends_metric_as_notification
18
- ActiveSupport::Notifications.expects(:instrument).with("circuit_gauge", { circuit: 'yammer:12', gauge: 'ratio', value: 12})
19
- Circuitbox::Notifier.new(:yammer, 12).metric_gauge(:ratio, 12)
20
- end
21
- end
@@ -1,23 +0,0 @@
1
- require 'test_helper'
2
-
3
- class ServiceFailureErrorTest < Minitest::Test
4
- class SomeOtherError < StandardError; end;
5
-
6
- attr_reader :error
7
-
8
- def setup
9
- raise SomeOtherError, "some other error"
10
- rescue => ex
11
- @error = ex
12
- end
13
-
14
- def test_includes_the_message_of_the_wrapped_exception
15
- ex = Circuitbox::ServiceFailureError.new('test', error)
16
- assert_equal "Circuitbox::ServiceFailureError wrapped: #{error}", ex.to_s
17
- end
18
-
19
- def test_keeps_the_original_backtrace
20
- ex = Circuitbox::ServiceFailureError.new('test', error)
21
- assert_equal error.backtrace, ex.backtrace
22
- end
23
- end
data/test/test_helper.rb DELETED
@@ -1,15 +0,0 @@
1
- require 'minitest/autorun'
2
- require 'mocha/mini_test'
3
- require 'timecop'
4
- require 'gimme'
5
- require 'circuitbox'
6
-
7
- DEV_NULL = (RUBY_PLATFORM =~ /mswin|mingw/ ? "NUL" : "/dev/null")
8
-
9
- class Circuitbox
10
- class CircuitBreaker
11
- def logger
12
- @_dev_null_logger ||= Logger.new(DEV_NULL)
13
- end
14
- end
15
- end