dalli 2.7.4 → 2.7.5
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/Gemfile +0 -7
- data/History.md +15 -0
- data/LICENSE +1 -1
- data/README.md +14 -6
- data/lib/active_support/cache/dalli_store.rb +39 -9
- data/lib/dalli/client.rb +40 -27
- data/lib/dalli/server.rb +33 -18
- data/lib/dalli/socket.rb +14 -4
- data/lib/dalli/version.rb +1 -1
- metadata +79 -43
- data/Performance.md +0 -42
- data/Rakefile +0 -43
- data/dalli.gemspec +0 -29
- data/test/benchmark_test.rb +0 -243
- data/test/helper.rb +0 -56
- data/test/memcached_mock.rb +0 -201
- data/test/sasl/memcached.conf +0 -1
- data/test/sasl/sasldb +0 -1
- data/test/test_active_support.rb +0 -541
- data/test/test_cas_client.rb +0 -107
- data/test/test_compressor.rb +0 -52
- data/test/test_dalli.rb +0 -682
- data/test/test_encoding.rb +0 -32
- data/test/test_failover.rb +0 -137
- data/test/test_network.rb +0 -64
- data/test/test_rack_session.rb +0 -341
- data/test/test_ring.rb +0 -85
- data/test/test_sasl.rb +0 -105
- data/test/test_serializer.rb +0 -29
- data/test/test_server.rb +0 -110
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 29b86664473df52d0ca07fbf21015a39260962a1
|
4
|
+
data.tar.gz: 5061af271bef6e1db864d5e6de4f9464412e0c67
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7ccec720347f06a142f74d1a500948390f0dcd3520c73922363267c393d99067cb3682f4c2577cc2cbbd99a18a93f800041eaec7570303c73d5e7ab451b9396e
|
7
|
+
data.tar.gz: a68483e89e28c82d52852d73c921aa40578abad55a2f3ec9106b01f1fd5dfc6f98104e9b2f8ab0006306685a4b3ecafca3d768ebf81223c213171482ee00d1f1
|
data/Gemfile
CHANGED
data/History.md
CHANGED
@@ -1,6 +1,21 @@
|
|
1
1
|
Dalli Changelog
|
2
2
|
=====================
|
3
3
|
|
4
|
+
2.7.5
|
5
|
+
==========
|
6
|
+
|
7
|
+
- Support rcvbuff and sndbuff byte configuration. (btatnall)
|
8
|
+
- Add `:cache_nils` option to support nil values in `DalliStore#fetch` and `Dalli::Client#fetch` (wjordan, #559)
|
9
|
+
- Log retryable server errors with 'warn' instead of 'info' (phrinx)
|
10
|
+
- Fix timeout issue with Dalli::Client#get_multi_yielder (dspeterson)
|
11
|
+
- Escape namespaces with special regexp characters (Steven Peckins)
|
12
|
+
- Ensure LocalCache supports the `:raw` option and Entry unwrapping (sj26)
|
13
|
+
- Ensure bad ttl values don't cause Dalli::RingError (eagletmt, petergoldstein)
|
14
|
+
- Always pass namespaced key to instrumentation API (kaorimatz)
|
15
|
+
- Replace use of deprecated TimeoutError with Timeout::Error (eagletmt)
|
16
|
+
- Clean up gemspec, and use Bundler for loading (grosser)
|
17
|
+
- Dry up local cache testing (grosser)
|
18
|
+
|
4
19
|
2.7.4
|
5
20
|
==========
|
6
21
|
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Dalli [![Build Status](https://secure.travis-ci.org/
|
1
|
+
Dalli [![Build Status](https://secure.travis-ci.org/petergoldstein/dalli.png)](http://travis-ci.org/petergoldstein/dalli) [![Dependency Status](https://gemnasium.com/petergoldstein/dalli.png)](https://gemnasium.com/petergoldstein/dalli) [![Code Climate](https://codeclimate.com/github/petergoldstein/dalli.png)](https://codeclimate.com/github/petergoldstein/dalli)
|
2
2
|
=====
|
3
3
|
|
4
4
|
Dalli is a high performance pure Ruby client for accessing memcached servers. It works with memcached 1.4+ only as it uses the newer binary protocol. It should be considered a replacement for the memcache-client gem.
|
@@ -13,7 +13,7 @@ Dalli's initial development was sponsored by [CouchBase](http://www.couchbase.co
|
|
13
13
|
Design
|
14
14
|
------------
|
15
15
|
|
16
|
-
|
16
|
+
Mike Perham decided to write Dalli after maintaining memcache-client for two years for a few specific reasons:
|
17
17
|
|
18
18
|
0. The code is mostly old and gross. The bulk of the code is a single 1000 line .rb file.
|
19
19
|
1. It has a lot of options that are infrequently used which complicate the codebase.
|
@@ -186,6 +186,12 @@ If serving compressed data using nginx's HttpMemcachedModule, set `memcached_gzi
|
|
186
186
|
|
187
187
|
**password**: The password to use for authenticating this client instance against a SASL-enabled memcached server. Heroku users should not need to use this normally.
|
188
188
|
|
189
|
+
**sndbuf**: In bytes, set the socket SO_SNDBUF. Defaults to operating system default.
|
190
|
+
|
191
|
+
**rcvbuf**: In bytes, set the socket SO_RCVBUF. Defaults to operating system default.
|
192
|
+
|
193
|
+
**cache_nils**: Boolean. If true Dalli will not treat cached `nil` values as 'not found' for `#fetch` operations. Default is false.
|
194
|
+
|
189
195
|
Features and Changes
|
190
196
|
------------------------
|
191
197
|
|
@@ -209,20 +215,22 @@ We're not accepting new compressors. They are trivial to add in an initializer.
|
|
209
215
|
Thanks
|
210
216
|
------------
|
211
217
|
|
218
|
+
Mike Perham - for originally authoring the Dalli project and serving as maintainer and primary contributor
|
219
|
+
|
212
220
|
Eric Wong - for help using his [kgio](http://bogomips.org/kgio/) library.
|
213
221
|
|
214
222
|
Brian Mitchell - for his remix-stash project which was helpful when implementing and testing the binary protocol support.
|
215
223
|
|
216
224
|
[CouchBase](http://couchbase.com) - for their project sponsorship
|
217
225
|
|
218
|
-
|
219
|
-
Author
|
226
|
+
Authors
|
220
227
|
----------
|
221
228
|
|
222
|
-
|
229
|
+
* [Peter M. Goldstein](https://github.com/petergoldstein) - current maintainer
|
230
|
+
* [Mike Perham](https://github.com/mperham) and contributors
|
223
231
|
|
224
232
|
|
225
233
|
Copyright
|
226
234
|
-----------
|
227
235
|
|
228
|
-
Copyright (c) Mike Perham. See LICENSE for details.
|
236
|
+
Copyright (c) Mike Perham, Peter M. Goldstein. See LICENSE for details.
|
@@ -68,6 +68,7 @@ module ActiveSupport
|
|
68
68
|
end
|
69
69
|
|
70
70
|
extend Strategy::LocalCache
|
71
|
+
extend LocalCacheEntryUnwrapAndRaw
|
71
72
|
end
|
72
73
|
|
73
74
|
##
|
@@ -81,31 +82,41 @@ module ActiveSupport
|
|
81
82
|
@data.with(&block)
|
82
83
|
end
|
83
84
|
|
85
|
+
# Fetch the value associated with the key.
|
86
|
+
# If a value is found, then it is returned.
|
87
|
+
#
|
88
|
+
# If a value is not found and no block is given, then nil is returned.
|
89
|
+
#
|
90
|
+
# If a value is not found (or if the found value is nil and :cache_nils is false)
|
91
|
+
# and a block is given, the block will be invoked and its return value
|
92
|
+
# written to the cache and returned.
|
84
93
|
def fetch(name, options=nil)
|
85
94
|
options ||= {}
|
95
|
+
options[:cache_nils] = true if @options[:cache_nils]
|
86
96
|
namespaced_name = namespaced_key(name, options)
|
87
|
-
|
97
|
+
not_found = options[:cache_nils] ? Dalli::Server::NOT_FOUND : nil
|
88
98
|
if block_given?
|
99
|
+
entry = not_found
|
89
100
|
unless options[:force]
|
90
101
|
entry = instrument(:read, namespaced_name, options) do |payload|
|
91
102
|
read_entry(namespaced_name, options).tap do |result|
|
92
103
|
if payload
|
93
104
|
payload[:super_operation] = :fetch
|
94
|
-
payload[:hit] =
|
105
|
+
payload[:hit] = result != not_found
|
95
106
|
end
|
96
107
|
end
|
97
108
|
end
|
98
109
|
end
|
99
110
|
|
100
|
-
if
|
101
|
-
instrument(:
|
102
|
-
entry
|
103
|
-
else
|
104
|
-
result = instrument(:generate, name, options) do |payload|
|
111
|
+
if entry == not_found
|
112
|
+
result = instrument(:generate, namespaced_name, options) do |payload|
|
105
113
|
yield
|
106
114
|
end
|
107
115
|
write(name, result, options)
|
108
116
|
result
|
117
|
+
else
|
118
|
+
instrument(:fetch_hit, namespaced_name, options) { |payload| }
|
119
|
+
entry
|
109
120
|
end
|
110
121
|
else
|
111
122
|
read(name, options)
|
@@ -157,7 +168,7 @@ module ActiveSupport
|
|
157
168
|
def read_multi(*names)
|
158
169
|
options = names.extract_options!
|
159
170
|
mapping = names.inject({}) { |memo, name| memo[namespaced_key(name, options)] = name; memo }
|
160
|
-
instrument(:read_multi,
|
171
|
+
instrument(:read_multi, mapping.keys) do
|
161
172
|
results = {}
|
162
173
|
if local_cache
|
163
174
|
mapping.each_key do |key|
|
@@ -188,7 +199,7 @@ module ActiveSupport
|
|
188
199
|
options = names.extract_options!
|
189
200
|
mapping = names.inject({}) { |memo, name| memo[namespaced_key(name, options)] = name; memo }
|
190
201
|
|
191
|
-
instrument(:fetch_multi,
|
202
|
+
instrument(:fetch_multi, mapping.keys) do
|
192
203
|
with do |connection|
|
193
204
|
results = connection.get_multi(mapping.keys)
|
194
205
|
|
@@ -367,6 +378,25 @@ module ActiveSupport
|
|
367
378
|
def raise_errors?
|
368
379
|
!!@options[:raise_errors]
|
369
380
|
end
|
381
|
+
|
382
|
+
# Make sure LocalCache is giving raw values, not `Entry`s, and
|
383
|
+
# respect `raw` option.
|
384
|
+
module LocalCacheEntryUnwrapAndRaw # :nodoc:
|
385
|
+
protected
|
386
|
+
def read_entry(key, options)
|
387
|
+
retval = super
|
388
|
+
if retval.is_a? ActiveSupport::Cache::Entry
|
389
|
+
# Must have come from LocalStore, unwrap it
|
390
|
+
if options[:raw]
|
391
|
+
retval.value.to_s
|
392
|
+
else
|
393
|
+
retval.value
|
394
|
+
end
|
395
|
+
else
|
396
|
+
retval
|
397
|
+
end
|
398
|
+
end
|
399
|
+
end
|
370
400
|
end
|
371
401
|
end
|
372
402
|
end
|
data/lib/dalli/client.rb
CHANGED
@@ -27,6 +27,7 @@ module Dalli
|
|
27
27
|
# - :compress - defaults to false, if true Dalli will compress values larger than 1024 bytes before sending them to memcached.
|
28
28
|
# - :serializer - defaults to Marshal
|
29
29
|
# - :compressor - defaults to zlib
|
30
|
+
# - :cache_nils - defaults to false, if true Dalli will not treat cached nil values as 'not found' for #fetch operations.
|
30
31
|
#
|
31
32
|
def initialize(servers=nil, options={})
|
32
33
|
@servers = normalize_servers(servers || ENV["MEMCACHE_SERVERS"] || '127.0.0.1:11211')
|
@@ -52,8 +53,9 @@ module Dalli
|
|
52
53
|
|
53
54
|
##
|
54
55
|
# Get the value associated with the key.
|
56
|
+
# If a value is not found, then +nil+ is returned.
|
55
57
|
def get(key, options=nil)
|
56
|
-
perform(:get, key)
|
58
|
+
perform(:get, key, options)
|
57
59
|
end
|
58
60
|
|
59
61
|
##
|
@@ -71,12 +73,25 @@ module Dalli
|
|
71
73
|
end
|
72
74
|
end
|
73
75
|
|
76
|
+
CACHE_NILS = {cache_nils: true}.freeze
|
77
|
+
|
78
|
+
# Fetch the value associated with the key.
|
79
|
+
# If a value is found, then it is returned.
|
80
|
+
#
|
81
|
+
# If a value is not found and no block is given, then nil is returned.
|
82
|
+
#
|
83
|
+
# If a value is not found (or if the found value is nil and :cache_nils is false)
|
84
|
+
# and a block is given, the block will be invoked and its return value
|
85
|
+
# written to the cache and returned.
|
74
86
|
def fetch(key, ttl=nil, options=nil)
|
75
|
-
|
87
|
+
options = options.nil? ? CACHE_NILS : options.merge(CACHE_NILS) if @options[:cache_nils]
|
76
88
|
val = get(key, options)
|
77
|
-
|
89
|
+
not_found = @options[:cache_nils] ?
|
90
|
+
val == Dalli::Server::NOT_FOUND :
|
91
|
+
val.nil?
|
92
|
+
if not_found && block_given?
|
78
93
|
val = yield
|
79
|
-
add(key, val, ttl, options)
|
94
|
+
add(key, val, ttl_or_default(ttl), options)
|
80
95
|
end
|
81
96
|
val
|
82
97
|
end
|
@@ -93,34 +108,30 @@ module Dalli
|
|
93
108
|
# - false if the value was changed by someone else.
|
94
109
|
# - true if the value was successfully updated.
|
95
110
|
def cas(key, ttl=nil, options=nil)
|
96
|
-
ttl ||= @options[:expires_in].to_i
|
97
111
|
(value, cas) = perform(:cas, key)
|
98
112
|
value = (!value || value == 'Not found') ? nil : value
|
99
113
|
if value
|
100
114
|
newvalue = yield(value)
|
101
|
-
perform(:set, key, newvalue, ttl, cas, options)
|
115
|
+
perform(:set, key, newvalue, ttl_or_default(ttl), cas, options)
|
102
116
|
end
|
103
117
|
end
|
104
118
|
|
105
119
|
def set(key, value, ttl=nil, options=nil)
|
106
|
-
ttl
|
107
|
-
perform(:set, key, value, ttl, 0, options)
|
120
|
+
perform(:set, key, value, ttl_or_default(ttl), 0, options)
|
108
121
|
end
|
109
122
|
|
110
123
|
##
|
111
124
|
# Conditionally add a key/value pair, if the key does not already exist
|
112
125
|
# on the server. Returns truthy if the operation succeeded.
|
113
126
|
def add(key, value, ttl=nil, options=nil)
|
114
|
-
ttl
|
115
|
-
perform(:add, key, value, ttl, options)
|
127
|
+
perform(:add, key, value, ttl_or_default(ttl), options)
|
116
128
|
end
|
117
129
|
|
118
130
|
##
|
119
131
|
# Conditionally add a key/value pair, only if the key already exists
|
120
132
|
# on the server. Returns truthy if the operation succeeded.
|
121
133
|
def replace(key, value, ttl=nil, options=nil)
|
122
|
-
ttl
|
123
|
-
perform(:replace, key, value, ttl, 0, options)
|
134
|
+
perform(:replace, key, value, ttl_or_default(ttl), 0, options)
|
124
135
|
end
|
125
136
|
|
126
137
|
def delete(key)
|
@@ -161,8 +172,7 @@ module Dalli
|
|
161
172
|
# #cas.
|
162
173
|
def incr(key, amt=1, ttl=nil, default=nil)
|
163
174
|
raise ArgumentError, "Positive values only: #{amt}" if amt < 0
|
164
|
-
|
165
|
-
perform(:incr, key, amt.to_i, ttl, default)
|
175
|
+
perform(:incr, key, amt.to_i, ttl_or_default(ttl), default)
|
166
176
|
end
|
167
177
|
|
168
178
|
##
|
@@ -181,8 +191,7 @@ module Dalli
|
|
181
191
|
# #cas.
|
182
192
|
def decr(key, amt=1, ttl=nil, default=nil)
|
183
193
|
raise ArgumentError, "Positive values only: #{amt}" if amt < 0
|
184
|
-
|
185
|
-
perform(:decr, key, amt.to_i, ttl, default)
|
194
|
+
perform(:decr, key, amt.to_i, ttl_or_default(ttl), default)
|
186
195
|
end
|
187
196
|
|
188
197
|
##
|
@@ -190,8 +199,7 @@ module Dalli
|
|
190
199
|
#
|
191
200
|
# Returns true if key exists, otherwise nil.
|
192
201
|
def touch(key, ttl=nil)
|
193
|
-
|
194
|
-
resp = perform(:touch, key, ttl)
|
202
|
+
resp = perform(:touch, key, ttl_or_default(ttl))
|
195
203
|
resp.nil? ? nil : true
|
196
204
|
end
|
197
205
|
|
@@ -250,6 +258,12 @@ module Dalli
|
|
250
258
|
|
251
259
|
private
|
252
260
|
|
261
|
+
def ttl_or_default(ttl)
|
262
|
+
(ttl || @options[:expires_in]).to_i
|
263
|
+
rescue NoMethodError
|
264
|
+
raise ArgumentError, "Cannot convert ttl (#{ttl}) to an integer"
|
265
|
+
end
|
266
|
+
|
253
267
|
def groups_for_keys(*keys)
|
254
268
|
groups = mapped_keys(keys).flatten.group_by do |key|
|
255
269
|
begin
|
@@ -296,8 +310,9 @@ module Dalli
|
|
296
310
|
end
|
297
311
|
|
298
312
|
##
|
299
|
-
# Normalizes the argument into an array of servers.
|
300
|
-
# the
|
313
|
+
# Normalizes the argument into an array of servers.
|
314
|
+
# If the argument is a string, it's expected that the URIs are comma separated e.g.
|
315
|
+
# "memcache1.example.com:11211,memcache2.example.com:11211,memcache3.example.com:11211"
|
301
316
|
def normalize_servers(servers)
|
302
317
|
if servers.is_a? String
|
303
318
|
return servers.split(",")
|
@@ -354,7 +369,7 @@ module Dalli
|
|
354
369
|
end
|
355
370
|
|
356
371
|
def key_without_namespace(key)
|
357
|
-
(ns = namespace) ? key.sub(%r(\A#{ns}:), '') : key
|
372
|
+
(ns = namespace) ? key.sub(%r(\A#{Regexp.escape ns}:), '') : key
|
358
373
|
end
|
359
374
|
|
360
375
|
def namespace
|
@@ -401,12 +416,10 @@ module Dalli
|
|
401
416
|
# calculate remaining timeout
|
402
417
|
elapsed = Time.now - start
|
403
418
|
timeout = servers.first.options[:socket_timeout]
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
readable, _ = IO.select(sockets, nil, nil, timeout - elapsed)
|
409
|
-
end
|
419
|
+
time_left = (elapsed > timeout) ? 0 : timeout - elapsed
|
420
|
+
|
421
|
+
sockets = servers.map(&:sock)
|
422
|
+
readable, _ = IO.select(sockets, nil, nil, time_left)
|
410
423
|
|
411
424
|
if readable.nil?
|
412
425
|
# no response within timeout; abort pending connections
|
data/lib/dalli/server.rb
CHANGED
@@ -31,7 +31,11 @@ module Dalli
|
|
31
31
|
:serializer => Marshal,
|
32
32
|
:username => nil,
|
33
33
|
:password => nil,
|
34
|
-
:keepalive => true
|
34
|
+
:keepalive => true,
|
35
|
+
# max byte size for SO_SNDBUF
|
36
|
+
:sndbuf => nil,
|
37
|
+
# max byte size for SO_RCVBUF
|
38
|
+
:rcvbuf => nil
|
35
39
|
}
|
36
40
|
|
37
41
|
def initialize(attribs, options = {})
|
@@ -203,20 +207,28 @@ module Dalli
|
|
203
207
|
|
204
208
|
def verify_state
|
205
209
|
failure!(RuntimeError.new('Already writing to socket')) if @inprogress
|
206
|
-
|
210
|
+
if @pid && @pid != Process.pid
|
211
|
+
message = 'Fork detected, re-connecting child process...'
|
212
|
+
Dalli.logger.info { message }
|
213
|
+
reconnect! message
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def reconnect!(message)
|
218
|
+
close
|
219
|
+
sleep(options[:socket_failure_delay]) if options[:socket_failure_delay]
|
220
|
+
raise Dalli::NetworkError, message
|
207
221
|
end
|
208
222
|
|
209
223
|
def failure!(exception)
|
210
224
|
message = "#{name} failed (count: #{@fail_count}) #{exception.class}: #{exception.message}"
|
211
|
-
Dalli.logger.
|
225
|
+
Dalli.logger.warn { message }
|
212
226
|
|
213
227
|
@fail_count += 1
|
214
228
|
if @fail_count >= options[:socket_max_failures]
|
215
229
|
down!
|
216
230
|
else
|
217
|
-
|
218
|
-
sleep(options[:socket_failure_delay]) if options[:socket_failure_delay]
|
219
|
-
raise Dalli::NetworkError, "Socket operation failed, retrying..."
|
231
|
+
reconnect! 'Socket operation failed, retrying...'
|
220
232
|
end
|
221
233
|
end
|
222
234
|
|
@@ -255,10 +267,10 @@ module Dalli
|
|
255
267
|
Thread.current[:dalli_multi]
|
256
268
|
end
|
257
269
|
|
258
|
-
def get(key)
|
270
|
+
def get(key, options=nil)
|
259
271
|
req = [REQUEST, OPCODES[:get], key.bytesize, 0, 0, 0, key.bytesize, 0, 0, key].pack(FORMAT[:get])
|
260
272
|
write(req)
|
261
|
-
generic_response(true)
|
273
|
+
generic_response(true, !!(options && options.is_a?(Hash) && options[:cache_nils]))
|
262
274
|
end
|
263
275
|
|
264
276
|
def send_multiget(keys)
|
@@ -400,7 +412,7 @@ module Dalli
|
|
400
412
|
marshalled = true
|
401
413
|
begin
|
402
414
|
self.serializer.dump(value)
|
403
|
-
rescue
|
415
|
+
rescue Timeout::Error => e
|
404
416
|
raise e
|
405
417
|
rescue => ex
|
406
418
|
# Marshalling can throw several different types of generic Ruby exceptions.
|
@@ -413,7 +425,8 @@ module Dalli
|
|
413
425
|
value.to_s
|
414
426
|
end
|
415
427
|
compressed = false
|
416
|
-
if
|
428
|
+
set_compress_option = true if options && options[:compress]
|
429
|
+
if (@options[:compress] || set_compress_option) && value.bytesize >= @options[:compression_min_size] &&
|
417
430
|
(!@options[:compression_max_size] || value.bytesize <= @options[:compression_max_size])
|
418
431
|
value = self.compressor.compress(value)
|
419
432
|
compressed = true
|
@@ -471,19 +484,21 @@ module Dalli
|
|
471
484
|
# > An expiration time, in seconds. Can be up to 30 days. After 30 days, is treated as a unix timestamp of an exact date.
|
472
485
|
MAX_ACCEPTABLE_EXPIRATION_INTERVAL = 30*24*60*60 # 30 days
|
473
486
|
def sanitize_ttl(ttl)
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
ttl.to_i
|
479
|
-
end
|
487
|
+
ttl_as_i = ttl.to_i
|
488
|
+
return ttl_as_i if ttl_as_i <= MAX_ACCEPTABLE_EXPIRATION_INTERVAL
|
489
|
+
Dalli.logger.debug "Expiration interval (#{ttl_as_i}) too long for Memcached, converting to an expiration timestamp"
|
490
|
+
Time.now.to_i + ttl_as_i
|
480
491
|
end
|
481
492
|
|
482
|
-
|
493
|
+
# Implements the NullObject pattern to store an application-defined value for 'Key not found' responses.
|
494
|
+
class NilObject; end
|
495
|
+
NOT_FOUND = NilObject.new
|
496
|
+
|
497
|
+
def generic_response(unpack=false, cache_nils=false)
|
483
498
|
(extras, _, status, count) = read_header.unpack(NORMAL_HEADER)
|
484
499
|
data = read(count) if count > 0
|
485
500
|
if status == 1
|
486
|
-
nil
|
501
|
+
cache_nils ? NOT_FOUND : nil
|
487
502
|
elsif status == 2 || status == 5
|
488
503
|
false # Not stored, normal status for add operation
|
489
504
|
elsif status != 0
|