routemaster-drain 3.1.0 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +7 -1
- data/README.md +12 -0
- data/lib/routemaster/api_client.rb +9 -7
- data/lib/routemaster/api_client_circuit.rb +43 -0
- data/lib/routemaster/drain.rb +1 -1
- data/routemaster-drain.gemspec +1 -0
- data/spec/routemaster/api_client_circuit_spec.rb +63 -0
- metadata +20 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1ddda71d2b877bc368adcfb3d6f97d6c79916b8b2d72db9036d4bdf34ae7f5d8
|
4
|
+
data.tar.gz: 2c3371a3ed8b1df43f743bbc3d9e715fc8ea63ea796042b922f564c13da45467
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a4034317d864f77c4b111f0837673fc3bc9b7f1cb3f93fa79aeff4170b2d80dcf9f6a3cc58072a5a6819791013612a04c59a34baee67b2380ac0a36dd66a8c52
|
7
|
+
data.tar.gz: 51b7c394000cbe0efe657f53ea00b7d4d99063675796a78e8ee3f72e8708e18256311c93792bcd98875a022ac80bc29c20f6eaa034ae178ad6f6ef3196093a38
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -329,6 +329,18 @@ response.user.show(1)
|
|
329
329
|
#=> HateoasResponse
|
330
330
|
```
|
331
331
|
|
332
|
+
### Circuit Breaker
|
333
|
+
|
334
|
+
The client ships with a circuit breaker for get requests, this can be enabled by setting `ROUTEMASTER_ENABLE_API_CLIENT_CIRCUIT`. The Circuit Breaker is powered by [Circuitbox](https://github.com/yammer/circuitbox).
|
335
|
+
|
336
|
+
The following parameters can be used:
|
337
|
+
|
338
|
+
- `ROUTEMASTER_CIRCUIT_BREAKER_SLEEP_WINDOW` - The time in seconds the circuit will remain open once it's triggered
|
339
|
+
- `ROUTEMASTER_CIRCUIT_BREAKER_TIME_WINDOW` - The time in seconds over which it calculates the error rate
|
340
|
+
- `ROUTEMASTER_CIRCUIT_BREAKER_VOLUME_THRESHOLD` - The minimum amount of requests that can trigger the circuit to open
|
341
|
+
- `ROUTEMASTER_CIRCUIT_BREAKER_ERROR_THRESHOLD` - The % of errors required for the circuit to open in the sleep window
|
342
|
+
|
343
|
+
Each of these settings can be controlled on a per domain basis. To set `ROUTEMASTER_CIRCUIT_BREAKER_SLEEP_WINDOW` for `example.com` set `example.com.ROUTEMASTER_CIRCUIT_BREAKER_SLEEP_WINDOW`
|
332
344
|
|
333
345
|
|
334
346
|
## Internals
|
@@ -8,6 +8,7 @@ require 'routemaster/middleware/response_caching'
|
|
8
8
|
require 'routemaster/middleware/error_handling'
|
9
9
|
require 'routemaster/middleware/metrics'
|
10
10
|
require 'routemaster/responses/response_promise'
|
11
|
+
require 'routemaster/api_client_circuit'
|
11
12
|
|
12
13
|
# This is not a direct dependency, we need to load it early to prevent a
|
13
14
|
# circular dependency in hateoas_response.rb
|
@@ -55,13 +56,14 @@ module Routemaster
|
|
55
56
|
def get(url, params: {}, headers: {}, options: {})
|
56
57
|
enable_caching = options.fetch(:enable_caching, true)
|
57
58
|
response_class = options[:response_class]
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
59
|
+
APIClientCircuit.new(url).call do
|
60
|
+
_wrapped_response _request(
|
61
|
+
:get,
|
62
|
+
url: url,
|
63
|
+
params: params,
|
64
|
+
headers: headers.merge(response_cache_opt_headers(enable_caching))),
|
65
|
+
response_class: response_class
|
66
|
+
end
|
65
67
|
end
|
66
68
|
|
67
69
|
# Same as {{get}}, except with
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'circuitbox'
|
2
|
+
require 'routemaster/errors'
|
3
|
+
module Routemaster
|
4
|
+
class APIClientCircuit
|
5
|
+
def initialize(url)
|
6
|
+
url = URI.parse(url) unless url.is_a? URI
|
7
|
+
@circuit_name = url.host.downcase
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(&block)
|
11
|
+
if enabled?
|
12
|
+
begin
|
13
|
+
return circuit.run!(&block)
|
14
|
+
rescue Circuitbox::ServiceFailureError => e
|
15
|
+
raise e.original
|
16
|
+
end
|
17
|
+
else
|
18
|
+
return block.call
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def enabled?
|
25
|
+
ENV.fetch('ROUTEMASTER_ENABLE_API_CLIENT_CIRCUIT', 'NO') =~ /\A(YES|TRUE|ON|1)\Z/i
|
26
|
+
end
|
27
|
+
|
28
|
+
def circuit
|
29
|
+
Circuitbox.circuit(@circuit_name, {
|
30
|
+
sleep_window: configuration_setting(@circuit_name, 'ROUTEMASTER_CIRCUIT_BREAKER_SLEEP_WINDOW', 60).to_i,
|
31
|
+
time_window: configuration_setting(@circuit_name, 'ROUTEMASTER_CIRCUIT_BREAKER_TIME_WINDOW', 120).to_i,
|
32
|
+
volume_threshold: configuration_setting(@circuit_name, 'ROUTEMASTER_CIRCUIT_BREAKER_VOLUME_THRESHOLD', 50).to_i,
|
33
|
+
error_threshold: configuration_setting(@circuit_name, 'ROUTEMASTER_CIRCUIT_BREAKER_ERROR_THRESHOLD', 50).to_i,
|
34
|
+
cache: Moneta.new(:Redis, backend: Config.cache_redis),
|
35
|
+
exceptions: [Routemaster::Errors::FatalResource, Faraday::TimeoutError]
|
36
|
+
})
|
37
|
+
end
|
38
|
+
|
39
|
+
def configuration_setting(circuit_name, setting_name, default)
|
40
|
+
ENV.fetch("#{circuit_name}.#{setting_name}", ENV.fetch(setting_name, default))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/routemaster/drain.rb
CHANGED
data/routemaster-drain.gemspec
CHANGED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'spec/support/uses_dotenv'
|
3
|
+
require 'spec/support/uses_redis'
|
4
|
+
require 'spec/support/uses_webmock'
|
5
|
+
require 'routemaster/api_client'
|
6
|
+
require 'routemaster/api_client_circuit'
|
7
|
+
|
8
|
+
describe Routemaster::APIClientCircuit do
|
9
|
+
uses_webmock
|
10
|
+
uses_redis
|
11
|
+
|
12
|
+
context "when enabled" do
|
13
|
+
before do
|
14
|
+
sb_req
|
15
|
+
allow_any_instance_of(described_class).to receive(:enabled?){ true }
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:url){ 'http://example.com/foobar' }
|
19
|
+
|
20
|
+
let(:sb_req){
|
21
|
+
stub_request(:get, url).to_return(
|
22
|
+
status: status,
|
23
|
+
body: { id: 132, type: 'widget' }.to_json,
|
24
|
+
headers: {
|
25
|
+
'content-type' => 'application/json;v=1'
|
26
|
+
}
|
27
|
+
)
|
28
|
+
}
|
29
|
+
|
30
|
+
def perform
|
31
|
+
Routemaster::APIClient.new.get(url)
|
32
|
+
end
|
33
|
+
|
34
|
+
context "when not erroring" do
|
35
|
+
let(:status) { 200 }
|
36
|
+
|
37
|
+
it "should pass through a response" do
|
38
|
+
expect(perform.status).to eq 200
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "when erroring" do
|
43
|
+
let(:status){ 500 }
|
44
|
+
|
45
|
+
it "should pass through a single error" do
|
46
|
+
expect{ perform }.to raise_error Routemaster::Errors::FatalResource
|
47
|
+
expect(sb_req).to have_been_requested
|
48
|
+
end
|
49
|
+
|
50
|
+
context "after lots of errors" do
|
51
|
+
before do
|
52
|
+
60.times do
|
53
|
+
perform rescue Routemaster::Errors::FatalResource
|
54
|
+
end
|
55
|
+
end
|
56
|
+
it "should limit the amount of requests" do
|
57
|
+
expect(a_request(:get, url)).to have_been_made.at_least_times(49)
|
58
|
+
expect(a_request(:get, url)).to have_been_made.at_most_times(51)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: routemaster-drain
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julien Letessier
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-11-
|
11
|
+
date: 2017-11-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -136,6 +136,20 @@ dependencies:
|
|
136
136
|
- - ">="
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: circuitbox
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
139
153
|
description:
|
140
154
|
email:
|
141
155
|
- julien.letessier@gmail.com
|
@@ -167,6 +181,7 @@ files:
|
|
167
181
|
- gemfiles/rails_5.gemfile
|
168
182
|
- lib/core_ext/forwardable.rb
|
169
183
|
- lib/routemaster/api_client.rb
|
184
|
+
- lib/routemaster/api_client_circuit.rb
|
170
185
|
- lib/routemaster/cache.rb
|
171
186
|
- lib/routemaster/cache_key.rb
|
172
187
|
- lib/routemaster/config.rb
|
@@ -209,6 +224,7 @@ files:
|
|
209
224
|
- lib/routemaster/tasks/fix_cache_ttl.rb
|
210
225
|
- routemaster-drain.gemspec
|
211
226
|
- spec/core_ext/forwardable_spec.rb
|
227
|
+
- spec/routemaster/api_client_circuit_spec.rb
|
212
228
|
- spec/routemaster/api_client_spec.rb
|
213
229
|
- spec/routemaster/cache_spec.rb
|
214
230
|
- spec/routemaster/config_spec.rb
|
@@ -270,12 +286,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
270
286
|
version: '0'
|
271
287
|
requirements: []
|
272
288
|
rubyforge_project:
|
273
|
-
rubygems_version: 2.
|
289
|
+
rubygems_version: 2.7.0
|
274
290
|
signing_key:
|
275
291
|
specification_version: 4
|
276
292
|
summary: Event receiver for the Routemaster bus
|
277
293
|
test_files:
|
278
294
|
- spec/core_ext/forwardable_spec.rb
|
295
|
+
- spec/routemaster/api_client_circuit_spec.rb
|
279
296
|
- spec/routemaster/api_client_spec.rb
|
280
297
|
- spec/routemaster/cache_spec.rb
|
281
298
|
- spec/routemaster/config_spec.rb
|