circuitbox 1.1.1 → 2.0.0.pre4

Sign up to get free protection for your applications and to get access to all the features.
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