dalli 2.7.8 → 3.2.2
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 -1
- data/History.md +159 -0
- data/README.md +27 -223
- data/lib/dalli/cas/client.rb +1 -57
- data/lib/dalli/client.rb +227 -254
- data/lib/dalli/compressor.rb +12 -2
- data/lib/dalli/key_manager.rb +113 -0
- data/lib/dalli/options.rb +6 -7
- data/lib/dalli/pipelined_getter.rb +177 -0
- data/lib/dalli/protocol/base.rb +241 -0
- data/lib/dalli/protocol/binary/request_formatter.rb +117 -0
- data/lib/dalli/protocol/binary/response_header.rb +36 -0
- data/lib/dalli/protocol/binary/response_processor.rb +239 -0
- data/lib/dalli/protocol/binary/sasl_authentication.rb +60 -0
- data/lib/dalli/protocol/binary.rb +173 -0
- data/lib/dalli/protocol/connection_manager.rb +252 -0
- data/lib/dalli/protocol/meta/key_regularizer.rb +31 -0
- data/lib/dalli/protocol/meta/request_formatter.rb +108 -0
- data/lib/dalli/protocol/meta/response_processor.rb +211 -0
- data/lib/dalli/protocol/meta.rb +177 -0
- data/lib/dalli/protocol/response_buffer.rb +54 -0
- data/lib/dalli/protocol/server_config_parser.rb +84 -0
- data/lib/dalli/protocol/ttl_sanitizer.rb +45 -0
- data/lib/dalli/protocol/value_compressor.rb +85 -0
- data/lib/dalli/protocol/value_marshaller.rb +59 -0
- data/lib/dalli/protocol/value_serializer.rb +91 -0
- data/lib/dalli/protocol.rb +8 -0
- data/lib/dalli/ring.rb +94 -83
- data/lib/dalli/server.rb +3 -746
- data/lib/dalli/servers_arg_normalizer.rb +54 -0
- data/lib/dalli/socket.rb +117 -137
- data/lib/dalli/version.rb +4 -1
- data/lib/dalli.rb +43 -15
- data/lib/rack/session/dalli.rb +103 -94
- metadata +64 -27
- data/lib/action_dispatch/middleware/session/dalli_store.rb +0 -82
- data/lib/active_support/cache/dalli_store.rb +0 -429
- data/lib/dalli/railtie.rb +0 -8
@@ -1,429 +0,0 @@
|
|
1
|
-
# encoding: ascii
|
2
|
-
# frozen_string_literal: true
|
3
|
-
require 'dalli'
|
4
|
-
|
5
|
-
module ActiveSupport
|
6
|
-
module Cache
|
7
|
-
class DalliStore
|
8
|
-
|
9
|
-
attr_reader :silence, :options
|
10
|
-
alias_method :silence?, :silence
|
11
|
-
|
12
|
-
# Silence the logger.
|
13
|
-
def silence!
|
14
|
-
@silence = true
|
15
|
-
self
|
16
|
-
end
|
17
|
-
|
18
|
-
# Silence the logger within a block.
|
19
|
-
def mute
|
20
|
-
previous_silence, @silence = defined?(@silence) && @silence, true
|
21
|
-
yield
|
22
|
-
ensure
|
23
|
-
@silence = previous_silence
|
24
|
-
end
|
25
|
-
|
26
|
-
ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/
|
27
|
-
|
28
|
-
# Creates a new DalliStore object, with the given memcached server
|
29
|
-
# addresses. Each address is either a host name, or a host-with-port string
|
30
|
-
# in the form of "host_name:port". For example:
|
31
|
-
#
|
32
|
-
# ActiveSupport::Cache::DalliStore.new("localhost", "server-downstairs.localnetwork:8229")
|
33
|
-
#
|
34
|
-
# If no addresses are specified, then DalliStore will connect to
|
35
|
-
# localhost port 11211 (the default memcached port).
|
36
|
-
#
|
37
|
-
# Connection Pool support
|
38
|
-
#
|
39
|
-
# If you are using multithreaded Rails, the Rails.cache singleton can become a source
|
40
|
-
# of contention. You can use a connection pool of Dalli clients with Rails.cache by
|
41
|
-
# passing :pool_size and/or :pool_timeout:
|
42
|
-
#
|
43
|
-
# config.cache_store = :dalli_store, 'localhost:11211', :pool_size => 10
|
44
|
-
#
|
45
|
-
# Both pool options default to 5. You must include the `connection_pool` gem if you
|
46
|
-
# wish to use pool support.
|
47
|
-
#
|
48
|
-
def initialize(*addresses)
|
49
|
-
addresses = addresses.flatten
|
50
|
-
options = addresses.extract_options!
|
51
|
-
@options = options.dup
|
52
|
-
|
53
|
-
pool_options = {}
|
54
|
-
pool_options[:size] = options[:pool_size] if options[:pool_size]
|
55
|
-
pool_options[:timeout] = options[:pool_timeout] if options[:pool_timeout]
|
56
|
-
|
57
|
-
@options[:compress] ||= @options[:compression]
|
58
|
-
|
59
|
-
addresses.compact!
|
60
|
-
servers = if addresses.empty?
|
61
|
-
nil # use the default from Dalli::Client
|
62
|
-
else
|
63
|
-
addresses
|
64
|
-
end
|
65
|
-
if pool_options.empty?
|
66
|
-
@data = Dalli::Client.new(servers, @options)
|
67
|
-
else
|
68
|
-
@data = ::ConnectionPool.new(pool_options) { Dalli::Client.new(servers, @options.merge(:threadsafe => false)) }
|
69
|
-
end
|
70
|
-
|
71
|
-
extend Strategy::LocalCache
|
72
|
-
extend LocalCacheEntryUnwrapAndRaw
|
73
|
-
end
|
74
|
-
|
75
|
-
##
|
76
|
-
# Access the underlying Dalli::Client or ConnectionPool instance for
|
77
|
-
# access to get_multi, etc.
|
78
|
-
def dalli
|
79
|
-
@data
|
80
|
-
end
|
81
|
-
|
82
|
-
def with(&block)
|
83
|
-
@data.with(&block)
|
84
|
-
end
|
85
|
-
|
86
|
-
# Fetch the value associated with the key.
|
87
|
-
# If a value is found, then it is returned.
|
88
|
-
#
|
89
|
-
# If a value is not found and no block is given, then nil is returned.
|
90
|
-
#
|
91
|
-
# If a value is not found (or if the found value is nil and :cache_nils is false)
|
92
|
-
# and a block is given, the block will be invoked and its return value
|
93
|
-
# written to the cache and returned.
|
94
|
-
def fetch(name, options=nil)
|
95
|
-
options ||= {}
|
96
|
-
options[:cache_nils] = true if @options[:cache_nils]
|
97
|
-
namespaced_name = namespaced_key(name, options)
|
98
|
-
not_found = options[:cache_nils] ? Dalli::Server::NOT_FOUND : nil
|
99
|
-
if block_given?
|
100
|
-
entry = not_found
|
101
|
-
unless options[:force]
|
102
|
-
entry = instrument_with_log(:read, namespaced_name, options) do |payload|
|
103
|
-
read_entry(namespaced_name, options).tap do |result|
|
104
|
-
if payload
|
105
|
-
payload[:super_operation] = :fetch
|
106
|
-
payload[:hit] = not_found != result
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
if not_found == entry
|
113
|
-
result = instrument_with_log(:generate, namespaced_name, options) do |payload|
|
114
|
-
yield
|
115
|
-
end
|
116
|
-
write(name, result, options)
|
117
|
-
result
|
118
|
-
else
|
119
|
-
instrument_with_log(:fetch_hit, namespaced_name, options) { |payload| }
|
120
|
-
entry
|
121
|
-
end
|
122
|
-
else
|
123
|
-
read(name, options)
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
def read(name, options=nil)
|
128
|
-
options ||= {}
|
129
|
-
name = namespaced_key(name, options)
|
130
|
-
|
131
|
-
instrument_with_log(:read, name, options) do |payload|
|
132
|
-
entry = read_entry(name, options)
|
133
|
-
payload[:hit] = !entry.nil? if payload
|
134
|
-
entry
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
def write(name, value, options=nil)
|
139
|
-
options ||= {}
|
140
|
-
name = namespaced_key(name, options)
|
141
|
-
|
142
|
-
instrument_with_log(:write, name, options) do |payload|
|
143
|
-
with do |connection|
|
144
|
-
options = options.merge(:connection => connection)
|
145
|
-
write_entry(name, value, options)
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
def exist?(name, options=nil)
|
151
|
-
options ||= {}
|
152
|
-
name = namespaced_key(name, options)
|
153
|
-
|
154
|
-
log(:exist, name, options)
|
155
|
-
!read_entry(name, options).nil?
|
156
|
-
end
|
157
|
-
|
158
|
-
def delete(name, options=nil)
|
159
|
-
options ||= {}
|
160
|
-
name = namespaced_key(name, options)
|
161
|
-
|
162
|
-
instrument_with_log(:delete, name, options) do |payload|
|
163
|
-
delete_entry(name, options)
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
# Reads multiple keys from the cache using a single call to the
|
168
|
-
# servers for all keys. Keys must be Strings.
|
169
|
-
def read_multi(*names)
|
170
|
-
options = names.extract_options!
|
171
|
-
mapping = names.inject({}) { |memo, name| memo[namespaced_key(name, options)] = name; memo }
|
172
|
-
instrument_with_log(:read_multi, mapping.keys) do
|
173
|
-
results = {}
|
174
|
-
if local_cache
|
175
|
-
mapping.each_key do |key|
|
176
|
-
if value = local_cache.read_entry(key, options)
|
177
|
-
results[key] = value
|
178
|
-
end
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
data = with { |c| c.get_multi(mapping.keys - results.keys) }
|
183
|
-
results.merge!(data)
|
184
|
-
results.inject({}) do |memo, (inner, _)|
|
185
|
-
entry = results[inner]
|
186
|
-
# NB Backwards data compatibility, to be removed at some point
|
187
|
-
value = (entry.is_a?(ActiveSupport::Cache::Entry) ? entry.value : entry)
|
188
|
-
memo[mapping[inner]] = value
|
189
|
-
local_cache.write_entry(inner, value, options) if local_cache
|
190
|
-
memo
|
191
|
-
end
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
# Fetches data from the cache, using the given keys. If there is data in
|
196
|
-
# the cache with the given keys, then that data is returned. Otherwise,
|
197
|
-
# the supplied block is called for each key for which there was no data,
|
198
|
-
# and the result will be written to the cache and returned.
|
199
|
-
def fetch_multi(*names)
|
200
|
-
options = names.extract_options!
|
201
|
-
mapping = names.inject({}) { |memo, name| memo[namespaced_key(name, options)] = name; memo }
|
202
|
-
|
203
|
-
instrument_with_log(:fetch_multi, mapping.keys) do
|
204
|
-
with do |connection|
|
205
|
-
results = connection.get_multi(mapping.keys)
|
206
|
-
|
207
|
-
connection.multi do
|
208
|
-
mapping.inject({}) do |memo, (expanded, name)|
|
209
|
-
memo[name] = results[expanded]
|
210
|
-
if memo[name].nil?
|
211
|
-
value = yield(name)
|
212
|
-
memo[name] = value
|
213
|
-
options = options.merge(:connection => connection)
|
214
|
-
write_entry(expanded, value, options)
|
215
|
-
end
|
216
|
-
|
217
|
-
memo
|
218
|
-
end
|
219
|
-
end
|
220
|
-
end
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
# Increment a cached value. This method uses the memcached incr atomic
|
225
|
-
# operator and can only be used on values written with the :raw option.
|
226
|
-
# Calling it on a value not stored with :raw will fail.
|
227
|
-
# :initial defaults to the amount passed in, as if the counter was initially zero.
|
228
|
-
# memcached counters cannot hold negative values.
|
229
|
-
def increment(name, amount = 1, options=nil)
|
230
|
-
options ||= {}
|
231
|
-
name = namespaced_key(name, options)
|
232
|
-
initial = options.has_key?(:initial) ? options[:initial] : amount
|
233
|
-
expires_in = options[:expires_in]
|
234
|
-
instrument_with_log(:increment, name, :amount => amount) do
|
235
|
-
with { |c| c.incr(name, amount, expires_in, initial) }
|
236
|
-
end
|
237
|
-
rescue Dalli::DalliError => e
|
238
|
-
log_dalli_error(e)
|
239
|
-
instrument_error(e) if instrument_errors?
|
240
|
-
raise if raise_errors?
|
241
|
-
nil
|
242
|
-
end
|
243
|
-
|
244
|
-
# Decrement a cached value. This method uses the memcached decr atomic
|
245
|
-
# operator and can only be used on values written with the :raw option.
|
246
|
-
# Calling it on a value not stored with :raw will fail.
|
247
|
-
# :initial defaults to zero, as if the counter was initially zero.
|
248
|
-
# memcached counters cannot hold negative values.
|
249
|
-
def decrement(name, amount = 1, options=nil)
|
250
|
-
options ||= {}
|
251
|
-
name = namespaced_key(name, options)
|
252
|
-
initial = options.has_key?(:initial) ? options[:initial] : 0
|
253
|
-
expires_in = options[:expires_in]
|
254
|
-
instrument_with_log(:decrement, name, :amount => amount) do
|
255
|
-
with { |c| c.decr(name, amount, expires_in, initial) }
|
256
|
-
end
|
257
|
-
rescue Dalli::DalliError => e
|
258
|
-
log_dalli_error(e)
|
259
|
-
instrument_error(e) if instrument_errors?
|
260
|
-
raise if raise_errors?
|
261
|
-
nil
|
262
|
-
end
|
263
|
-
|
264
|
-
# Clear the entire cache on all memcached servers. This method should
|
265
|
-
# be used with care when using a shared cache.
|
266
|
-
def clear(options=nil)
|
267
|
-
instrument_with_log(:clear, 'flushing all keys') do
|
268
|
-
with { |c| c.flush_all }
|
269
|
-
end
|
270
|
-
rescue Dalli::DalliError => e
|
271
|
-
log_dalli_error(e)
|
272
|
-
instrument_error(e) if instrument_errors?
|
273
|
-
raise if raise_errors?
|
274
|
-
nil
|
275
|
-
end
|
276
|
-
|
277
|
-
# Clear any local cache
|
278
|
-
def cleanup(options=nil)
|
279
|
-
end
|
280
|
-
|
281
|
-
# Get the statistics from the memcached servers.
|
282
|
-
def stats
|
283
|
-
with { |c| c.stats }
|
284
|
-
end
|
285
|
-
|
286
|
-
def reset
|
287
|
-
with { |c| c.reset }
|
288
|
-
end
|
289
|
-
|
290
|
-
def logger
|
291
|
-
Dalli.logger
|
292
|
-
end
|
293
|
-
|
294
|
-
def logger=(new_logger)
|
295
|
-
Dalli.logger = new_logger
|
296
|
-
end
|
297
|
-
|
298
|
-
protected
|
299
|
-
|
300
|
-
# Read an entry from the cache.
|
301
|
-
def read_entry(key, options) # :nodoc:
|
302
|
-
entry = with { |c| c.get(key, options) }
|
303
|
-
# NB Backwards data compatibility, to be removed at some point
|
304
|
-
entry.is_a?(ActiveSupport::Cache::Entry) ? entry.value : entry
|
305
|
-
rescue Dalli::DalliError => e
|
306
|
-
log_dalli_error(e)
|
307
|
-
instrument_error(e) if instrument_errors?
|
308
|
-
raise if raise_errors?
|
309
|
-
nil
|
310
|
-
end
|
311
|
-
|
312
|
-
# Write an entry to the cache.
|
313
|
-
def write_entry(key, value, options) # :nodoc:
|
314
|
-
# cleanup LocalCache
|
315
|
-
cleanup if options[:unless_exist]
|
316
|
-
method = options[:unless_exist] ? :add : :set
|
317
|
-
expires_in = options[:expires_in]
|
318
|
-
connection = options.delete(:connection)
|
319
|
-
connection.send(method, key, value, expires_in, options)
|
320
|
-
rescue Dalli::DalliError => e
|
321
|
-
log_dalli_error(e)
|
322
|
-
instrument_error(e) if instrument_errors?
|
323
|
-
raise if raise_errors?
|
324
|
-
false
|
325
|
-
end
|
326
|
-
|
327
|
-
# Delete an entry from the cache.
|
328
|
-
def delete_entry(key, options) # :nodoc:
|
329
|
-
with { |c| c.delete(key) }
|
330
|
-
rescue Dalli::DalliError => e
|
331
|
-
log_dalli_error(e)
|
332
|
-
instrument_error(e) if instrument_errors?
|
333
|
-
raise if raise_errors?
|
334
|
-
false
|
335
|
-
end
|
336
|
-
|
337
|
-
private
|
338
|
-
|
339
|
-
def namespaced_key(key, options)
|
340
|
-
key = expanded_key(key)
|
341
|
-
namespace = options[:namespace] if options
|
342
|
-
prefix = namespace.is_a?(Proc) ? namespace.call : namespace
|
343
|
-
key = "#{prefix}:#{key}" if prefix
|
344
|
-
key = "#{key[0, 213]}:md5:#{::Digest::MD5.hexdigest(key)}" if key && key.size > 250
|
345
|
-
key
|
346
|
-
end
|
347
|
-
alias :normalize_key :namespaced_key
|
348
|
-
|
349
|
-
# Expand key to be a consistent string value. Invoke +cache_key+ if
|
350
|
-
# object responds to +cache_key+. Otherwise, to_param method will be
|
351
|
-
# called. If the key is a Hash, then keys will be sorted alphabetically.
|
352
|
-
def expanded_key(key) # :nodoc:
|
353
|
-
return key.cache_key.to_s if key.respond_to?(:cache_key)
|
354
|
-
|
355
|
-
case key
|
356
|
-
when Array
|
357
|
-
if key.size > 1
|
358
|
-
key = key.collect{|element| expanded_key(element)}
|
359
|
-
else
|
360
|
-
key = key.first
|
361
|
-
end
|
362
|
-
when Hash
|
363
|
-
key = key.sort_by { |k,_| k.to_s }.collect{|k,v| "#{k}=#{v}"}
|
364
|
-
end
|
365
|
-
|
366
|
-
key = key.to_param
|
367
|
-
if key.respond_to? :force_encoding
|
368
|
-
key = key.dup
|
369
|
-
key.force_encoding('binary')
|
370
|
-
end
|
371
|
-
key
|
372
|
-
end
|
373
|
-
|
374
|
-
def log_dalli_error(error)
|
375
|
-
logger.error("DalliError: #{error.message}") if logger
|
376
|
-
end
|
377
|
-
|
378
|
-
def instrument_with_log(operation, key, options=nil)
|
379
|
-
log(operation, key, options)
|
380
|
-
|
381
|
-
payload = { :key => key }
|
382
|
-
payload.merge!(options) if options.is_a?(Hash)
|
383
|
-
instrument(operation, payload) { |p| yield(p) }
|
384
|
-
end
|
385
|
-
|
386
|
-
def instrument_error(error)
|
387
|
-
instrument(:error, { :key => 'DalliError', :message => error.message })
|
388
|
-
end
|
389
|
-
|
390
|
-
def instrument(operation, payload)
|
391
|
-
ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload) do
|
392
|
-
yield(payload) if block_given?
|
393
|
-
end
|
394
|
-
end
|
395
|
-
|
396
|
-
def log(operation, key, options=nil)
|
397
|
-
return unless logger && logger.debug? && !silence?
|
398
|
-
logger.debug("Cache #{operation}: #{key}#{options.blank? ? "" : " (#{options.inspect})"}")
|
399
|
-
end
|
400
|
-
|
401
|
-
def raise_errors?
|
402
|
-
!!@options[:raise_errors]
|
403
|
-
end
|
404
|
-
|
405
|
-
def instrument_errors?
|
406
|
-
!!@options[:instrument_errors]
|
407
|
-
end
|
408
|
-
|
409
|
-
# Make sure LocalCache is giving raw values, not `Entry`s, and
|
410
|
-
# respect `raw` option.
|
411
|
-
module LocalCacheEntryUnwrapAndRaw # :nodoc:
|
412
|
-
protected
|
413
|
-
def read_entry(key, options)
|
414
|
-
retval = super
|
415
|
-
if retval.is_a? ActiveSupport::Cache::Entry
|
416
|
-
# Must have come from LocalStore, unwrap it
|
417
|
-
if options[:raw]
|
418
|
-
retval.value.to_s
|
419
|
-
else
|
420
|
-
retval.value
|
421
|
-
end
|
422
|
-
else
|
423
|
-
retval
|
424
|
-
end
|
425
|
-
end
|
426
|
-
end
|
427
|
-
end
|
428
|
-
end
|
429
|
-
end
|