dalli 2.7.9 → 3.2.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/{History.md → CHANGELOG.md} +180 -0
  3. data/Gemfile +17 -1
  4. data/README.md +30 -221
  5. data/lib/dalli/cas/client.rb +1 -57
  6. data/lib/dalli/client.rb +226 -258
  7. data/lib/dalli/compressor.rb +12 -2
  8. data/lib/dalli/key_manager.rb +121 -0
  9. data/lib/dalli/options.rb +6 -7
  10. data/lib/dalli/pid_cache.rb +40 -0
  11. data/lib/dalli/pipelined_getter.rb +177 -0
  12. data/lib/dalli/protocol/base.rb +250 -0
  13. data/lib/dalli/protocol/binary/request_formatter.rb +117 -0
  14. data/lib/dalli/protocol/binary/response_header.rb +36 -0
  15. data/lib/dalli/protocol/binary/response_processor.rb +239 -0
  16. data/lib/dalli/protocol/binary/sasl_authentication.rb +60 -0
  17. data/lib/dalli/protocol/binary.rb +173 -0
  18. data/lib/dalli/protocol/connection_manager.rb +255 -0
  19. data/lib/dalli/protocol/meta/key_regularizer.rb +31 -0
  20. data/lib/dalli/protocol/meta/request_formatter.rb +121 -0
  21. data/lib/dalli/protocol/meta/response_processor.rb +211 -0
  22. data/lib/dalli/protocol/meta.rb +178 -0
  23. data/lib/dalli/protocol/response_buffer.rb +54 -0
  24. data/lib/dalli/protocol/server_config_parser.rb +86 -0
  25. data/lib/dalli/protocol/ttl_sanitizer.rb +45 -0
  26. data/lib/dalli/protocol/value_compressor.rb +85 -0
  27. data/lib/dalli/protocol/value_marshaller.rb +59 -0
  28. data/lib/dalli/protocol/value_serializer.rb +91 -0
  29. data/lib/dalli/protocol.rb +19 -0
  30. data/lib/dalli/ring.rb +94 -83
  31. data/lib/dalli/server.rb +3 -746
  32. data/lib/dalli/servers_arg_normalizer.rb +54 -0
  33. data/lib/dalli/socket.rb +117 -137
  34. data/lib/dalli/version.rb +4 -1
  35. data/lib/dalli.rb +43 -15
  36. data/lib/rack/session/dalli.rb +103 -94
  37. metadata +34 -128
  38. data/lib/action_dispatch/middleware/session/dalli_store.rb +0 -82
  39. data/lib/active_support/cache/dalli_store.rb +0 -432
  40. data/lib/dalli/railtie.rb +0 -8
@@ -1,432 +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(name)
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. Invokes +cache_key_with_version+
350
- # first to support Rails 5.2 cache versioning.
351
- # Invoke +cache_key+ if object responds to +cache_key+. Otherwise, to_param method
352
- # will be called. If the key is a Hash, then keys will be sorted alphabetically.
353
- def expanded_key(key) # :nodoc:
354
- return key.cache_key_with_version.to_s if key.respond_to?(:cache_key_with_version)
355
- return key.cache_key.to_s if key.respond_to?(:cache_key)
356
- original_object_id = key.object_id
357
-
358
- case key
359
- when Array
360
- if key.size > 1
361
- key = key.collect{|element| expanded_key(element)}
362
- else
363
- key = key.first
364
- end
365
- when Hash
366
- key = key.sort_by { |k,_| k.to_s }.collect{|k,v| "#{k}=#{v}"}
367
- end
368
-
369
- key = key.to_param
370
- if key.respond_to? :force_encoding
371
- key = key.dup if key.object_id == original_object_id
372
- key.force_encoding('binary')
373
- end
374
- key
375
- end
376
-
377
- def log_dalli_error(error)
378
- logger.error("DalliError: #{error.message}") if logger
379
- end
380
-
381
- def instrument_with_log(operation, key, options=nil)
382
- log(operation, key, options)
383
-
384
- payload = { :key => key }
385
- payload.merge!(options) if options.is_a?(Hash)
386
- instrument(operation, payload) { |p| yield(p) }
387
- end
388
-
389
- def instrument_error(error)
390
- instrument(:error, { :key => 'DalliError', :message => error.message })
391
- end
392
-
393
- def instrument(operation, payload)
394
- ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload) do
395
- yield(payload) if block_given?
396
- end
397
- end
398
-
399
- def log(operation, key, options=nil)
400
- return unless logger && logger.debug? && !silence?
401
- logger.debug("Cache #{operation}: #{key}#{options.blank? ? "" : " (#{options.inspect})"}")
402
- end
403
-
404
- def raise_errors?
405
- !!@options[:raise_errors]
406
- end
407
-
408
- def instrument_errors?
409
- !!@options[:instrument_errors]
410
- end
411
-
412
- # Make sure LocalCache is giving raw values, not `Entry`s, and
413
- # respect `raw` option.
414
- module LocalCacheEntryUnwrapAndRaw # :nodoc:
415
- protected
416
- def read_entry(key, options)
417
- retval = super
418
- if retval.is_a? ActiveSupport::Cache::Entry
419
- # Must have come from LocalStore, unwrap it
420
- if options[:raw]
421
- retval.value.to_s
422
- else
423
- retval.value
424
- end
425
- else
426
- retval
427
- end
428
- end
429
- end
430
- end
431
- end
432
- 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