dalli 2.7.11 → 3.0.4
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 +5 -10
- data/History.md +57 -0
- data/README.md +26 -91
- data/lib/dalli/cas/client.rb +1 -59
- data/lib/dalli/client.rb +183 -110
- data/lib/dalli/compressor.rb +4 -3
- data/lib/dalli/options.rb +3 -4
- data/lib/dalli/protocol/binary.rb +703 -0
- data/lib/dalli/protocol/server_config_parser.rb +67 -0
- data/lib/dalli/protocol/ttl_sanitizer.rb +45 -0
- data/lib/dalli/protocol/value_compressor.rb +85 -0
- data/lib/dalli/protocol.rb +9 -0
- data/lib/dalli/ring.rb +12 -63
- data/lib/dalli/server.rb +2 -748
- data/lib/dalli/socket.rb +62 -128
- data/lib/dalli/version.rb +2 -1
- data/lib/dalli.rb +17 -16
- data/lib/rack/session/dalli.rb +21 -40
- metadata +37 -8
- data/lib/action_dispatch/middleware/session/dalli_store.rb +0 -82
- data/lib/active_support/cache/dalli_store.rb +0 -441
- data/lib/dalli/railtie.rb +0 -8
data/lib/dalli/client.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require
|
2
|
+
|
3
|
+
require "digest/md5"
|
4
|
+
require "set"
|
4
5
|
|
5
6
|
# encoding: ascii
|
6
7
|
module Dalli
|
7
8
|
class Client
|
8
|
-
|
9
9
|
##
|
10
10
|
# Dalli::Client is the main class which developers will use to interact with
|
11
11
|
# the memcached server. Usage:
|
@@ -25,14 +25,17 @@ module Dalli
|
|
25
25
|
# - :failover - if a server is down, look for and store values on another server in the ring. Default: true.
|
26
26
|
# - :threadsafe - ensure that only one thread is actively using a socket at a time. Default: true.
|
27
27
|
# - :expires_in - default TTL in seconds if you do not pass TTL as a parameter to an individual operation, defaults to 0 or forever
|
28
|
-
# - :compress -
|
28
|
+
# - :compress - if true Dalli will compress values larger than compression_min_size bytes before sending them to memcached. Default: true.
|
29
|
+
# - :compression_min_size - the minimum size (in bytes) for which Dalli will compress values sent to Memcached. Defaults to 4K.
|
29
30
|
# - :serializer - defaults to Marshal
|
30
31
|
# - :compressor - defaults to zlib
|
31
32
|
# - :cache_nils - defaults to false, if true Dalli will not treat cached nil values as 'not found' for #fetch operations.
|
32
33
|
# - :digest_class - defaults to Digest::MD5, allows you to pass in an object that responds to the hexdigest method, useful for injecting a FIPS compliant hash object.
|
34
|
+
# - :protocol_implementation - defaults to Dalli::Protocol::Binary which uses the binary protocol. Allows you to pass an alternative implementation using another protocol.
|
33
35
|
#
|
34
|
-
def initialize(servers=nil, options={})
|
35
|
-
|
36
|
+
def initialize(servers = nil, options = {})
|
37
|
+
validate_servers_arg(servers)
|
38
|
+
@servers = normalize_servers(servers || ENV["MEMCACHE_SERVERS"] || "127.0.0.1:11211")
|
36
39
|
@options = normalize_options(options)
|
37
40
|
@ring = nil
|
38
41
|
end
|
@@ -56,7 +59,7 @@ module Dalli
|
|
56
59
|
##
|
57
60
|
# Get the value associated with the key.
|
58
61
|
# If a value is not found, then +nil+ is returned.
|
59
|
-
def get(key, options=nil)
|
62
|
+
def get(key, options = nil)
|
60
63
|
perform(:get, key, options)
|
61
64
|
end
|
62
65
|
|
@@ -65,15 +68,15 @@ module Dalli
|
|
65
68
|
# If a block is given, yields key/value pairs one at a time.
|
66
69
|
# Otherwise returns a hash of { 'key' => 'value', 'key2' => 'value1' }
|
67
70
|
def get_multi(*keys)
|
68
|
-
|
69
|
-
|
71
|
+
keys.flatten!
|
72
|
+
keys.compact!
|
70
73
|
|
71
|
-
return {} if
|
74
|
+
return {} if keys.empty?
|
72
75
|
if block_given?
|
73
|
-
get_multi_yielder(keys) {|k, data| yield k, data.first}
|
76
|
+
get_multi_yielder(keys) { |k, data| yield k, data.first }
|
74
77
|
else
|
75
|
-
|
76
|
-
get_multi_yielder(keys) {|k, data| hash[k] = data.first}
|
78
|
+
{}.tap do |hash|
|
79
|
+
get_multi_yielder(keys) { |k, data| hash[k] = data.first }
|
77
80
|
end
|
78
81
|
end
|
79
82
|
end
|
@@ -88,11 +91,11 @@ module Dalli
|
|
88
91
|
# If a value is not found (or if the found value is nil and :cache_nils is false)
|
89
92
|
# and a block is given, the block will be invoked and its return value
|
90
93
|
# written to the cache and returned.
|
91
|
-
def fetch(key, ttl=nil, options=nil)
|
94
|
+
def fetch(key, ttl = nil, options = nil)
|
92
95
|
options = options.nil? ? CACHE_NILS : options.merge(CACHE_NILS) if @options[:cache_nils]
|
93
96
|
val = get(key, options)
|
94
97
|
not_found = @options[:cache_nils] ?
|
95
|
-
val == Dalli::
|
98
|
+
val == Dalli::Protocol::NOT_FOUND :
|
96
99
|
val.nil?
|
97
100
|
if not_found && block_given?
|
98
101
|
val = yield
|
@@ -112,7 +115,7 @@ module Dalli
|
|
112
115
|
# - nil if the key did not exist.
|
113
116
|
# - false if the value was changed by someone else.
|
114
117
|
# - true if the value was successfully updated.
|
115
|
-
def cas(key, ttl=nil, options=nil, &block)
|
118
|
+
def cas(key, ttl = nil, options = nil, &block)
|
116
119
|
cas_core(key, false, ttl, options, &block)
|
117
120
|
end
|
118
121
|
|
@@ -123,25 +126,25 @@ module Dalli
|
|
123
126
|
# Returns:
|
124
127
|
# - false if the value was changed by someone else.
|
125
128
|
# - true if the value was successfully updated.
|
126
|
-
def cas!(key, ttl=nil, options=nil, &block)
|
129
|
+
def cas!(key, ttl = nil, options = nil, &block)
|
127
130
|
cas_core(key, true, ttl, options, &block)
|
128
131
|
end
|
129
132
|
|
130
|
-
def set(key, value, ttl=nil, options=nil)
|
133
|
+
def set(key, value, ttl = nil, options = nil)
|
131
134
|
perform(:set, key, value, ttl_or_default(ttl), 0, options)
|
132
135
|
end
|
133
136
|
|
134
137
|
##
|
135
138
|
# Conditionally add a key/value pair, if the key does not already exist
|
136
139
|
# on the server. Returns truthy if the operation succeeded.
|
137
|
-
def add(key, value, ttl=nil, options=nil)
|
140
|
+
def add(key, value, ttl = nil, options = nil)
|
138
141
|
perform(:add, key, value, ttl_or_default(ttl), options)
|
139
142
|
end
|
140
143
|
|
141
144
|
##
|
142
145
|
# Conditionally add a key/value pair, only if the key already exists
|
143
146
|
# on the server. Returns truthy if the operation succeeded.
|
144
|
-
def replace(key, value, ttl=nil, options=nil)
|
147
|
+
def replace(key, value, ttl = nil, options = nil)
|
145
148
|
perform(:replace, key, value, ttl_or_default(ttl), 0, options)
|
146
149
|
end
|
147
150
|
|
@@ -163,7 +166,7 @@ module Dalli
|
|
163
166
|
perform(:prepend, key, value.to_s)
|
164
167
|
end
|
165
168
|
|
166
|
-
def flush(delay=0)
|
169
|
+
def flush(delay = 0)
|
167
170
|
time = -delay
|
168
171
|
ring.servers.map { |s| s.request(:flush, time += delay) }
|
169
172
|
end
|
@@ -181,7 +184,7 @@ module Dalli
|
|
181
184
|
# Note that the ttl will only apply if the counter does not already
|
182
185
|
# exist. To increase an existing counter and update its TTL, use
|
183
186
|
# #cas.
|
184
|
-
def incr(key, amt=1, ttl=nil, default=nil)
|
187
|
+
def incr(key, amt = 1, ttl = nil, default = nil)
|
185
188
|
raise ArgumentError, "Positive values only: #{amt}" if amt < 0
|
186
189
|
perform(:incr, key, amt.to_i, ttl_or_default(ttl), default)
|
187
190
|
end
|
@@ -200,7 +203,7 @@ module Dalli
|
|
200
203
|
# Note that the ttl will only apply if the counter does not already
|
201
204
|
# exist. To decrease an existing counter and update its TTL, use
|
202
205
|
# #cas.
|
203
|
-
def decr(key, amt=1, ttl=nil, default=nil)
|
206
|
+
def decr(key, amt = 1, ttl = nil, default = nil)
|
204
207
|
raise ArgumentError, "Positive values only: #{amt}" if amt < 0
|
205
208
|
perform(:decr, key, amt.to_i, ttl_or_default(ttl), default)
|
206
209
|
end
|
@@ -209,20 +212,28 @@ module Dalli
|
|
209
212
|
# Touch updates expiration time for a given key.
|
210
213
|
#
|
211
214
|
# Returns true if key exists, otherwise nil.
|
212
|
-
def touch(key, ttl=nil)
|
215
|
+
def touch(key, ttl = nil)
|
213
216
|
resp = perform(:touch, key, ttl_or_default(ttl))
|
214
217
|
resp.nil? ? nil : true
|
215
218
|
end
|
216
219
|
|
220
|
+
##
|
221
|
+
# Gat (get and touch) fetch an item and simultaneously update its expiration time.
|
222
|
+
#
|
223
|
+
# If a value is not found, then +nil+ is returned.
|
224
|
+
def gat(key, ttl = nil)
|
225
|
+
perform(:gat, key, ttl_or_default(ttl))
|
226
|
+
end
|
227
|
+
|
217
228
|
##
|
218
229
|
# Collect the stats for each server.
|
219
230
|
# You can optionally pass a type including :items, :slabs or :settings to get specific stats
|
220
231
|
# Returns a hash like { 'hostname:port' => { 'stat1' => 'value1', ... }, 'hostname2:port' => { ... } }
|
221
|
-
def stats(type=nil)
|
222
|
-
type = nil
|
232
|
+
def stats(type = nil)
|
233
|
+
type = nil unless [nil, :items, :slabs, :settings].include? type
|
223
234
|
values = {}
|
224
235
|
ring.servers.each do |server|
|
225
|
-
values[
|
236
|
+
values[server.name.to_s] = server.alive? ? server.request(:stats, type.to_s) : nil
|
226
237
|
end
|
227
238
|
values
|
228
239
|
end
|
@@ -246,11 +257,63 @@ module Dalli
|
|
246
257
|
def version
|
247
258
|
values = {}
|
248
259
|
ring.servers.each do |server|
|
249
|
-
values[
|
260
|
+
values[server.name.to_s] = server.alive? ? server.request(:version) : nil
|
250
261
|
end
|
251
262
|
values
|
252
263
|
end
|
253
264
|
|
265
|
+
##
|
266
|
+
# Get the value and CAS ID associated with the key. If a block is provided,
|
267
|
+
# value and CAS will be passed to the block.
|
268
|
+
def get_cas(key)
|
269
|
+
(value, cas) = perform(:cas, key)
|
270
|
+
value = !value || value == "Not found" ? nil : value
|
271
|
+
if block_given?
|
272
|
+
yield value, cas
|
273
|
+
else
|
274
|
+
[value, cas]
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
##
|
279
|
+
# Fetch multiple keys efficiently, including available metadata such as CAS.
|
280
|
+
# If a block is given, yields key/data pairs one a time. Data is an array:
|
281
|
+
# [value, cas_id]
|
282
|
+
# If no block is given, returns a hash of
|
283
|
+
# { 'key' => [value, cas_id] }
|
284
|
+
def get_multi_cas(*keys)
|
285
|
+
if block_given?
|
286
|
+
get_multi_yielder(keys) { |*args| yield(*args) }
|
287
|
+
else
|
288
|
+
{}.tap do |hash|
|
289
|
+
get_multi_yielder(keys) { |k, data| hash[k] = data }
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
##
|
295
|
+
# Set the key-value pair, verifying existing CAS.
|
296
|
+
# Returns the resulting CAS value if succeeded, and falsy otherwise.
|
297
|
+
def set_cas(key, value, cas, ttl = nil, options = nil)
|
298
|
+
ttl ||= @options[:expires_in].to_i
|
299
|
+
perform(:set, key, value, ttl, cas, options)
|
300
|
+
end
|
301
|
+
|
302
|
+
##
|
303
|
+
# Conditionally add a key/value pair, verifying existing CAS, only if the
|
304
|
+
# key already exists on the server. Returns the new CAS value if the
|
305
|
+
# operation succeeded, or falsy otherwise.
|
306
|
+
def replace_cas(key, value, cas, ttl = nil, options = nil)
|
307
|
+
ttl ||= @options[:expires_in].to_i
|
308
|
+
perform(:replace, key, value, ttl, cas, options)
|
309
|
+
end
|
310
|
+
|
311
|
+
# Delete a key/value pair, verifying existing CAS.
|
312
|
+
# Returns true if succeeded, and falsy otherwise.
|
313
|
+
def delete_cas(key, cas = 0)
|
314
|
+
perform(:delete, key, cas)
|
315
|
+
end
|
316
|
+
|
254
317
|
##
|
255
318
|
# Close our connection to each server.
|
256
319
|
# If you perform another operation after this, the connections will be re-established.
|
@@ -269,9 +332,9 @@ module Dalli
|
|
269
332
|
|
270
333
|
private
|
271
334
|
|
272
|
-
def cas_core(key, always_set, ttl=nil, options=nil)
|
335
|
+
def cas_core(key, always_set, ttl = nil, options = nil)
|
273
336
|
(value, cas) = perform(:cas, key)
|
274
|
-
value =
|
337
|
+
value = !value || value == "Not found" ? nil : value
|
275
338
|
return if value.nil? && !always_set
|
276
339
|
newvalue = yield(value)
|
277
340
|
perform(:set, key, newvalue, ttl_or_default(ttl), cas, options)
|
@@ -284,50 +347,61 @@ module Dalli
|
|
284
347
|
end
|
285
348
|
|
286
349
|
def groups_for_keys(*keys)
|
287
|
-
|
350
|
+
keys.flatten!
|
351
|
+
keys.map! { |a| validate_key(a.to_s) }
|
352
|
+
|
353
|
+
keys.group_by { |key|
|
288
354
|
begin
|
289
355
|
ring.server_for_key(key)
|
290
356
|
rescue Dalli::RingError
|
291
357
|
Dalli.logger.debug { "unable to get key #{key}" }
|
292
358
|
nil
|
293
359
|
end
|
294
|
-
|
295
|
-
return groups
|
296
|
-
end
|
297
|
-
|
298
|
-
def mapped_keys(keys)
|
299
|
-
keys_array = keys.flatten
|
300
|
-
keys_array.map! { |a| validate_key(a.to_s) }
|
301
|
-
keys_array
|
360
|
+
}
|
302
361
|
end
|
303
362
|
|
304
363
|
def make_multi_get_requests(groups)
|
305
364
|
groups.each do |server, keys_for_server|
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
Dalli.logger.debug { "unable to get keys for server #{server.name}" }
|
315
|
-
end
|
365
|
+
# TODO: do this with the perform chokepoint?
|
366
|
+
# But given the fact that fetching the response doesn't take place
|
367
|
+
# in that slot it's misleading anyway. Need to move all of this method
|
368
|
+
# into perform to be meaningful
|
369
|
+
server.request(:send_multiget, keys_for_server)
|
370
|
+
rescue DalliError, NetworkError => e
|
371
|
+
Dalli.logger.debug { e.inspect }
|
372
|
+
Dalli.logger.debug { "unable to get keys for server #{server.name}" }
|
316
373
|
end
|
317
374
|
end
|
318
375
|
|
319
376
|
def perform_multi_response_start(servers)
|
377
|
+
deleted = []
|
378
|
+
|
320
379
|
servers.each do |server|
|
321
380
|
next unless server.alive?
|
381
|
+
|
322
382
|
begin
|
323
383
|
server.multi_response_start
|
324
|
-
rescue
|
384
|
+
rescue Dalli::NetworkError
|
385
|
+
servers.each { |s| s.multi_response_abort unless s.sock.nil? }
|
386
|
+
raise
|
387
|
+
rescue Dalli::DalliError => e
|
325
388
|
Dalli.logger.debug { e.inspect }
|
326
389
|
Dalli.logger.debug { "results from this server will be missing" }
|
327
|
-
|
390
|
+
deleted.append(server)
|
328
391
|
end
|
329
392
|
end
|
330
|
-
|
393
|
+
|
394
|
+
servers.delete_if { |server| deleted.include?(server) }
|
395
|
+
end
|
396
|
+
|
397
|
+
##
|
398
|
+
# Ensures that the servers arg is either an array or a string.
|
399
|
+
def validate_servers_arg(servers)
|
400
|
+
return if servers.nil?
|
401
|
+
return if servers.is_a?(Array)
|
402
|
+
return if servers.is_a?(String)
|
403
|
+
|
404
|
+
raise ArgumentError, "An explicit servers argument must be a comma separated string or an array containing strings."
|
331
405
|
end
|
332
406
|
|
333
407
|
##
|
@@ -346,30 +420,30 @@ module Dalli
|
|
346
420
|
|
347
421
|
def ring
|
348
422
|
@ring ||= Dalli::Ring.new(
|
349
|
-
@servers.map
|
350
|
-
|
351
|
-
if s
|
423
|
+
@servers.map { |s|
|
424
|
+
server_options = {}
|
425
|
+
if s.start_with?("memcached://")
|
352
426
|
uri = URI.parse(s)
|
353
427
|
server_options[:username] = uri.user
|
354
428
|
server_options[:password] = uri.password
|
355
429
|
s = "#{uri.host}:#{uri.port}"
|
356
430
|
end
|
357
|
-
Dalli::
|
358
|
-
|
431
|
+
@options.fetch(:protocol_implementation, Dalli::Protocol::Binary).new(s, @options.merge(server_options))
|
432
|
+
}, @options
|
359
433
|
)
|
360
434
|
end
|
361
435
|
|
362
436
|
# Chokepoint method for instrumentation
|
363
437
|
def perform(*all_args)
|
364
|
-
return yield if block_given?
|
365
|
-
op, key, *args = *all_args
|
366
|
-
|
367
|
-
key = key.to_s
|
368
|
-
key = validate_key(key)
|
369
438
|
begin
|
439
|
+
return yield if block_given?
|
440
|
+
op, key, *args = all_args
|
441
|
+
|
442
|
+
key = key.to_s
|
443
|
+
key = validate_key(key)
|
444
|
+
|
370
445
|
server = ring.server_for_key(key)
|
371
|
-
|
372
|
-
ret
|
446
|
+
server.request(op, key, *args)
|
373
447
|
rescue NetworkError => e
|
374
448
|
Dalli.logger.debug { e.inspect }
|
375
449
|
Dalli.logger.debug { "retrying request with new server" }
|
@@ -382,10 +456,10 @@ module Dalli
|
|
382
456
|
key = key_with_namespace(key)
|
383
457
|
if key.length > 250
|
384
458
|
digest_class = @options[:digest_class] || ::Digest::MD5
|
385
|
-
max_length_before_namespace = 212 - (namespace ||
|
459
|
+
max_length_before_namespace = 212 - (namespace || "").size
|
386
460
|
key = "#{key[0, max_length_before_namespace]}:md5:#{digest_class.hexdigest(key)}"
|
387
461
|
end
|
388
|
-
|
462
|
+
key
|
389
463
|
end
|
390
464
|
|
391
465
|
def key_with_namespace(key)
|
@@ -393,7 +467,7 @@ module Dalli
|
|
393
467
|
end
|
394
468
|
|
395
469
|
def key_without_namespace(key)
|
396
|
-
(ns = namespace) ? key.sub(%r
|
470
|
+
(ns = namespace) ? key.sub(%r{\A#{Regexp.escape ns}:}, "") : key
|
397
471
|
end
|
398
472
|
|
399
473
|
def namespace
|
@@ -423,54 +497,53 @@ module Dalli
|
|
423
497
|
perform do
|
424
498
|
return {} if keys.empty?
|
425
499
|
ring.lock do
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
break
|
500
|
+
groups = groups_for_keys(keys)
|
501
|
+
if (unfound_keys = groups.delete(nil))
|
502
|
+
Dalli.logger.debug { "unable to get keys for #{unfound_keys.length} keys because no matching server was found" }
|
503
|
+
end
|
504
|
+
make_multi_get_requests(groups)
|
505
|
+
|
506
|
+
servers = groups.keys
|
507
|
+
return if servers.empty?
|
508
|
+
servers = perform_multi_response_start(servers)
|
509
|
+
|
510
|
+
start = Time.now
|
511
|
+
loop do
|
512
|
+
# remove any dead servers
|
513
|
+
servers.delete_if { |s| s.sock.nil? }
|
514
|
+
break if servers.empty?
|
515
|
+
|
516
|
+
# calculate remaining timeout
|
517
|
+
elapsed = Time.now - start
|
518
|
+
timeout = servers.first.options[:socket_timeout]
|
519
|
+
time_left = elapsed > timeout ? 0 : timeout - elapsed
|
520
|
+
|
521
|
+
sockets = servers.map(&:sock)
|
522
|
+
readable, _ = IO.select(sockets, nil, nil, time_left)
|
523
|
+
|
524
|
+
if readable.nil?
|
525
|
+
# no response within timeout; abort pending connections
|
526
|
+
servers.each do |server|
|
527
|
+
Dalli.logger.debug { "memcached at #{server.name} did not response within timeout" }
|
528
|
+
server.multi_response_abort
|
529
|
+
end
|
530
|
+
break
|
458
531
|
|
459
|
-
|
460
|
-
|
461
|
-
|
532
|
+
else
|
533
|
+
readable.each do |sock|
|
534
|
+
server = sock.server
|
462
535
|
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
536
|
+
begin
|
537
|
+
server.multi_response_nonblock.each_pair do |key, value_list|
|
538
|
+
yield key_without_namespace(key), value_list
|
539
|
+
end
|
467
540
|
|
468
|
-
|
469
|
-
servers.delete(server)
|
470
|
-
end
|
471
|
-
rescue NetworkError
|
541
|
+
if server.multi_response_completed?
|
472
542
|
servers.delete(server)
|
473
543
|
end
|
544
|
+
rescue NetworkError
|
545
|
+
servers.each { |s| s.multi_response_abort unless s.sock.nil? }
|
546
|
+
raise
|
474
547
|
end
|
475
548
|
end
|
476
549
|
end
|
data/lib/dalli/compressor.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require
|
2
|
+
|
3
|
+
require "zlib"
|
4
|
+
require "stringio"
|
4
5
|
|
5
6
|
module Dalli
|
6
7
|
class Compressor
|
@@ -15,7 +16,7 @@ module Dalli
|
|
15
16
|
|
16
17
|
class GzipCompressor
|
17
18
|
def self.compress(data)
|
18
|
-
io = StringIO.new(
|
19
|
+
io = StringIO.new(+"", "w")
|
19
20
|
gz = Zlib::GzipWriter.new(io)
|
20
21
|
gz.write(data)
|
21
22
|
gz.close
|
data/lib/dalli/options.rb
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'thread'
|
3
|
-
require 'monitor'
|
4
2
|
|
5
|
-
|
3
|
+
require "monitor"
|
6
4
|
|
5
|
+
module Dalli
|
7
6
|
# Make Dalli threadsafe by using a lock around all
|
8
7
|
# public server methods.
|
9
8
|
#
|
10
|
-
# Dalli::
|
9
|
+
# Dalli::Protocol::Binary.extend(Dalli::Threadsafe)
|
11
10
|
#
|
12
11
|
module Threadsafe
|
13
12
|
def self.extended(obj)
|