px-service-client 1.2.2 → 1.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +24 -7
- data/lib/px/service/client/caching.rb +5 -7
- data/lib/px/service/client/version.rb +1 -1
- data/spec/px/service/client/base_spec.rb +52 -30
- data/spec/px/service/client/caching/caching_spec.rb +13 -8
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: db46c3146668dad3c40c3a5cabc1feaf4743b745
|
4
|
+
data.tar.gz: 94824bed638685d41404aee9739453fdc2f79280
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 232d9cee11917707c1463cc1d53a9b1336225ef4cb22d5611a3dce074eb6b2feb9ca7e3b5420b69018f0d4acfa847407b37426519482ffd501df2c66875a1a59
|
7
|
+
data.tar.gz: 1d1b736be0ebc4a91957c09832a34ae892e16690d4f4df9dca211496bc6deb5c3d2f8f46e534999373b0ea51821453ce08cac9dd64a9c0e8bec64b8c2a85f340
|
data/README.md
CHANGED
@@ -38,7 +38,7 @@ This gem includes several common features used in 500px service client libraries
|
|
38
38
|
The features are:
|
39
39
|
|
40
40
|
#### Px::Service::Client::Base
|
41
|
-
This class provides a basic `make_request(method, url, ...)` method that produces an asynchronous request. The method immediately returns a `
|
41
|
+
This class provides a basic `make_request(method, url, ...)` method that produces an asynchronous request. The method immediately returns a `Future`. It works together with `Multiplexer`(discussed below) and uses [Typhoeus](https://github.com/typhoeus/typhoeus) as the underlying HTTP client to support asynchronicity.
|
42
42
|
|
43
43
|
**Customized clients usually inherit this class and include other features/mixins, if needed.**
|
44
44
|
|
@@ -55,7 +55,7 @@ multi = Px::Service::Client::Multiplexer.new
|
|
55
55
|
multi.context do
|
56
56
|
method = :get
|
57
57
|
url = 'http://www.example.com'
|
58
|
-
req = make_request(method, url) # returns a
|
58
|
+
req = make_request(method, url) # returns a Future
|
59
59
|
multi.do(req) # queues the request/future into hydra
|
60
60
|
end
|
61
61
|
|
@@ -64,9 +64,9 @@ multi.run # a blocking call, like hydra.run
|
|
64
64
|
```
|
65
65
|
`multi.context` encapsulates the block into a [`Fiber`](http://ruby-doc.org/core-2.2.0/Fiber.html) object and immediately runs (or `resume`, in Fiber's term) that fiber until the block explicitly gives up control. The method returns `multi` itself.
|
66
66
|
|
67
|
-
`multi.do(request_or_future,retries)` queues the request into `hydra`. It always returns a `
|
67
|
+
`multi.do(request_or_future,retries)` queues the request into `hydra`. It always returns a `Future`. A [`Typhoeus::Request`](https://github.com/typhoeus/typhoeus) will be converted into a `Future ` in this call.
|
68
68
|
|
69
|
-
Finally, `multi.run` starts `hydra` to execute the requests in parallel. The request is made as soon as the multiplexer is started. You get the results of the request by evaluating the value of the `
|
69
|
+
Finally, `multi.run` starts `hydra` to execute the requests in parallel. The request is made as soon as the multiplexer is started. You get the results of the request by evaluating the value of the `Future`.
|
70
70
|
|
71
71
|
#### Px::Service::Client::Caching
|
72
72
|
|
@@ -89,12 +89,29 @@ end
|
|
89
89
|
# An example of a cached request
|
90
90
|
result = cache_request(url, :last_resort, refresh_probability: 1) do
|
91
91
|
req = make_request(method, url)
|
92
|
+
response = @multi.do(req)
|
93
|
+
|
94
|
+
# cache_request() expects a future that returns the result to be cached
|
95
|
+
Px::Service::Client::Future.new do
|
96
|
+
JSON.parse(response.body)
|
97
|
+
end
|
92
98
|
end
|
93
99
|
```
|
94
100
|
|
95
|
-
`cache_request` expects a block that
|
101
|
+
`cache_request` expects a block that returns a `Future` object. The return value (usually the response body) of that future will be cached. `cache_request` always returns a future. By evaluating the future, i.e., via the `Future.value!` call, you get the result (whether cached or not).
|
102
|
+
|
103
|
+
|
104
|
+
**Note**: DO NOT cache the `Typhoeus::Response` directly (See the below code snippet), because the response object cannot be serializable to be stored in memcached. That's the reason why we see warning message: `You are trying to cache a Ruby object which cannot be serialized to memcached.`
|
105
|
+
|
106
|
+
```
|
107
|
+
# An incorrect example of using cache_request()
|
108
|
+
cache_request(url, :last_resort) do
|
109
|
+
req = make_request(method, url)
|
110
|
+
response = @multi.do(req) # DO NOT do this
|
111
|
+
end
|
96
112
|
|
97
|
-
|
113
|
+
```
|
114
|
+
Responses are cached in either a *last-resort* or *first-resort* manner.
|
98
115
|
|
99
116
|
*last-resort* means that the cached value is only used when the service client request fails (with a
|
100
117
|
`ServiceError`). If the service client request succeeds, there is a chance that the cache value may get refreshed. The `refresh_probability` is provided to let the cached value
|
@@ -125,7 +142,7 @@ end
|
|
125
142
|
req = make_request(method, url) # overrides Px::Service::Client::Base
|
126
143
|
```
|
127
144
|
|
128
|
-
Adds a circuit breaker to the client. `make_request` always returns `
|
145
|
+
Adds a circuit breaker to the client. `make_request` always returns `Future`
|
129
146
|
|
130
147
|
The circuit will open on any exception from the wrapped method, or if the request runs for longer than the `invocation_timeout`.
|
131
148
|
|
@@ -72,8 +72,7 @@ module Px::Service::Client
|
|
72
72
|
begin
|
73
73
|
raise ArgumentError.new('Block did not return a Future.') unless retry_response.is_a?(Future)
|
74
74
|
resp = retry_response.value!
|
75
|
-
|
76
|
-
entry = CacheEntry.new(config.cache_client, url, policy_group, resp.options)
|
75
|
+
entry = CacheEntry.new(config.cache_client, url, policy_group, resp)
|
77
76
|
|
78
77
|
# Only store a new result if we roll a 0
|
79
78
|
r = rand(refresh_probability)
|
@@ -88,7 +87,7 @@ module Px::Service::Client
|
|
88
87
|
end
|
89
88
|
|
90
89
|
entry.touch(expires_in, refresh_window: 1.minute)
|
91
|
-
|
90
|
+
entry.data
|
92
91
|
end
|
93
92
|
end
|
94
93
|
end
|
@@ -109,7 +108,7 @@ module Px::Service::Client
|
|
109
108
|
# don't also try to update the cache.
|
110
109
|
entry.touch(expires_in)
|
111
110
|
else
|
112
|
-
return Future.new {
|
111
|
+
return Future.new { entry.data }
|
113
112
|
end
|
114
113
|
end
|
115
114
|
|
@@ -119,8 +118,7 @@ module Px::Service::Client
|
|
119
118
|
begin
|
120
119
|
raise ArgumentError.new('Block did not return a Future.') unless retry_response.is_a?(Future)
|
121
120
|
resp = retry_response.value!
|
122
|
-
|
123
|
-
entry = CacheEntry.new(config.cache_client, url, policy_group, resp.options)
|
121
|
+
entry = CacheEntry.new(config.cache_client, url, policy_group, resp)
|
124
122
|
entry.store(expires_in)
|
125
123
|
resp
|
126
124
|
rescue Px::Service::ServiceError => ex
|
@@ -135,7 +133,7 @@ module Px::Service::Client
|
|
135
133
|
# Set the entry to be expired again (but reset the refresh window). This allows the next call to try again
|
136
134
|
# (assuming the circuit breaker is reset) but keeps the value in the cache in the meantime
|
137
135
|
entry.touch(0.seconds)
|
138
|
-
|
136
|
+
entry.data
|
139
137
|
end
|
140
138
|
end
|
141
139
|
|
@@ -67,8 +67,8 @@ describe Px::Service::Client::Base do
|
|
67
67
|
Typhoeus.stub(url).and_return(response)
|
68
68
|
end
|
69
69
|
|
70
|
-
shared_examples_for 'a request that returns a cached response' do
|
71
|
-
let(:cache_entry) { Px::Service::Client::Caching::CacheEntry.new(dalli, url, 'general',
|
70
|
+
shared_examples_for 'a request that returns a cached response body' do
|
71
|
+
let(:cache_entry) { Px::Service::Client::Caching::CacheEntry.new(dalli, url, 'general', response.body, Time.now + 1.year) }
|
72
72
|
|
73
73
|
before :each do
|
74
74
|
Typhoeus::Expectation.clear
|
@@ -80,8 +80,10 @@ describe Px::Service::Client::Base do
|
|
80
80
|
multi.context do
|
81
81
|
resp = multi.do(req)
|
82
82
|
end.run
|
83
|
-
|
84
|
-
|
83
|
+
|
84
|
+
Px::Service::Client::Future.new do
|
85
|
+
resp.options[:body]
|
86
|
+
end
|
85
87
|
end
|
86
88
|
end
|
87
89
|
|
@@ -94,12 +96,14 @@ describe Px::Service::Client::Base do
|
|
94
96
|
multi.context do
|
95
97
|
resp = multi.do(req)
|
96
98
|
end.run
|
97
|
-
|
98
|
-
|
99
|
+
|
100
|
+
Px::Service::Client::Future.new do
|
101
|
+
resp.options[:body]
|
102
|
+
end
|
99
103
|
end
|
100
104
|
end
|
101
105
|
|
102
|
-
it 'returns the cached response' do
|
106
|
+
it 'returns the cached response body' do
|
103
107
|
Typhoeus::Expectation.clear
|
104
108
|
Typhoeus.stub(url).and_return(response)
|
105
109
|
req = subject.send(:make_request, 'get', url)
|
@@ -108,10 +112,12 @@ describe Px::Service::Client::Base do
|
|
108
112
|
|
109
113
|
multi.context do
|
110
114
|
resp = multi.do(req)
|
111
|
-
expect(resp).to eq(cache_entry.data)
|
112
|
-
end
|
115
|
+
expect(resp.options[:body]).to eq(cache_entry.data)
|
116
|
+
end
|
113
117
|
|
114
|
-
|
118
|
+
Px::Service::Client::Future.new do
|
119
|
+
resp.options[:body]
|
120
|
+
end
|
115
121
|
end
|
116
122
|
end
|
117
123
|
end
|
@@ -120,7 +126,7 @@ describe Px::Service::Client::Base do
|
|
120
126
|
let(:strategy) { :first_resort }
|
121
127
|
let(:response) { successful_response }
|
122
128
|
|
123
|
-
it_behaves_like 'a request that returns a cached response'
|
129
|
+
it_behaves_like 'a request that returns a cached response body'
|
124
130
|
|
125
131
|
context 'when the request fails' do
|
126
132
|
let(:response) do
|
@@ -141,8 +147,10 @@ describe Px::Service::Client::Base do
|
|
141
147
|
resp = multi.do(req)
|
142
148
|
called = true
|
143
149
|
end.run
|
144
|
-
|
145
|
-
|
150
|
+
|
151
|
+
Px::Service::Client::Future.new do
|
152
|
+
resp.options[:body]
|
153
|
+
end
|
146
154
|
end
|
147
155
|
|
148
156
|
expect(called).to be_truthy
|
@@ -156,15 +164,17 @@ describe Px::Service::Client::Base do
|
|
156
164
|
multi.context do
|
157
165
|
resp = multi.do(req)
|
158
166
|
end.run
|
159
|
-
|
160
|
-
|
167
|
+
|
168
|
+
Px::Service::Client::Future.new do
|
169
|
+
resp.options[:body]
|
170
|
+
end
|
161
171
|
end.value!
|
162
172
|
}.to raise_error(Px::Service::ServiceError, 'Failed')
|
163
173
|
end
|
164
174
|
end
|
165
175
|
|
166
176
|
context 'when a response has been cached' do
|
167
|
-
it_behaves_like 'a request that returns a cached response'
|
177
|
+
it_behaves_like 'a request that returns a cached response body'
|
168
178
|
end
|
169
179
|
end
|
170
180
|
end
|
@@ -183,8 +193,10 @@ describe Px::Service::Client::Base do
|
|
183
193
|
resp = multi.do(req)
|
184
194
|
called = true
|
185
195
|
end.run
|
186
|
-
|
187
|
-
|
196
|
+
|
197
|
+
Px::Service::Client::Future.new do
|
198
|
+
resp.options[:body]
|
199
|
+
end
|
188
200
|
end
|
189
201
|
|
190
202
|
expect(called).to be_truthy
|
@@ -203,14 +215,16 @@ describe Px::Service::Client::Base do
|
|
203
215
|
called = false
|
204
216
|
req = subject.send(:make_request, 'get', url)
|
205
217
|
|
206
|
-
subject.cache_request(req.request.url, strategy: strategy) do
|
218
|
+
subject.cache_request(req.request.url, strategy: strategy, refresh_probability: 0) do
|
207
219
|
resp = nil
|
208
220
|
multi.context do
|
209
221
|
resp = multi.do(req)
|
210
222
|
called = true
|
211
223
|
end.run
|
212
|
-
|
213
|
-
|
224
|
+
|
225
|
+
Px::Service::Client::Future.new do
|
226
|
+
resp.options[:body]
|
227
|
+
end
|
214
228
|
end
|
215
229
|
|
216
230
|
expect(called).to be_truthy
|
@@ -225,14 +239,16 @@ describe Px::Service::Client::Base do
|
|
225
239
|
multi.context do
|
226
240
|
resp = multi.do(req)
|
227
241
|
end.run
|
228
|
-
|
229
|
-
|
242
|
+
|
243
|
+
Px::Service::Client::Future.new do
|
244
|
+
resp.options[:body]
|
245
|
+
end
|
230
246
|
end.value!
|
231
247
|
}.to raise_error(Px::Service::ServiceError, 'Failed')
|
232
248
|
end
|
233
249
|
end
|
234
250
|
|
235
|
-
context 'when a response has been cached' do
|
251
|
+
context 'when a response body has been cached' do
|
236
252
|
before :each do
|
237
253
|
Typhoeus::Expectation.clear
|
238
254
|
Typhoeus.stub(url).and_return(successful_response)
|
@@ -245,7 +261,9 @@ describe Px::Service::Client::Base do
|
|
245
261
|
resp = multi.do(req)
|
246
262
|
end.run
|
247
263
|
|
248
|
-
|
264
|
+
Px::Service::Client::Future.new do
|
265
|
+
resp.options[:body]
|
266
|
+
end
|
249
267
|
end
|
250
268
|
end
|
251
269
|
|
@@ -259,13 +277,15 @@ describe Px::Service::Client::Base do
|
|
259
277
|
called = true
|
260
278
|
end.run
|
261
279
|
|
262
|
-
|
280
|
+
Px::Service::Client::Future.new do
|
281
|
+
resp.options[:body]
|
282
|
+
end
|
263
283
|
end
|
264
284
|
|
265
285
|
expect(called).to be_truthy
|
266
286
|
end
|
267
287
|
|
268
|
-
it 'returns the cached response' do
|
288
|
+
it 'returns the cached response body' do
|
269
289
|
Typhoeus::Expectation.clear
|
270
290
|
Typhoeus.stub(url).and_return(response)
|
271
291
|
req = subject.send(:make_request, 'get', url)
|
@@ -275,9 +295,11 @@ describe Px::Service::Client::Base do
|
|
275
295
|
multi.context do
|
276
296
|
resp = multi.do(req)
|
277
297
|
end.run
|
278
|
-
|
279
|
-
|
280
|
-
|
298
|
+
|
299
|
+
Px::Service::Client::Future.new do
|
300
|
+
resp.options[:body]
|
301
|
+
end
|
302
|
+
end.value!['status']).to be(200)
|
281
303
|
end
|
282
304
|
end
|
283
305
|
|
@@ -39,7 +39,7 @@ describe Px::Service::Client::Caching do
|
|
39
39
|
it "should call the block" do
|
40
40
|
called = false
|
41
41
|
subject.cache_request(url, strategy: strategy) do
|
42
|
-
called = true
|
42
|
+
Px::Service::Client::Future.new { called = true }
|
43
43
|
end
|
44
44
|
|
45
45
|
expect(called).to be_truthy
|
@@ -131,7 +131,7 @@ describe Px::Service::Client::Caching do
|
|
131
131
|
it "returns the cached response on failure" do
|
132
132
|
expect(subject.cache_request(url, strategy: strategy) do
|
133
133
|
Px::Service::Client::Future.new { raise Px::Service::ServiceError.new("Error", 500) }
|
134
|
-
end.value
|
134
|
+
end.value!).to eq(response.options)
|
135
135
|
end
|
136
136
|
|
137
137
|
it "does not returns the cached response on request error" do
|
@@ -195,8 +195,10 @@ describe Px::Service::Client::Caching do
|
|
195
195
|
|
196
196
|
it "returns the response" do
|
197
197
|
expect(subject.cache_request(url, strategy: strategy) do
|
198
|
-
|
199
|
-
|
198
|
+
Future.new do
|
199
|
+
nil
|
200
|
+
end
|
201
|
+
end.value!).to eq(response.options)
|
200
202
|
end
|
201
203
|
end
|
202
204
|
end
|
@@ -217,7 +219,10 @@ describe Px::Service::Client::Caching do
|
|
217
219
|
it "invokes the block" do
|
218
220
|
called = false
|
219
221
|
subject.cache_request(url, strategy: strategy) do |u|
|
220
|
-
|
222
|
+
Px::Service::Client::Future.new do
|
223
|
+
called = true
|
224
|
+
{ stub: "stub str" }.to_hash
|
225
|
+
end
|
221
226
|
end
|
222
227
|
|
223
228
|
expect(called).to be_truthy
|
@@ -252,7 +257,7 @@ describe Px::Service::Client::Caching do
|
|
252
257
|
end.run
|
253
258
|
|
254
259
|
resp
|
255
|
-
end.value
|
260
|
+
end.value!).to eq(response.options)
|
256
261
|
|
257
262
|
expect(called).to be_falsey
|
258
263
|
|
@@ -272,13 +277,13 @@ describe Px::Service::Client::Caching do
|
|
272
277
|
|
273
278
|
expect(subject.cache_request(url, strategy: strategy) do
|
274
279
|
nil
|
275
|
-
end.value
|
280
|
+
end.value).to eq(response.options)
|
276
281
|
end
|
277
282
|
|
278
283
|
it "returns the cached response on failure" do
|
279
284
|
expect(subject.cache_request(url, strategy: strategy) do
|
280
285
|
Px::Service::Client::Future.new { raise Px::Service::ServiceError.new("Error", 500) }
|
281
|
-
end.value
|
286
|
+
end.value!).to eq(response.options)
|
282
287
|
end
|
283
288
|
|
284
289
|
it "does not returns the cached response on request error" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: px-service-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Micacchi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-07-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: will_paginate
|