circuitbox 0.9.0 → 0.10.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: 87808cd77975fc82ac067b5c0c82f72f993f6b77
4
- data.tar.gz: 74889c759914d18561ec967cd77edde519bd6d5d
3
+ metadata.gz: c0f2e7b33bd03af9f5b4aa606de11a0a1068b931
4
+ data.tar.gz: 922098a0bf91d9c782c3ac147e94af11f834cf94
5
5
  SHA512:
6
- metadata.gz: 427a52c594b433cc8a506ebe995d56c64b7f1174766c74ce43f476cbf4f848656c2e9c9f6867a8cc31fcadecd92c64a7fcff4db865e7e4cca2c0605e18f6fc0b
7
- data.tar.gz: 6236374797da10131120158b8c2c65f2cff3b4654f3b3ca24a0c85a27f02a6ccb20025babab8c412f6ea82d8457027d600544b9cd857c16fdea2a4636cfb38f8
6
+ metadata.gz: e1fefde3c0c5af41966ff72eee0c71d3c0e5478306b3f744b95eed5ea4e5daeb651ecaf0cf0f7a92eb354571b0af3628c48881e3b29b0f291b8c1a8f1da57adb
7
+ data.tar.gz: 34add156e9af7dd00a3a12be2c6dde1d05e7f8c5ab5bfcb7006022351ddcbccc57586b340bd6d4b8323bb71cfc02022a49e5fef0bf8afbc88df0288edd93822b
data/README.md CHANGED
@@ -196,13 +196,27 @@ conn.get("/api", circuit_breaker_run_options: {})
196
196
  c.use Circuitbox::FaradayMiddleware, circuit_breaker_options: {}
197
197
  ```
198
198
 
199
- ## TODO
200
- * ~~Fix Faraday integration to return a Faraday response object~~
201
- * Split stats into it's own repository
202
- * ~~Circuit Breaker should raise an exception by default instead of returning nil~~
203
- * Refactor to use single state variable
204
- * Fix the partition hack
205
- * Integrate with Breakerbox/Hystrix
199
+ <<<<<<< HEAD
200
+ * `open_circuit` lambda determining what response is considered a failure,
201
+ counting towards the opening of the circuit
202
+
203
+ ```ruby
204
+ c.use Circuitbox::FaradayMiddleware, open_circuit: lambda { |response| response.status >= 500 }
205
+ ```
206
+ ## CHANGELOG
207
+ <<<<<<< HEAD
208
+ ### version next
209
+
210
+ ### v0.10
211
+ - configuration option for faraday middleware for what should be considered to open the circuit [enrico-scalavio](https://github.com/enrico-scalavino)
212
+ - fix for issue 16, support of in_parallel requests in faraday middlware which were opening the circuit.
213
+ - deprecate the __run_option__ `:storage_key`
214
+
215
+ ### v0.9
216
+ - add `run!` method to raise exception on circuit open and service
217
+
218
+ ### v0.8
219
+ - Everything prior to keeping the change log
206
220
 
207
221
  ## Installation
208
222
 
data/circuitbox.gemspec CHANGED
@@ -26,6 +26,7 @@ Gem::Specification.new do |spec|
26
26
  spec.add_development_dependency "gimme"
27
27
  spec.add_development_dependency "minitest"
28
28
  spec.add_development_dependency "mocha"
29
+ spec.add_development_dependency "typhoeus"
29
30
  spec.add_development_dependency "timecop"
30
31
  spec.add_development_dependency "faraday"
31
32
  spec.add_development_dependency "logger"
@@ -43,7 +43,6 @@ class Circuitbox
43
43
 
44
44
  def run!(run_options = {})
45
45
  @partition = run_options.delete(:partition) # sorry for this hack.
46
- cache_key = run_options.delete(:storage_key)
47
46
 
48
47
  if open?
49
48
  logger.debug "[CIRCUIT] open: skipping #{service}"
@@ -62,17 +61,12 @@ class Circuitbox
62
61
  end
63
62
 
64
63
  logger.debug "[CIRCUIT] closed: #{service} querie success"
65
- cache_response(cache_key, response) if cache_key
66
64
  success!
67
65
  rescue *exceptions => exception
68
66
  logger.debug "[CIRCUIT] closed: detected #{service} failure"
69
67
  failure!
70
68
  open! if half_open?
71
- if cache_key
72
- response = get_cached_response(cache_key)
73
- else
74
- raise Circuitbox::ServiceFailureError.new(service, exception)
75
- end
69
+ raise Circuitbox::ServiceFailureError.new(service, exception)
76
70
  end
77
71
  end
78
72
 
@@ -252,14 +246,6 @@ class Circuitbox
252
246
  Digest::SHA1.hexdigest(storage_key(:cache, args.inspect.to_s))
253
247
  end
254
248
 
255
- def cache_response(args, response)
256
- cache.write(response_key(args), response)
257
- end
258
-
259
- def get_cached_response(args)
260
- cache.read(response_key(args))
261
- end
262
-
263
249
  def stat_storage_key(event, options = {})
264
250
  storage_key(:stats, align_time_on_minute, event, options)
265
251
  end
@@ -22,19 +22,22 @@ class Circuitbox
22
22
 
23
23
  def initialize(app, opts = {})
24
24
  @app = app
25
- @opts = opts
25
+ default_options = { open_circuit: lambda { |response| !response.success? } }
26
+ @opts = default_options.merge(opts)
26
27
  super(app)
27
28
  end
28
29
 
29
30
  def call(request_env)
30
31
  service_response = nil
31
- response = circuit(request_env).run(run_options(request_env)) do
32
- service_response = @app.call(request_env)
33
- raise RequestFailed unless service_response.success?
34
- service_response
32
+ check_circuit_open!(request_env)
33
+ circuit(request_env).run!(run_options(request_env)) do
34
+ @app.call(request_env).on_complete do |env|
35
+ service_response = Faraday::Response.new(env)
36
+ raise RequestFailed if open_circuit?(service_response)
37
+ end
35
38
  end
36
-
37
- response.nil? ? circuit_open_value(request_env, service_response) : response
39
+ rescue Circuitbox::Error
40
+ circuit_open_value(request_env, service_response)
38
41
  end
39
42
 
40
43
  def exceptions
@@ -47,6 +50,11 @@ class Circuitbox
47
50
 
48
51
  private
49
52
 
53
+ def check_circuit_open!(env)
54
+ # raises if the circuit is open
55
+ circuit(env).run!(run_options(env)) { :noop }
56
+ end
57
+
50
58
  def run_options(env)
51
59
  env[:circuit_breaker_run_options] || {}
52
60
  end
@@ -75,6 +83,10 @@ class Circuitbox
75
83
  end
76
84
  end
77
85
 
86
+ def open_circuit?(response)
87
+ opts[:open_circuit].call(response)
88
+ end
89
+
78
90
  def circuitbox
79
91
  @circuitbox ||= opts.fetch(:circuitbox, Circuitbox)
80
92
  end
@@ -1,3 +1,3 @@
1
1
  class Circuitbox
2
- VERSION='0.9.0'
2
+ VERSION='0.10.0'
3
3
  end
@@ -7,13 +7,14 @@ class Circuitbox
7
7
  class FaradayMiddlewareTest < Minitest::Test
8
8
 
9
9
  attr_reader :app
10
+
10
11
  def setup
11
12
  @app = gimme
12
13
  end
13
14
 
14
15
  def test_default_identifier
15
16
  env = { url: "sential" }
16
- assert_equal FaradayMiddleware.new(app).identifier.call(env), "sential"
17
+ assert_equal "sential", FaradayMiddleware.new(app).identifier.call(env)
17
18
  end
18
19
 
19
20
  def test_overwrite_identifier
@@ -25,19 +26,21 @@ class Circuitbox
25
26
  stub_circuitbox
26
27
  env = { url: "url" }
27
28
  give(circuitbox).circuit("url", anything) { circuit }
29
+ give(circuit).run!(anything) { raise Circuitbox::Error }
28
30
  default_value_generator = lambda { |_| :sential }
29
31
  middleware = FaradayMiddleware.new(app,
30
32
  circuitbox: circuitbox,
31
33
  default_value: default_value_generator)
32
- assert_equal middleware.call(env), :sential
34
+ assert_equal :sential, middleware.call(env)
33
35
  end
34
36
 
35
37
  def test_overwrite_default_value_generator_static_value
36
38
  stub_circuitbox
37
39
  env = { url: "url" }
38
40
  give(circuitbox).circuit("url", anything) { circuit }
41
+ give(circuit).run!(anything) { raise Circuitbox::Error }
39
42
  middleware = FaradayMiddleware.new(app, circuitbox: circuitbox, default_value: :sential)
40
- assert_equal middleware.call(env), :sential
43
+ assert_equal :sential, middleware.call(env)
41
44
  end
42
45
 
43
46
  def test_default_exceptions
@@ -46,6 +49,29 @@ class Circuitbox
46
49
  assert_includes middleware.exceptions, FaradayMiddleware::RequestFailed
47
50
  end
48
51
 
52
+ def test_overridde_success_response
53
+ env = { url: "url" }
54
+ app = gimme
55
+ give(app).call(anything) { Faraday::Response.new(status: 400) }
56
+ error_response = lambda { |response| response.status >= 500 }
57
+ response = FaradayMiddleware.new(app, open_circuit: error_response).call(env)
58
+ assert_kind_of Faraday::Response, response
59
+ assert_equal response.status, 400
60
+ assert response.finished?
61
+ refute response.success?
62
+ end
63
+
64
+ def test_default_success_response
65
+ env = { url: "url" }
66
+ app = gimme
67
+ give(app).call(anything) { Faraday::Response.new(status: 400) }
68
+ response = FaradayMiddleware.new(app).call(env)
69
+ assert_kind_of Faraday::Response, response
70
+ assert_equal response.status, 503
71
+ assert response.finished?
72
+ refute response.success?
73
+ end
74
+
49
75
  def test_overwrite_exceptions
50
76
  middleware = FaradayMiddleware.new(app, exceptions: [SentialException])
51
77
  assert_includes middleware.exceptions, SentialException
@@ -53,12 +79,12 @@ class Circuitbox
53
79
 
54
80
  def test_pass_circuit_breaker_run_options
55
81
  stub_circuitbox
56
- give(circuit).run(:sential)
82
+ give(circuit).run!(:sential)
57
83
  give(circuitbox).circuit("url", anything) { circuit }
58
84
  env = { url: "url", circuit_breaker_run_options: :sential }
59
85
  middleware = FaradayMiddleware.new(app, circuitbox: circuitbox)
60
86
  middleware.call(env)
61
- verify(circuit).run(:sential)
87
+ verify(circuit, 2.times).run!(:sential) # one to check open, one to execute command
62
88
  end
63
89
 
64
90
  def test_pass_circuit_breaker_options
@@ -74,13 +100,14 @@ class Circuitbox
74
100
  middleware = FaradayMiddleware.new(app, options)
75
101
  middleware.call(env)
76
102
 
77
- verify(circuitbox).circuit("url", expected_circuit_breaker_options)
103
+ verify(circuitbox, 2.times).circuit("url", expected_circuit_breaker_options)
78
104
  end
79
105
 
80
106
  def test_overwrite_circuitbreaker_default_value
81
107
  stub_circuitbox
82
108
  env = { url: "url", circuit_breaker_default_value: :sential }
83
109
  give(circuitbox).circuit("url", anything) { circuit }
110
+ give(circuit).run!(anything) { raise Circuitbox::Error }
84
111
  middleware = FaradayMiddleware.new(app, circuitbox: circuitbox)
85
112
  assert_equal middleware.call(env), :sential
86
113
  end
@@ -88,7 +115,7 @@ class Circuitbox
88
115
  def test_return_value_closed_circuit
89
116
  stub_circuitbox
90
117
  env = { url: "url" }
91
- give(circuit).run(anything) { :sential }
118
+ give(circuit).run!(anything) { :sential }
92
119
  give(circuitbox).circuit("url", anything) { circuit }
93
120
  middleware = FaradayMiddleware.new(app, circuitbox: circuitbox)
94
121
  assert_equal middleware.call(env), :sential
@@ -97,7 +124,7 @@ class Circuitbox
97
124
  def test_return_null_response_for_open_circuit
98
125
  stub_circuitbox
99
126
  env = { url: "url" }
100
- give(circuit).run(anything) { nil }
127
+ give(circuit).run!(anything) { raise Circuitbox::Error }
101
128
  give(circuitbox).circuit("url", anything) { circuit }
102
129
  response = FaradayMiddleware.new(app, circuitbox: circuitbox).call(env)
103
130
  assert_kind_of Faraday::Response, response
@@ -1,4 +1,5 @@
1
1
  require "integration_helper"
2
+ require "typhoeus/adapters/faraday"
2
3
 
3
4
  class Circuitbox
4
5
  class FaradayMiddlewareTest < Minitest::Test
@@ -6,7 +7,7 @@ class Circuitbox
6
7
  def setup
7
8
  @connection = Faraday.new do |c|
8
9
  c.use FaradayMiddleware
9
- c.adapter Faraday.default_adapter
10
+ c.adapter :typhoeus # support in_parallel
10
11
  end
11
12
  @success_url = "http://localhost:4711"
12
13
  @failure_url = "http://localhost:4712"
@@ -21,16 +22,47 @@ class Circuitbox
21
22
  Circuitbox.reset
22
23
  end
23
24
 
25
+ def test_failure_circuit_response
26
+ failure_response = @connection.get(@failure_url)
27
+ assert_equal failure_response.status, 503
28
+ assert_match failure_response.original_response.body, "Failure!"
29
+ end
30
+
24
31
  def test_open_circuit_response
25
32
  10.times { @connection.get(@failure_url) } # make the CircuitBreaker open
33
+
26
34
  open_circuit_response = @connection.get(@failure_url)
27
- assert open_circuit_response.status, 503
28
- assert_match open_circuit_response.original_response.body, "Failure!"
35
+ assert_equal open_circuit_response.status, 503
36
+ assert open_circuit_response.original_response.nil?
29
37
  end
30
38
 
31
39
  def test_closed_circuit_response
32
40
  result = @connection.get(@success_url)
33
41
  assert result.success?
34
42
  end
43
+
44
+ def test_parallel_requests_closed_circuit_response
45
+ response_1, response_2 = nil
46
+ @connection.in_parallel do
47
+ response_1 = @connection.get(@success_url)
48
+ response_2 = @connection.get(@success_url)
49
+ end
50
+
51
+ assert response_1.success?
52
+ assert response_2.success?
53
+ end
54
+
55
+ def test_parallel_requests_open_circuit_response
56
+ 10.times { @connection.get(@failure_url) } # make the CircuitBreaker open
57
+ response_1, response_2 = nil
58
+ @connection.in_parallel do
59
+ response_1 = @connection.get(@failure_url)
60
+ response_2 = @connection.get(@failure_url)
61
+ end
62
+
63
+ assert_equal response_1.status, 503
64
+ assert_equal response_2.status, 503
65
+ end
66
+
35
67
  end
36
68
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: circuitbox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fahim Ferdous
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-16 00:00:00.000000000 Z
11
+ date: 2015-04-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -122,6 +122,20 @@ dependencies:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: typhoeus
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: timecop
127
141
  requirement: !ruby/object:Gem::Requirement
@@ -245,7 +259,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
245
259
  version: '0'
246
260
  requirements: []
247
261
  rubyforge_project:
248
- rubygems_version: 2.4.5
262
+ rubygems_version: 2.4.3
249
263
  signing_key:
250
264
  specification_version: 4
251
265
  summary: A robust circuit breaker that manages failing external services.