dalli 2.7.8 → 3.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/{History.md → CHANGELOG.md} +168 -0
  3. data/Gemfile +5 -1
  4. data/README.md +27 -223
  5. data/lib/dalli/cas/client.rb +1 -57
  6. data/lib/dalli/client.rb +227 -254
  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/pipelined_getter.rb +177 -0
  11. data/lib/dalli/protocol/base.rb +241 -0
  12. data/lib/dalli/protocol/binary/request_formatter.rb +117 -0
  13. data/lib/dalli/protocol/binary/response_header.rb +36 -0
  14. data/lib/dalli/protocol/binary/response_processor.rb +239 -0
  15. data/lib/dalli/protocol/binary/sasl_authentication.rb +60 -0
  16. data/lib/dalli/protocol/binary.rb +173 -0
  17. data/lib/dalli/protocol/connection_manager.rb +252 -0
  18. data/lib/dalli/protocol/meta/key_regularizer.rb +31 -0
  19. data/lib/dalli/protocol/meta/request_formatter.rb +121 -0
  20. data/lib/dalli/protocol/meta/response_processor.rb +211 -0
  21. data/lib/dalli/protocol/meta.rb +178 -0
  22. data/lib/dalli/protocol/response_buffer.rb +54 -0
  23. data/lib/dalli/protocol/server_config_parser.rb +86 -0
  24. data/lib/dalli/protocol/ttl_sanitizer.rb +45 -0
  25. data/lib/dalli/protocol/value_compressor.rb +85 -0
  26. data/lib/dalli/protocol/value_marshaller.rb +59 -0
  27. data/lib/dalli/protocol/value_serializer.rb +91 -0
  28. data/lib/dalli/protocol.rb +8 -0
  29. data/lib/dalli/ring.rb +94 -83
  30. data/lib/dalli/server.rb +3 -746
  31. data/lib/dalli/servers_arg_normalizer.rb +54 -0
  32. data/lib/dalli/socket.rb +117 -137
  33. data/lib/dalli/version.rb +4 -1
  34. data/lib/dalli.rb +43 -15
  35. data/lib/rack/session/dalli.rb +103 -94
  36. metadata +65 -28
  37. data/lib/action_dispatch/middleware/session/dalli_store.rb +0 -82
  38. data/lib/active_support/cache/dalli_store.rb +0 -429
  39. 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
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