dalli 3.1.0 → 3.1.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of dalli might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/History.md +10 -0
- data/lib/dalli/client.rb +155 -120
- data/lib/dalli/options.rb +3 -3
- data/lib/dalli/pipelined_getter.rb +13 -11
- data/lib/dalli/protocol/binary/request_formatter.rb +11 -3
- data/lib/dalli/protocol/binary/response_processor.rb +37 -15
- data/lib/dalli/protocol/binary.rb +183 -349
- data/lib/dalli/protocol/connection_manager.rb +242 -0
- data/lib/dalli/protocol/response_buffer.rb +9 -6
- data/lib/dalli/protocol/server_config_parser.rb +7 -7
- data/lib/dalli/version.rb +1 -1
- data/lib/dalli.rb +2 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 89dd04ff2b4fd6812f2fae613cc984c7d34029a2637ebf5c11d5398361fa7993
|
4
|
+
data.tar.gz: caf5ad6315ddb803ba61418d189c99ce7849f1acced49cf6e32e78e5ca35817f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e425b9704308ce7f3737c7e07dcd2699448ba173fd8fa3ac1e9d3d8867890ce1029dd69f98fb4856bab6c8d81685d34be1c41a192039fccb631c2d4b4749b85a
|
7
|
+
data.tar.gz: 0afdb9aa3fde689a6b84b72aa44aa228e23994f747cce60f8a3471f250a7fa60fdbb3481e4ab4f2665de80be4799b3294f308bb86efa815867e121cd7dc223c9
|
data/History.md
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
Dalli Changelog
|
2
2
|
=====================
|
3
3
|
|
4
|
+
Unreleased
|
5
|
+
==========
|
6
|
+
|
7
|
+
3.1.1
|
8
|
+
==========
|
9
|
+
|
10
|
+
- Add quiet support for incr, decr, append, depend, and flush (petergoldstein)
|
11
|
+
- Additional refactoring to allow reuse of connection behavior (petergoldstein)
|
12
|
+
- Fix issue in flush such that it wasn't passing the delay argument to memcached (petergoldstein)
|
13
|
+
|
4
14
|
3.1.0
|
5
15
|
==========
|
6
16
|
|
data/lib/dalli/client.rb
CHANGED
@@ -49,7 +49,7 @@ module Dalli
|
|
49
49
|
def initialize(servers = nil, options = {})
|
50
50
|
@servers = ::Dalli::ServersArgNormalizer.normalize_servers(servers)
|
51
51
|
@options = normalize_options(options)
|
52
|
-
@key_manager = ::Dalli::KeyManager.new(options)
|
52
|
+
@key_manager = ::Dalli::KeyManager.new(@options)
|
53
53
|
@ring = nil
|
54
54
|
end
|
55
55
|
|
@@ -58,24 +58,39 @@ module Dalli
|
|
58
58
|
#
|
59
59
|
|
60
60
|
##
|
61
|
-
#
|
62
|
-
#
|
63
|
-
|
64
|
-
|
65
|
-
def multi
|
66
|
-
old = Thread.current[::Dalli::MULTI_KEY]
|
67
|
-
Thread.current[::Dalli::MULTI_KEY] = true
|
68
|
-
yield
|
69
|
-
ensure
|
70
|
-
@ring&.pipeline_consume_and_ignore_responses
|
71
|
-
Thread.current[::Dalli::MULTI_KEY] = old
|
61
|
+
# Get the value associated with the key.
|
62
|
+
# If a value is not found, then +nil+ is returned.
|
63
|
+
def get(key, req_options = nil)
|
64
|
+
perform(:get, key, req_options)
|
72
65
|
end
|
73
66
|
|
74
67
|
##
|
75
|
-
#
|
68
|
+
# Gat (get and touch) fetch an item and simultaneously update its expiration time.
|
69
|
+
#
|
76
70
|
# If a value is not found, then +nil+ is returned.
|
77
|
-
def
|
78
|
-
perform(:
|
71
|
+
def gat(key, ttl = nil)
|
72
|
+
perform(:gat, key, ttl_or_default(ttl))
|
73
|
+
end
|
74
|
+
|
75
|
+
##
|
76
|
+
# Touch updates expiration time for a given key.
|
77
|
+
#
|
78
|
+
# Returns true if key exists, otherwise nil.
|
79
|
+
def touch(key, ttl = nil)
|
80
|
+
resp = perform(:touch, key, ttl_or_default(ttl))
|
81
|
+
resp.nil? ? nil : true
|
82
|
+
end
|
83
|
+
|
84
|
+
##
|
85
|
+
# Get the value and CAS ID associated with the key. If a block is provided,
|
86
|
+
# value and CAS will be passed to the block.
|
87
|
+
def get_cas(key)
|
88
|
+
(value, cas) = perform(:cas, key)
|
89
|
+
# TODO: This is odd. Confirm this is working as expected.
|
90
|
+
value = nil if !value || value == 'Not found'
|
91
|
+
return [value, cas] unless block_given?
|
92
|
+
|
93
|
+
yield value, cas
|
79
94
|
end
|
80
95
|
|
81
96
|
##
|
@@ -97,7 +112,21 @@ module Dalli
|
|
97
112
|
end
|
98
113
|
end
|
99
114
|
|
100
|
-
|
115
|
+
##
|
116
|
+
# Fetch multiple keys efficiently, including available metadata such as CAS.
|
117
|
+
# If a block is given, yields key/data pairs one a time. Data is an array:
|
118
|
+
# [value, cas_id]
|
119
|
+
# If no block is given, returns a hash of
|
120
|
+
# { 'key' => [value, cas_id] }
|
121
|
+
def get_multi_cas(*keys)
|
122
|
+
if block_given?
|
123
|
+
pipelined_getter.process(keys) { |*args| yield(*args) }
|
124
|
+
else
|
125
|
+
{}.tap do |hash|
|
126
|
+
pipelined_getter.process(keys) { |k, data| hash[k] = data }
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
101
130
|
|
102
131
|
# Fetch the value associated with the key.
|
103
132
|
# If a value is found, then it is returned.
|
@@ -110,19 +139,11 @@ module Dalli
|
|
110
139
|
def fetch(key, ttl = nil, req_options = nil)
|
111
140
|
req_options = req_options.nil? ? CACHE_NILS : req_options.merge(CACHE_NILS) if cache_nils
|
112
141
|
val = get(key, req_options)
|
113
|
-
|
114
|
-
val = yield
|
115
|
-
add(key, val, ttl_or_default(ttl), req_options)
|
116
|
-
end
|
117
|
-
val
|
118
|
-
end
|
142
|
+
return val unless block_given? && not_found?(val)
|
119
143
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
def cache_nils
|
125
|
-
@options[:cache_nils]
|
144
|
+
new_val = yield
|
145
|
+
add(key, new_val, ttl_or_default(ttl), req_options)
|
146
|
+
new_val
|
126
147
|
end
|
127
148
|
|
128
149
|
##
|
@@ -136,8 +157,8 @@ module Dalli
|
|
136
157
|
# - nil if the key did not exist.
|
137
158
|
# - false if the value was changed by someone else.
|
138
159
|
# - true if the value was successfully updated.
|
139
|
-
def cas(key, ttl = nil,
|
140
|
-
cas_core(key, false, ttl,
|
160
|
+
def cas(key, ttl = nil, req_options = nil, &block)
|
161
|
+
cas_core(key, false, ttl, req_options, &block)
|
141
162
|
end
|
142
163
|
|
143
164
|
##
|
@@ -147,30 +168,78 @@ module Dalli
|
|
147
168
|
# Returns:
|
148
169
|
# - false if the value was changed by someone else.
|
149
170
|
# - true if the value was successfully updated.
|
150
|
-
def cas!(key, ttl = nil,
|
151
|
-
cas_core(key, true, ttl,
|
171
|
+
def cas!(key, ttl = nil, req_options = nil, &block)
|
172
|
+
cas_core(key, true, ttl, req_options, &block)
|
173
|
+
end
|
174
|
+
|
175
|
+
##
|
176
|
+
# Turn on quiet aka noreply support for a number of
|
177
|
+
# memcached operations.
|
178
|
+
#
|
179
|
+
# All relevant operations within this block will be effectively
|
180
|
+
# pipelined as Dalli will use 'quiet' versions. The invoked methods
|
181
|
+
# will all return nil, rather than their usual response. Method
|
182
|
+
# latency will be substantially lower, as the caller will not be
|
183
|
+
# blocking on responses.
|
184
|
+
#
|
185
|
+
# Currently supports storage (set, add, replace, append, prepend),
|
186
|
+
# arithmetic (incr, decr), flush and delete operations. Use of
|
187
|
+
# unsupported operations inside a block will raise an error.
|
188
|
+
#
|
189
|
+
# Any error replies will be discarded at the end of the block, and
|
190
|
+
# Dalli client methods invoked inside the block will not
|
191
|
+
# have return values
|
192
|
+
def quiet
|
193
|
+
old = Thread.current[::Dalli::QUIET]
|
194
|
+
Thread.current[::Dalli::QUIET] = true
|
195
|
+
yield
|
196
|
+
ensure
|
197
|
+
@ring&.pipeline_consume_and_ignore_responses
|
198
|
+
Thread.current[::Dalli::QUIET] = old
|
152
199
|
end
|
200
|
+
alias multi quiet
|
153
201
|
|
154
|
-
def set(key, value, ttl = nil,
|
155
|
-
|
202
|
+
def set(key, value, ttl = nil, req_options = nil)
|
203
|
+
set_cas(key, value, 0, ttl, req_options)
|
204
|
+
end
|
205
|
+
|
206
|
+
##
|
207
|
+
# Set the key-value pair, verifying existing CAS.
|
208
|
+
# Returns the resulting CAS value if succeeded, and falsy otherwise.
|
209
|
+
def set_cas(key, value, cas, ttl = nil, req_options = nil)
|
210
|
+
perform(:set, key, value, ttl_or_default(ttl), cas, req_options)
|
156
211
|
end
|
157
212
|
|
158
213
|
##
|
159
214
|
# Conditionally add a key/value pair, if the key does not already exist
|
160
215
|
# on the server. Returns truthy if the operation succeeded.
|
161
|
-
def add(key, value, ttl = nil,
|
162
|
-
perform(:add, key, value, ttl_or_default(ttl),
|
216
|
+
def add(key, value, ttl = nil, req_options = nil)
|
217
|
+
perform(:add, key, value, ttl_or_default(ttl), req_options)
|
163
218
|
end
|
164
219
|
|
165
220
|
##
|
166
221
|
# Conditionally add a key/value pair, only if the key already exists
|
167
222
|
# on the server. Returns truthy if the operation succeeded.
|
168
|
-
def replace(key, value, ttl = nil,
|
169
|
-
|
223
|
+
def replace(key, value, ttl = nil, req_options = nil)
|
224
|
+
replace_cas(key, value, 0, ttl, req_options)
|
225
|
+
end
|
226
|
+
|
227
|
+
##
|
228
|
+
# Conditionally add a key/value pair, verifying existing CAS, only if the
|
229
|
+
# key already exists on the server. Returns the new CAS value if the
|
230
|
+
# operation succeeded, or falsy otherwise.
|
231
|
+
def replace_cas(key, value, cas, ttl = nil, req_options = nil)
|
232
|
+
perform(:replace, key, value, ttl_or_default(ttl), cas, req_options)
|
233
|
+
end
|
234
|
+
|
235
|
+
# Delete a key/value pair, verifying existing CAS.
|
236
|
+
# Returns true if succeeded, and falsy otherwise.
|
237
|
+
def delete_cas(key, cas = 0)
|
238
|
+
perform(:delete, key, cas)
|
170
239
|
end
|
171
240
|
|
172
241
|
def delete(key)
|
173
|
-
|
242
|
+
delete_cas(key, 0)
|
174
243
|
end
|
175
244
|
|
176
245
|
##
|
@@ -187,13 +256,6 @@ module Dalli
|
|
187
256
|
perform(:prepend, key, value.to_s)
|
188
257
|
end
|
189
258
|
|
190
|
-
def flush(delay = 0)
|
191
|
-
time = -delay
|
192
|
-
ring.servers.map { |s| s.request(:flush, time += delay) }
|
193
|
-
end
|
194
|
-
|
195
|
-
alias flush_all flush
|
196
|
-
|
197
259
|
##
|
198
260
|
# Incr adds the given amount to the counter on the memcached server.
|
199
261
|
# Amt must be a positive integer value.
|
@@ -205,8 +267,10 @@ module Dalli
|
|
205
267
|
# Note that the ttl will only apply if the counter does not already
|
206
268
|
# exist. To increase an existing counter and update its TTL, use
|
207
269
|
# #cas.
|
270
|
+
#
|
271
|
+
# If the value already exists, it must have been set with raw: true
|
208
272
|
def incr(key, amt = 1, ttl = nil, default = nil)
|
209
|
-
|
273
|
+
check_positive!(amt)
|
210
274
|
|
211
275
|
perform(:incr, key, amt.to_i, ttl_or_default(ttl), default)
|
212
276
|
end
|
@@ -225,35 +289,31 @@ module Dalli
|
|
225
289
|
# Note that the ttl will only apply if the counter does not already
|
226
290
|
# exist. To decrease an existing counter and update its TTL, use
|
227
291
|
# #cas.
|
292
|
+
#
|
293
|
+
# If the value already exists, it must have been set with raw: true
|
228
294
|
def decr(key, amt = 1, ttl = nil, default = nil)
|
229
|
-
|
295
|
+
check_positive!(amt)
|
230
296
|
|
231
297
|
perform(:decr, key, amt.to_i, ttl_or_default(ttl), default)
|
232
298
|
end
|
233
299
|
|
234
300
|
##
|
235
|
-
#
|
236
|
-
#
|
237
|
-
# Returns true if key exists, otherwise nil.
|
238
|
-
def touch(key, ttl = nil)
|
239
|
-
resp = perform(:touch, key, ttl_or_default(ttl))
|
240
|
-
resp.nil? ? nil : true
|
241
|
-
end
|
242
|
-
|
301
|
+
# Flush the memcached server, at 'delay' seconds in the future.
|
302
|
+
# Delay defaults to zero seconds, which means an immediate flush.
|
243
303
|
##
|
244
|
-
|
245
|
-
|
246
|
-
# If a value is not found, then +nil+ is returned.
|
247
|
-
def gat(key, ttl = nil)
|
248
|
-
perform(:gat, key, ttl_or_default(ttl))
|
304
|
+
def flush(delay = 0)
|
305
|
+
ring.servers.map { |s| s.request(:flush, delay) }
|
249
306
|
end
|
307
|
+
alias flush_all flush
|
308
|
+
|
309
|
+
ALLOWED_STAT_KEYS = %i[items slabs settings].freeze
|
250
310
|
|
251
311
|
##
|
252
312
|
# Collect the stats for each server.
|
253
313
|
# You can optionally pass a type including :items, :slabs or :settings to get specific stats
|
254
314
|
# Returns a hash like { 'hostname:port' => { 'stat1' => 'value1', ... }, 'hostname2:port' => { ... } }
|
255
315
|
def stats(type = nil)
|
256
|
-
type = nil unless
|
316
|
+
type = nil unless ALLOWED_STAT_KEYS.include? type
|
257
317
|
values = {}
|
258
318
|
ring.servers.each do |server|
|
259
319
|
values[server.name.to_s] = server.alive? ? server.request(:stats, type.to_s) : nil
|
@@ -269,12 +329,6 @@ module Dalli
|
|
269
329
|
end
|
270
330
|
end
|
271
331
|
|
272
|
-
##
|
273
|
-
## Make sure memcache servers are alive, or raise an Dalli::RingError
|
274
|
-
def alive!
|
275
|
-
ring.server_for_key('')
|
276
|
-
end
|
277
|
-
|
278
332
|
##
|
279
333
|
## Version of the memcache servers.
|
280
334
|
def version
|
@@ -286,55 +340,9 @@ module Dalli
|
|
286
340
|
end
|
287
341
|
|
288
342
|
##
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
(value, cas) = perform(:cas, key)
|
293
|
-
value = nil if !value || value == 'Not found'
|
294
|
-
if block_given?
|
295
|
-
yield value, cas
|
296
|
-
else
|
297
|
-
[value, cas]
|
298
|
-
end
|
299
|
-
end
|
300
|
-
|
301
|
-
##
|
302
|
-
# Fetch multiple keys efficiently, including available metadata such as CAS.
|
303
|
-
# If a block is given, yields key/data pairs one a time. Data is an array:
|
304
|
-
# [value, cas_id]
|
305
|
-
# If no block is given, returns a hash of
|
306
|
-
# { 'key' => [value, cas_id] }
|
307
|
-
def get_multi_cas(*keys)
|
308
|
-
if block_given?
|
309
|
-
pipelined_getter.process(keys) { |*args| yield(*args) }
|
310
|
-
else
|
311
|
-
{}.tap do |hash|
|
312
|
-
pipelined_getter.process(keys) { |k, data| hash[k] = data }
|
313
|
-
end
|
314
|
-
end
|
315
|
-
end
|
316
|
-
|
317
|
-
##
|
318
|
-
# Set the key-value pair, verifying existing CAS.
|
319
|
-
# Returns the resulting CAS value if succeeded, and falsy otherwise.
|
320
|
-
def set_cas(key, value, cas, ttl = nil, options = nil)
|
321
|
-
ttl ||= @options[:expires_in].to_i
|
322
|
-
perform(:set, key, value, ttl, cas, options)
|
323
|
-
end
|
324
|
-
|
325
|
-
##
|
326
|
-
# Conditionally add a key/value pair, verifying existing CAS, only if the
|
327
|
-
# key already exists on the server. Returns the new CAS value if the
|
328
|
-
# operation succeeded, or falsy otherwise.
|
329
|
-
def replace_cas(key, value, cas, ttl = nil, options = nil)
|
330
|
-
ttl ||= @options[:expires_in].to_i
|
331
|
-
perform(:replace, key, value, ttl, cas, options)
|
332
|
-
end
|
333
|
-
|
334
|
-
# Delete a key/value pair, verifying existing CAS.
|
335
|
-
# Returns true if succeeded, and falsy otherwise.
|
336
|
-
def delete_cas(key, cas = 0)
|
337
|
-
perform(:delete, key, cas)
|
343
|
+
## Make sure memcache servers are alive, or raise an Dalli::RingError
|
344
|
+
def alive!
|
345
|
+
ring.server_for_key('')
|
338
346
|
end
|
339
347
|
|
340
348
|
##
|
@@ -346,6 +354,16 @@ module Dalli
|
|
346
354
|
end
|
347
355
|
alias reset close
|
348
356
|
|
357
|
+
CACHE_NILS = { cache_nils: true }.freeze
|
358
|
+
|
359
|
+
def not_found?(val)
|
360
|
+
cache_nils ? val == ::Dalli::NOT_FOUND : val.nil?
|
361
|
+
end
|
362
|
+
|
363
|
+
def cache_nils
|
364
|
+
@options[:cache_nils]
|
365
|
+
end
|
366
|
+
|
349
367
|
# Stub method so a bare Dalli client can pretend to be a connection pool.
|
350
368
|
def with
|
351
369
|
yield self
|
@@ -353,15 +371,23 @@ module Dalli
|
|
353
371
|
|
354
372
|
private
|
355
373
|
|
356
|
-
def
|
374
|
+
def check_positive!(amt)
|
375
|
+
raise ArgumentError, "Positive values only: #{amt}" if amt.negative?
|
376
|
+
end
|
377
|
+
|
378
|
+
def cas_core(key, always_set, ttl = nil, req_options = nil)
|
357
379
|
(value, cas) = perform(:cas, key)
|
358
380
|
value = nil if !value || value == 'Not found'
|
359
381
|
return if value.nil? && !always_set
|
360
382
|
|
361
383
|
newvalue = yield(value)
|
362
|
-
perform(:set, key, newvalue, ttl_or_default(ttl), cas,
|
384
|
+
perform(:set, key, newvalue, ttl_or_default(ttl), cas, req_options)
|
363
385
|
end
|
364
386
|
|
387
|
+
##
|
388
|
+
# Uses the argument TTL or the client-wide default. Ensures
|
389
|
+
# that the value is an integer
|
390
|
+
##
|
365
391
|
def ttl_or_default(ttl)
|
366
392
|
(ttl || @options[:expires_in]).to_i
|
367
393
|
rescue NoMethodError
|
@@ -382,7 +408,16 @@ module Dalli
|
|
382
408
|
@protocol_implementation ||= @options.fetch(:protocol_implementation, Dalli::Protocol::Binary)
|
383
409
|
end
|
384
410
|
|
385
|
-
|
411
|
+
##
|
412
|
+
# Chokepoint method for memcached methods with a key argument.
|
413
|
+
# Validates the key, resolves the key to the appropriate server
|
414
|
+
# instance, and invokes the memcached method on the appropriate
|
415
|
+
# server.
|
416
|
+
#
|
417
|
+
# This method also forces retries on network errors - when
|
418
|
+
# a particular memcached instance becomes unreachable, or the
|
419
|
+
# operational times out.
|
420
|
+
##
|
386
421
|
def perform(*all_args)
|
387
422
|
return yield if block_given?
|
388
423
|
|
data/lib/dalli/options.rb
CHANGED
@@ -31,19 +31,19 @@ module Dalli
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
def
|
34
|
+
def pipeline_response_setup
|
35
35
|
@lock.synchronize do
|
36
36
|
super
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
def
|
40
|
+
def pipeline_next_responses
|
41
41
|
@lock.synchronize do
|
42
42
|
super
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
def
|
46
|
+
def pipeline_abort
|
47
47
|
@lock.synchronize do
|
48
48
|
super
|
49
49
|
end
|
@@ -19,13 +19,7 @@ module Dalli
|
|
19
19
|
@ring.lock do
|
20
20
|
servers = setup_requests(keys)
|
21
21
|
start_time = Time.now
|
22
|
-
|
23
|
-
# Remove any servers which are not connected
|
24
|
-
servers.delete_if { |s| !s.connected? }
|
25
|
-
break if servers.empty?
|
26
|
-
|
27
|
-
servers = fetch_responses(servers, start_time, @ring.socket_timeout, &block)
|
28
|
-
end
|
22
|
+
servers = fetch_responses(servers, start_time, @ring.socket_timeout, &block) until servers.empty?
|
29
23
|
end
|
30
24
|
rescue NetworkError => e
|
31
25
|
Dalli.logger.debug { e.inspect }
|
@@ -44,6 +38,10 @@ module Dalli
|
|
44
38
|
##
|
45
39
|
# Loop through the server-grouped sets of keys, writing
|
46
40
|
# the corresponding getkq requests to the appropriate servers
|
41
|
+
#
|
42
|
+
# It's worth noting that we could potentially reduce bytes
|
43
|
+
# on the wire by switching from getkq to getq, and using
|
44
|
+
# the opaque value to match requests to responses.
|
47
45
|
##
|
48
46
|
def make_getkq_requests(groups)
|
49
47
|
groups.each do |server, keys_for_server|
|
@@ -80,7 +78,7 @@ module Dalli
|
|
80
78
|
end
|
81
79
|
|
82
80
|
def finish_query_for_server(server)
|
83
|
-
server.
|
81
|
+
server.pipeline_response_setup
|
84
82
|
rescue Dalli::NetworkError
|
85
83
|
raise
|
86
84
|
rescue Dalli::DalliError => e
|
@@ -91,10 +89,14 @@ module Dalli
|
|
91
89
|
|
92
90
|
# Swallows Dalli::NetworkError
|
93
91
|
def abort_without_timeout(servers)
|
94
|
-
servers.each(&:
|
92
|
+
servers.each(&:pipeline_abort)
|
95
93
|
end
|
96
94
|
|
97
95
|
def fetch_responses(servers, start_time, timeout, &block)
|
96
|
+
# Remove any servers which are not connected
|
97
|
+
servers.delete_if { |s| !s.connected? }
|
98
|
+
return [] if servers.empty?
|
99
|
+
|
98
100
|
time_left = remaining_time(start_time, timeout)
|
99
101
|
readable_servers = servers_with_response(servers, time_left)
|
100
102
|
if readable_servers.empty?
|
@@ -135,11 +137,11 @@ module Dalli
|
|
135
137
|
# Processes responses from a server. Returns true if there are no
|
136
138
|
# additional responses from this server.
|
137
139
|
def process_server(server)
|
138
|
-
server.
|
140
|
+
server.pipeline_next_responses.each_pair do |key, value_list|
|
139
141
|
yield @key_manager.key_without_namespace(key), value_list
|
140
142
|
end
|
141
143
|
|
142
|
-
server.
|
144
|
+
server.pipeline_complete?
|
143
145
|
end
|
144
146
|
|
145
147
|
def servers_with_response(servers, timeout)
|
@@ -31,11 +31,14 @@ module Dalli
|
|
31
31
|
deleteq: 0x14,
|
32
32
|
incrq: 0x15,
|
33
33
|
decrq: 0x16,
|
34
|
+
flushq: 0x18,
|
35
|
+
appendq: 0x19,
|
36
|
+
prependq: 0x1A,
|
37
|
+
touch: 0x1C,
|
38
|
+
gat: 0x1D,
|
34
39
|
auth_negotiation: 0x20,
|
35
40
|
auth_request: 0x21,
|
36
|
-
auth_continue: 0x22
|
37
|
-
touch: 0x1C,
|
38
|
-
gat: 0x1D
|
41
|
+
auth_continue: 0x22
|
39
42
|
}.freeze
|
40
43
|
|
41
44
|
REQ_HEADER_FORMAT = 'CCnCCnNNQ'
|
@@ -56,6 +59,8 @@ module Dalli
|
|
56
59
|
|
57
60
|
append: KEY_AND_VALUE,
|
58
61
|
prepend: KEY_AND_VALUE,
|
62
|
+
appendq: KEY_AND_VALUE,
|
63
|
+
prependq: KEY_AND_VALUE,
|
59
64
|
auth_request: KEY_AND_VALUE,
|
60
65
|
auth_continue: KEY_AND_VALUE,
|
61
66
|
|
@@ -68,8 +73,11 @@ module Dalli
|
|
68
73
|
|
69
74
|
incr: INCR_DECR,
|
70
75
|
decr: INCR_DECR,
|
76
|
+
incrq: INCR_DECR,
|
77
|
+
decrq: INCR_DECR,
|
71
78
|
|
72
79
|
flush: TTL_ONLY,
|
80
|
+
flushq: TTL_ONLY,
|
73
81
|
|
74
82
|
noop: NO_BODY,
|
75
83
|
auth_negotiation: NO_BODY,
|
@@ -58,7 +58,7 @@ module Dalli
|
|
58
58
|
read(ResponseHeader::SIZE) || raise(Dalli::NetworkError, 'No response')
|
59
59
|
end
|
60
60
|
|
61
|
-
def
|
61
|
+
def raise_on_not_ok!(resp_header)
|
62
62
|
return if resp_header.ok?
|
63
63
|
|
64
64
|
raise Dalli::DalliError, "Response error #{resp_header.status}: #{RESPONSE_CODES[resp_header.status]}"
|
@@ -67,24 +67,45 @@ module Dalli
|
|
67
67
|
def generic_response(unpack: false, cache_nils: false)
|
68
68
|
resp_header, body = read_response
|
69
69
|
|
70
|
-
return cache_nils ? ::Dalli::NOT_FOUND : nil if resp_header.not_found?
|
71
70
|
return false if resp_header.not_stored? # Not stored, normal status for add operation
|
71
|
+
return cache_nils ? ::Dalli::NOT_FOUND : nil if resp_header.not_found?
|
72
72
|
|
73
|
-
|
73
|
+
raise_on_not_ok!(resp_header)
|
74
74
|
return true unless body
|
75
75
|
|
76
76
|
unpack_response_body(resp_header.extra_len, resp_header.key_len, body, unpack).last
|
77
77
|
end
|
78
78
|
|
79
|
-
|
79
|
+
##
|
80
|
+
# Response for a storage operation. Returns the cas on success. False
|
81
|
+
# if the value wasn't stored. And raises an error on all other error
|
82
|
+
# codes from memcached.
|
83
|
+
##
|
84
|
+
def storage_response
|
85
|
+
resp_header, = read_response
|
86
|
+
return false if resp_header.not_stored? # Not stored, normal status for add operation
|
87
|
+
|
88
|
+
raise_on_not_ok!(resp_header)
|
89
|
+
resp_header.cas
|
90
|
+
end
|
91
|
+
|
92
|
+
def no_body_response
|
93
|
+
resp_header, = read_response
|
94
|
+
return false if resp_header.not_stored? # Not stored, possible status for append/prepend
|
95
|
+
|
96
|
+
raise_on_not_ok!(resp_header)
|
97
|
+
true
|
98
|
+
end
|
99
|
+
|
100
|
+
def data_cas_response(unpack: true)
|
80
101
|
resp_header, body = read_response
|
81
102
|
return [nil, resp_header.cas] if resp_header.not_found?
|
82
103
|
return [nil, false] if resp_header.not_stored?
|
83
104
|
|
84
|
-
|
105
|
+
raise_on_not_ok!(resp_header)
|
85
106
|
return [nil, resp_header.cas] unless body
|
86
107
|
|
87
|
-
[unpack_response_body(resp_header.extra_len, resp_header.key_len, body,
|
108
|
+
[unpack_response_body(resp_header.extra_len, resp_header.key_len, body, unpack).last, resp_header.cas]
|
88
109
|
end
|
89
110
|
|
90
111
|
def cas_response
|
@@ -146,31 +167,32 @@ module Dalli
|
|
146
167
|
# complete response is found in the buffer, this will
|
147
168
|
# be the response size. Otherwise it is zero.
|
148
169
|
#
|
149
|
-
# The remaining
|
150
|
-
#
|
170
|
+
# The remaining three values in the array are the ResponseHeader,
|
171
|
+
# key, and value.
|
151
172
|
##
|
152
173
|
def getk_response_from_buffer(buf)
|
153
174
|
# There's no header in the buffer, so don't advance
|
154
|
-
return [0,
|
175
|
+
return [0, nil, nil, nil] unless contains_header?(buf)
|
155
176
|
|
156
177
|
resp_header = response_header_from_buffer(buf)
|
157
178
|
body_len = resp_header.body_len
|
158
179
|
|
159
|
-
#
|
160
|
-
#
|
180
|
+
# We have a complete response that has no body.
|
181
|
+
# This is either the response to the terminating
|
161
182
|
# noop or, if the status is not zero, an intermediate
|
162
183
|
# error response that needs to be discarded.
|
163
|
-
return [ResponseHeader::SIZE, resp_header
|
184
|
+
return [ResponseHeader::SIZE, resp_header, nil, nil] if body_len.zero?
|
164
185
|
|
165
|
-
# The header is in the buffer, but the body is not
|
166
186
|
resp_size = ResponseHeader::SIZE + body_len
|
167
|
-
|
187
|
+
# The header is in the buffer, but the body is not. As we don't have
|
188
|
+
# a complete response, don't advance the buffer
|
189
|
+
return [0, nil, nil, nil] unless buf.bytesize >= resp_size
|
168
190
|
|
169
191
|
# The full response is in our buffer, so parse it and return
|
170
192
|
# the values
|
171
193
|
body = buf.slice(ResponseHeader::SIZE, body_len)
|
172
194
|
key, value = unpack_response_body(resp_header.extra_len, resp_header.key_len, body, true)
|
173
|
-
[resp_size, resp_header
|
195
|
+
[resp_size, resp_header, key, value]
|
174
196
|
end
|
175
197
|
end
|
176
198
|
end
|