dalli 2.7.0 → 2.7.11

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.

@@ -1,4 +1,5 @@
1
1
  # encoding: ascii
2
+ # frozen_string_literal: true
2
3
  require 'dalli'
3
4
 
4
5
  module ActiveSupport
@@ -8,6 +9,10 @@ module ActiveSupport
8
9
  attr_reader :silence, :options
9
10
  alias_method :silence?, :silence
10
11
 
12
+ def self.supports_cache_versioning?
13
+ true
14
+ end
15
+
11
16
  # Silence the logger.
12
17
  def silence!
13
18
  @silence = true
@@ -45,6 +50,11 @@ module ActiveSupport
45
50
  # wish to use pool support.
46
51
  #
47
52
  def initialize(*addresses)
53
+ puts <<-EOS
54
+ DEPRECATION: :dalli_store will be removed in Dalli 3.0.
55
+ Please use Rails' official :mem_cache_store instead.
56
+ https://guides.rubyonrails.org/caching_with_rails.html
57
+ EOS
48
58
  addresses = addresses.flatten
49
59
  options = addresses.extract_options!
50
60
  @options = options.dup
@@ -54,6 +64,8 @@ module ActiveSupport
54
64
  pool_options[:timeout] = options[:pool_timeout] if options[:pool_timeout]
55
65
 
56
66
  @options[:compress] ||= @options[:compression]
67
+
68
+ addresses.compact!
57
69
  servers = if addresses.empty?
58
70
  nil # use the default from Dalli::Client
59
71
  else
@@ -66,6 +78,7 @@ module ActiveSupport
66
78
  end
67
79
 
68
80
  extend Strategy::LocalCache
81
+ extend LocalCacheEntryUnwrapAndRaw
69
82
  end
70
83
 
71
84
  ##
@@ -79,31 +92,41 @@ module ActiveSupport
79
92
  @data.with(&block)
80
93
  end
81
94
 
95
+ # Fetch the value associated with the key.
96
+ # If a value is found, then it is returned.
97
+ #
98
+ # If a value is not found and no block is given, then nil is returned.
99
+ #
100
+ # If a value is not found (or if the found value is nil and :cache_nils is false)
101
+ # and a block is given, the block will be invoked and its return value
102
+ # written to the cache and returned.
82
103
  def fetch(name, options=nil)
83
104
  options ||= {}
84
- name = expanded_key name
85
-
105
+ options[:cache_nils] = true if @options[:cache_nils]
106
+ namespaced_name = namespaced_key(name, options)
107
+ not_found = options[:cache_nils] ? Dalli::Server::NOT_FOUND : nil
86
108
  if block_given?
109
+ entry = not_found
87
110
  unless options[:force]
88
- entry = instrument(:read, name, options) do |payload|
89
- read_entry(name, options).tap do |result|
111
+ entry = instrument_with_log(:read, namespaced_name, options) do |payload|
112
+ read_entry(namespaced_name, options).tap do |result|
90
113
  if payload
91
114
  payload[:super_operation] = :fetch
92
- payload[:hit] = !!result
115
+ payload[:hit] = not_found != result
93
116
  end
94
117
  end
95
118
  end
96
119
  end
97
120
 
98
- if !entry.nil?
99
- instrument(:fetch_hit, name, options) { |payload| }
100
- entry
101
- else
102
- result = instrument(:generate, name, options) do |payload|
103
- yield
121
+ if not_found == entry
122
+ result = instrument_with_log(:generate, namespaced_name, options) do |payload|
123
+ yield(name)
104
124
  end
105
125
  write(name, result, options)
106
126
  result
127
+ else
128
+ instrument_with_log(:fetch_hit, namespaced_name, options) { |payload| }
129
+ entry
107
130
  end
108
131
  else
109
132
  read(name, options)
@@ -112,20 +135,20 @@ module ActiveSupport
112
135
 
113
136
  def read(name, options=nil)
114
137
  options ||= {}
115
- name = expanded_key name
138
+ name = namespaced_key(name, options)
116
139
 
117
- instrument(:read, name, options) do |payload|
140
+ instrument_with_log(:read, name, options) do |payload|
118
141
  entry = read_entry(name, options)
119
- payload[:hit] = !!entry if payload
142
+ payload[:hit] = !entry.nil? if payload
120
143
  entry
121
144
  end
122
145
  end
123
146
 
124
147
  def write(name, value, options=nil)
125
148
  options ||= {}
126
- name = expanded_key name
149
+ name = namespaced_key(name, options)
127
150
 
128
- instrument(:write, name, options) do |payload|
151
+ instrument_with_log(:write, name, options) do |payload|
129
152
  with do |connection|
130
153
  options = options.merge(:connection => connection)
131
154
  write_entry(name, value, options)
@@ -135,7 +158,7 @@ module ActiveSupport
135
158
 
136
159
  def exist?(name, options=nil)
137
160
  options ||= {}
138
- name = expanded_key name
161
+ name = namespaced_key(name, options)
139
162
 
140
163
  log(:exist, name, options)
141
164
  !read_entry(name, options).nil?
@@ -143,9 +166,9 @@ module ActiveSupport
143
166
 
144
167
  def delete(name, options=nil)
145
168
  options ||= {}
146
- name = expanded_key name
169
+ name = namespaced_key(name, options)
147
170
 
148
- instrument(:delete, name, options) do |payload|
171
+ instrument_with_log(:delete, name, options) do |payload|
149
172
  delete_entry(name, options)
150
173
  end
151
174
  end
@@ -153,12 +176,12 @@ module ActiveSupport
153
176
  # Reads multiple keys from the cache using a single call to the
154
177
  # servers for all keys. Keys must be Strings.
155
178
  def read_multi(*names)
156
- names.extract_options!
157
- mapping = names.inject({}) { |memo, name| memo[expanded_key(name)] = name; memo }
158
- instrument(:read_multi, names) do
179
+ options = names.extract_options!
180
+ mapping = names.inject({}) { |memo, name| memo[namespaced_key(name, options)] = name; memo }
181
+ instrument_with_log(:read_multi, mapping.keys) do
159
182
  results = {}
160
183
  if local_cache
161
- mapping.keys.each do |key|
184
+ mapping.each_key do |key|
162
185
  if value = local_cache.read_entry(key, options)
163
186
  results[key] = value
164
187
  end
@@ -184,9 +207,9 @@ module ActiveSupport
184
207
  # and the result will be written to the cache and returned.
185
208
  def fetch_multi(*names)
186
209
  options = names.extract_options!
187
- mapping = names.inject({}) { |memo, name| memo[expanded_key(name)] = name; memo }
210
+ mapping = names.inject({}) { |memo, name| memo[namespaced_key(name, options)] = name; memo }
188
211
 
189
- instrument(:fetch_multi, names) do
212
+ instrument_with_log(:fetch_multi, mapping.keys) do
190
213
  with do |connection|
191
214
  results = connection.get_multi(mapping.keys)
192
215
 
@@ -214,14 +237,15 @@ module ActiveSupport
214
237
  # memcached counters cannot hold negative values.
215
238
  def increment(name, amount = 1, options=nil)
216
239
  options ||= {}
217
- name = expanded_key name
240
+ name = namespaced_key(name, options)
218
241
  initial = options.has_key?(:initial) ? options[:initial] : amount
219
242
  expires_in = options[:expires_in]
220
- instrument(:increment, name, :amount => amount) do
243
+ instrument_with_log(:increment, name, :amount => amount) do
221
244
  with { |c| c.incr(name, amount, expires_in, initial) }
222
245
  end
223
246
  rescue Dalli::DalliError => e
224
- logger.error("DalliError: #{e.message}") if logger
247
+ log_dalli_error(e)
248
+ instrument_error(e) if instrument_errors?
225
249
  raise if raise_errors?
226
250
  nil
227
251
  end
@@ -233,14 +257,15 @@ module ActiveSupport
233
257
  # memcached counters cannot hold negative values.
234
258
  def decrement(name, amount = 1, options=nil)
235
259
  options ||= {}
236
- name = expanded_key name
260
+ name = namespaced_key(name, options)
237
261
  initial = options.has_key?(:initial) ? options[:initial] : 0
238
262
  expires_in = options[:expires_in]
239
- instrument(:decrement, name, :amount => amount) do
263
+ instrument_with_log(:decrement, name, :amount => amount) do
240
264
  with { |c| c.decr(name, amount, expires_in, initial) }
241
265
  end
242
266
  rescue Dalli::DalliError => e
243
- logger.error("DalliError: #{e.message}") if logger
267
+ log_dalli_error(e)
268
+ instrument_error(e) if instrument_errors?
244
269
  raise if raise_errors?
245
270
  nil
246
271
  end
@@ -248,11 +273,12 @@ module ActiveSupport
248
273
  # Clear the entire cache on all memcached servers. This method should
249
274
  # be used with care when using a shared cache.
250
275
  def clear(options=nil)
251
- instrument(:clear, 'flushing all keys') do
276
+ instrument_with_log(:clear, 'flushing all keys') do
252
277
  with { |c| c.flush_all }
253
278
  end
254
279
  rescue Dalli::DalliError => e
255
- logger.error("DalliError: #{e.message}") if logger
280
+ log_dalli_error(e)
281
+ instrument_error(e) if instrument_errors?
256
282
  raise if raise_errors?
257
283
  nil
258
284
  end
@@ -286,7 +312,8 @@ module ActiveSupport
286
312
  # NB Backwards data compatibility, to be removed at some point
287
313
  entry.is_a?(ActiveSupport::Cache::Entry) ? entry.value : entry
288
314
  rescue Dalli::DalliError => e
289
- logger.error("DalliError: #{e.message}") if logger
315
+ log_dalli_error(e)
316
+ instrument_error(e) if instrument_errors?
290
317
  raise if raise_errors?
291
318
  nil
292
319
  end
@@ -300,7 +327,8 @@ module ActiveSupport
300
327
  connection = options.delete(:connection)
301
328
  connection.send(method, key, value, expires_in, options)
302
329
  rescue Dalli::DalliError => e
303
- logger.error("DalliError: #{e.message}") if logger
330
+ log_dalli_error(e)
331
+ instrument_error(e) if instrument_errors?
304
332
  raise if raise_errors?
305
333
  false
306
334
  end
@@ -309,16 +337,31 @@ module ActiveSupport
309
337
  def delete_entry(key, options) # :nodoc:
310
338
  with { |c| c.delete(key) }
311
339
  rescue Dalli::DalliError => e
312
- logger.error("DalliError: #{e.message}") if logger
340
+ log_dalli_error(e)
341
+ instrument_error(e) if instrument_errors?
313
342
  raise if raise_errors?
314
343
  false
315
344
  end
316
345
 
317
346
  private
318
- # Expand key to be a consistent string value. Invoke +cache_key+ if
319
- # object responds to +cache_key+. Otherwise, to_param method will be
320
- # called. If the key is a Hash, then keys will be sorted alphabetically.
347
+
348
+ def namespaced_key(key, options)
349
+ digest_class = @options[:digest_class] || ::Digest::MD5
350
+ key = expanded_key(key)
351
+ namespace = options[:namespace] if options
352
+ prefix = namespace.is_a?(Proc) ? namespace.call : namespace
353
+ key = "#{prefix}:#{key}" if prefix
354
+ key = "#{key[0, 213]}:md5:#{digest_class.hexdigest(key)}" if key && key.size > 250
355
+ key
356
+ end
357
+ alias :normalize_key :namespaced_key
358
+
359
+ # Expand key to be a consistent string value. Invokes +cache_key_with_version+
360
+ # first to support Rails 5.2 cache versioning.
361
+ # Invoke +cache_key+ if object responds to +cache_key+. Otherwise, to_param method
362
+ # will be called. If the key is a Hash, then keys will be sorted alphabetically.
321
363
  def expanded_key(key) # :nodoc:
364
+ return key.cache_key_with_version.to_s if key.respond_to?(:cache_key_with_version)
322
365
  return key.cache_key.to_s if key.respond_to?(:cache_key)
323
366
 
324
367
  case key
@@ -340,12 +383,26 @@ module ActiveSupport
340
383
  key
341
384
  end
342
385
 
343
- def instrument(operation, key, options=nil)
386
+ def log_dalli_error(error)
387
+ logger.error("DalliError: #{error.message}") if logger
388
+ end
389
+
390
+ def instrument_with_log(operation, key, options=nil)
344
391
  log(operation, key, options)
345
392
 
346
393
  payload = { :key => key }
347
394
  payload.merge!(options) if options.is_a?(Hash)
348
- ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload){ yield(payload) }
395
+ instrument(operation, payload) { |p| yield(p) }
396
+ end
397
+
398
+ def instrument_error(error)
399
+ instrument(:error, { :key => 'DalliError', :message => error.message })
400
+ end
401
+
402
+ def instrument(operation, payload)
403
+ ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload) do
404
+ yield(payload) if block_given?
405
+ end
349
406
  end
350
407
 
351
408
  def log(operation, key, options=nil)
@@ -356,6 +413,29 @@ module ActiveSupport
356
413
  def raise_errors?
357
414
  !!@options[:raise_errors]
358
415
  end
416
+
417
+ def instrument_errors?
418
+ !!@options[:instrument_errors]
419
+ end
420
+
421
+ # Make sure LocalCache is giving raw values, not `Entry`s, and
422
+ # respect `raw` option.
423
+ module LocalCacheEntryUnwrapAndRaw # :nodoc:
424
+ protected
425
+ def read_entry(key, options)
426
+ retval = super(key, **options)
427
+ if retval.is_a? ActiveSupport::Cache::Entry
428
+ # Must have come from LocalStore, unwrap it
429
+ if options[:raw]
430
+ retval.value.to_s
431
+ else
432
+ retval.value
433
+ end
434
+ else
435
+ retval
436
+ end
437
+ end
438
+ end
359
439
  end
360
440
  end
361
441
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'dalli/client'
2
3
 
3
4
  module Dalli
@@ -33,7 +34,7 @@ module Dalli
33
34
 
34
35
  ##
35
36
  # Set the key-value pair, verifying existing CAS.
36
- # Returns the resulting CAS value if succeeded, and false otherwise.
37
+ # Returns the resulting CAS value if succeeded, and falsy otherwise.
37
38
  def set_cas(key, value, cas, ttl=nil, options=nil)
38
39
  ttl ||= @options[:expires_in].to_i
39
40
  perform(:set, key, value, ttl, cas, options)
@@ -42,17 +43,17 @@ module Dalli
42
43
  ##
43
44
  # Conditionally add a key/value pair, verifying existing CAS, only if the
44
45
  # key already exists on the server. Returns the new CAS value if the
45
- # operation succeeded, or false otherwise.
46
+ # operation succeeded, or falsy otherwise.
46
47
  def replace_cas(key, value, cas, ttl=nil, options=nil)
47
48
  ttl ||= @options[:expires_in].to_i
48
49
  perform(:replace, key, value, ttl, cas, options)
49
50
  end
50
51
 
51
52
  # Delete a key/value pair, verifying existing CAS.
52
- # Returns true if succeeded, and false otherwise.
53
+ # Returns true if succeeded, and falsy otherwise.
53
54
  def delete_cas(key, cas=0)
54
55
  perform(:delete, key, cas)
55
56
  end
56
57
 
57
58
  end
58
- end
59
+ end