dalli 2.7.10 → 3.0.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.

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