dalli 2.7.2 → 3.0.0
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 +5 -5
- data/Gemfile +5 -7
- data/History.md +107 -0
- data/LICENSE +1 -1
- data/README.md +53 -113
- data/lib/dalli/cas/client.rb +1 -58
- data/lib/dalli/client.rb +235 -133
- data/lib/dalli/compressor.rb +5 -3
- data/lib/dalli/options.rb +4 -4
- data/lib/dalli/protocol/binary.rb +753 -0
- data/lib/dalli/protocol.rb +9 -0
- data/lib/dalli/ring.rb +17 -68
- data/lib/dalli/server.rb +3 -693
- data/lib/dalli/socket.rb +72 -85
- data/lib/dalli/version.rb +3 -1
- data/lib/dalli.rb +17 -16
- data/lib/rack/session/dalli.rb +123 -31
- metadata +24 -59
- data/Performance.md +0 -42
- data/Rakefile +0 -42
- data/dalli.gemspec +0 -29
- data/lib/action_dispatch/middleware/session/dalli_store.rb +0 -81
- data/lib/active_support/cache/dalli_store.rb +0 -363
- data/lib/dalli/railtie.rb +0 -7
- data/test/benchmark_test.rb +0 -242
- data/test/helper.rb +0 -55
- data/test/memcached_mock.rb +0 -121
- data/test/sasldb +0 -1
- data/test/test_active_support.rb +0 -439
- data/test/test_cas_client.rb +0 -107
- data/test/test_compressor.rb +0 -53
- data/test/test_dalli.rb +0 -625
- data/test/test_encoding.rb +0 -32
- data/test/test_failover.rb +0 -128
- data/test/test_network.rb +0 -54
- data/test/test_rack_session.rb +0 -341
- data/test/test_ring.rb +0 -85
- data/test/test_sasl.rb +0 -110
- data/test/test_serializer.rb +0 -30
- data/test/test_server.rb +0 -80
data/dalli.gemspec
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
require './lib/dalli/version'
|
2
|
-
|
3
|
-
Gem::Specification.new do |s|
|
4
|
-
s.name = %q{dalli}
|
5
|
-
s.version = Dalli::VERSION
|
6
|
-
s.license = "MIT"
|
7
|
-
|
8
|
-
s.authors = ["Mike Perham"]
|
9
|
-
s.description = %q{High performance memcached client for Ruby}
|
10
|
-
s.email = %q{mperham@gmail.com}
|
11
|
-
s.files = Dir.glob("lib/**/*") + [
|
12
|
-
"LICENSE",
|
13
|
-
"README.md",
|
14
|
-
"History.md",
|
15
|
-
"Rakefile",
|
16
|
-
"Gemfile",
|
17
|
-
"dalli.gemspec",
|
18
|
-
"Performance.md",
|
19
|
-
]
|
20
|
-
s.homepage = %q{http://github.com/mperham/dalli}
|
21
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
22
|
-
s.require_paths = ["lib"]
|
23
|
-
s.summary = %q{High performance memcached client for Ruby}
|
24
|
-
s.test_files = Dir.glob("test/**/*")
|
25
|
-
s.add_development_dependency(%q<minitest>, [">= 4.2.0"])
|
26
|
-
s.add_development_dependency(%q<mocha>, [">= 0"])
|
27
|
-
s.add_development_dependency(%q<rails>, ["~> 4"])
|
28
|
-
end
|
29
|
-
|
@@ -1,81 +0,0 @@
|
|
1
|
-
require 'active_support/cache'
|
2
|
-
require 'action_dispatch/middleware/session/abstract_store'
|
3
|
-
require 'dalli'
|
4
|
-
|
5
|
-
# Dalli-based session store for Rails 3.0.
|
6
|
-
module ActionDispatch
|
7
|
-
module Session
|
8
|
-
class DalliStore < AbstractStore
|
9
|
-
def initialize(app, options = {})
|
10
|
-
# Support old :expires option
|
11
|
-
options[:expire_after] ||= options[:expires]
|
12
|
-
|
13
|
-
super
|
14
|
-
|
15
|
-
@default_options = { :namespace => 'rack:session' }.merge(@default_options)
|
16
|
-
|
17
|
-
@pool = options[:cache] || begin
|
18
|
-
Dalli::Client.new(
|
19
|
-
@default_options[:memcache_server], @default_options)
|
20
|
-
end
|
21
|
-
@namespace = @default_options[:namespace]
|
22
|
-
|
23
|
-
@raise_errors = !!@default_options[:raise_errors]
|
24
|
-
|
25
|
-
super
|
26
|
-
end
|
27
|
-
|
28
|
-
def reset
|
29
|
-
@pool.reset
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
def get_session(env, sid)
|
35
|
-
sid = generate_sid unless sid and !sid.empty?
|
36
|
-
begin
|
37
|
-
session = @pool.get(sid) || {}
|
38
|
-
rescue Dalli::DalliError => ex
|
39
|
-
# re-raise ArgumentError so Rails' session abstract_store.rb can autoload any missing models
|
40
|
-
raise ArgumentError, ex.message if ex.message =~ /unmarshal/
|
41
|
-
Rails.logger.warn("Session::DalliStore#get: #{ex.message}")
|
42
|
-
session = {}
|
43
|
-
end
|
44
|
-
[sid, session]
|
45
|
-
end
|
46
|
-
|
47
|
-
def set_session(env, sid, session_data, options = nil)
|
48
|
-
options ||= env[ENV_SESSION_OPTIONS_KEY]
|
49
|
-
expiry = options[:expire_after]
|
50
|
-
@pool.set(sid, session_data, expiry)
|
51
|
-
sid
|
52
|
-
rescue Dalli::DalliError
|
53
|
-
Rails.logger.warn("Session::DalliStore#set: #{$!.message}")
|
54
|
-
raise if @raise_errors
|
55
|
-
false
|
56
|
-
end
|
57
|
-
|
58
|
-
def destroy_session(env, session_id, options)
|
59
|
-
begin
|
60
|
-
@pool.delete(session_id)
|
61
|
-
rescue Dalli::DalliError
|
62
|
-
Rails.logger.warn("Session::DalliStore#destroy_session: #{$!.message}")
|
63
|
-
raise if @raise_errors
|
64
|
-
end
|
65
|
-
return nil if options[:drop]
|
66
|
-
generate_sid
|
67
|
-
end
|
68
|
-
|
69
|
-
def destroy(env)
|
70
|
-
if sid = current_session_id(env)
|
71
|
-
@pool.delete(sid)
|
72
|
-
end
|
73
|
-
rescue Dalli::DalliError
|
74
|
-
Rails.logger.warn("Session::DalliStore#destroy: #{$!.message}")
|
75
|
-
raise if @raise_errors
|
76
|
-
false
|
77
|
-
end
|
78
|
-
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
@@ -1,363 +0,0 @@
|
|
1
|
-
# encoding: ascii
|
2
|
-
require 'dalli'
|
3
|
-
|
4
|
-
module ActiveSupport
|
5
|
-
module Cache
|
6
|
-
class DalliStore
|
7
|
-
|
8
|
-
attr_reader :silence, :options
|
9
|
-
alias_method :silence?, :silence
|
10
|
-
|
11
|
-
# Silence the logger.
|
12
|
-
def silence!
|
13
|
-
@silence = true
|
14
|
-
self
|
15
|
-
end
|
16
|
-
|
17
|
-
# Silence the logger within a block.
|
18
|
-
def mute
|
19
|
-
previous_silence, @silence = defined?(@silence) && @silence, true
|
20
|
-
yield
|
21
|
-
ensure
|
22
|
-
@silence = previous_silence
|
23
|
-
end
|
24
|
-
|
25
|
-
ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/
|
26
|
-
|
27
|
-
# Creates a new DalliStore object, with the given memcached server
|
28
|
-
# addresses. Each address is either a host name, or a host-with-port string
|
29
|
-
# in the form of "host_name:port". For example:
|
30
|
-
#
|
31
|
-
# ActiveSupport::Cache::DalliStore.new("localhost", "server-downstairs.localnetwork:8229")
|
32
|
-
#
|
33
|
-
# If no addresses are specified, then DalliStore will connect to
|
34
|
-
# localhost port 11211 (the default memcached port).
|
35
|
-
#
|
36
|
-
# Connection Pool support
|
37
|
-
#
|
38
|
-
# If you are using multithreaded Rails, the Rails.cache singleton can become a source
|
39
|
-
# of contention. You can use a connection pool of Dalli clients with Rails.cache by
|
40
|
-
# passing :pool_size and/or :pool_timeout:
|
41
|
-
#
|
42
|
-
# config.cache_store = :dalli_store, 'localhost:11211', :pool_size => 10
|
43
|
-
#
|
44
|
-
# Both pool options default to 5. You must include the `connection_pool` gem if you
|
45
|
-
# wish to use pool support.
|
46
|
-
#
|
47
|
-
def initialize(*addresses)
|
48
|
-
addresses = addresses.flatten
|
49
|
-
options = addresses.extract_options!
|
50
|
-
@options = options.dup
|
51
|
-
|
52
|
-
pool_options = {}
|
53
|
-
pool_options[:size] = options[:pool_size] if options[:pool_size]
|
54
|
-
pool_options[:timeout] = options[:pool_timeout] if options[:pool_timeout]
|
55
|
-
|
56
|
-
@options[:compress] ||= @options[:compression]
|
57
|
-
|
58
|
-
addresses.compact!
|
59
|
-
servers = if addresses.empty?
|
60
|
-
nil # use the default from Dalli::Client
|
61
|
-
else
|
62
|
-
addresses
|
63
|
-
end
|
64
|
-
if pool_options.empty?
|
65
|
-
@data = Dalli::Client.new(servers, @options)
|
66
|
-
else
|
67
|
-
@data = ::ConnectionPool.new(pool_options) { Dalli::Client.new(servers, @options.merge(:threadsafe => false)) }
|
68
|
-
end
|
69
|
-
|
70
|
-
extend Strategy::LocalCache
|
71
|
-
end
|
72
|
-
|
73
|
-
##
|
74
|
-
# Access the underlying Dalli::Client or ConnectionPool instance for
|
75
|
-
# access to get_multi, etc.
|
76
|
-
def dalli
|
77
|
-
@data
|
78
|
-
end
|
79
|
-
|
80
|
-
def with(&block)
|
81
|
-
@data.with(&block)
|
82
|
-
end
|
83
|
-
|
84
|
-
def fetch(name, options=nil)
|
85
|
-
options ||= {}
|
86
|
-
name = expanded_key name
|
87
|
-
|
88
|
-
if block_given?
|
89
|
-
unless options[:force]
|
90
|
-
entry = instrument(:read, name, options) do |payload|
|
91
|
-
read_entry(name, options).tap do |result|
|
92
|
-
if payload
|
93
|
-
payload[:super_operation] = :fetch
|
94
|
-
payload[:hit] = !!result
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
if !entry.nil?
|
101
|
-
instrument(:fetch_hit, name, options) { |payload| }
|
102
|
-
entry
|
103
|
-
else
|
104
|
-
result = instrument(:generate, name, options) do |payload|
|
105
|
-
yield
|
106
|
-
end
|
107
|
-
write(name, result, options)
|
108
|
-
result
|
109
|
-
end
|
110
|
-
else
|
111
|
-
read(name, options)
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
def read(name, options=nil)
|
116
|
-
options ||= {}
|
117
|
-
name = expanded_key name
|
118
|
-
|
119
|
-
instrument(:read, name, options) do |payload|
|
120
|
-
entry = read_entry(name, options)
|
121
|
-
payload[:hit] = !!entry if payload
|
122
|
-
entry
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
def write(name, value, options=nil)
|
127
|
-
options ||= {}
|
128
|
-
name = expanded_key name
|
129
|
-
|
130
|
-
instrument(:write, name, options) do |payload|
|
131
|
-
with do |connection|
|
132
|
-
options = options.merge(:connection => connection)
|
133
|
-
write_entry(name, value, options)
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
def exist?(name, options=nil)
|
139
|
-
options ||= {}
|
140
|
-
name = expanded_key name
|
141
|
-
|
142
|
-
log(:exist, name, options)
|
143
|
-
!read_entry(name, options).nil?
|
144
|
-
end
|
145
|
-
|
146
|
-
def delete(name, options=nil)
|
147
|
-
options ||= {}
|
148
|
-
name = expanded_key name
|
149
|
-
|
150
|
-
instrument(:delete, name, options) do |payload|
|
151
|
-
delete_entry(name, options)
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
# Reads multiple keys from the cache using a single call to the
|
156
|
-
# servers for all keys. Keys must be Strings.
|
157
|
-
def read_multi(*names)
|
158
|
-
names.extract_options!
|
159
|
-
mapping = names.inject({}) { |memo, name| memo[expanded_key(name)] = name; memo }
|
160
|
-
instrument(:read_multi, names) do
|
161
|
-
results = {}
|
162
|
-
if local_cache
|
163
|
-
mapping.keys.each do |key|
|
164
|
-
if value = local_cache.read_entry(key, options)
|
165
|
-
results[key] = value
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
data = with { |c| c.get_multi(mapping.keys - results.keys) }
|
171
|
-
results.merge!(data)
|
172
|
-
results.inject({}) do |memo, (inner, _)|
|
173
|
-
entry = results[inner]
|
174
|
-
# NB Backwards data compatibility, to be removed at some point
|
175
|
-
value = (entry.is_a?(ActiveSupport::Cache::Entry) ? entry.value : entry)
|
176
|
-
memo[mapping[inner]] = value
|
177
|
-
local_cache.write_entry(inner, value, options) if local_cache
|
178
|
-
memo
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
# Fetches data from the cache, using the given keys. If there is data in
|
184
|
-
# the cache with the given keys, then that data is returned. Otherwise,
|
185
|
-
# the supplied block is called for each key for which there was no data,
|
186
|
-
# and the result will be written to the cache and returned.
|
187
|
-
def fetch_multi(*names)
|
188
|
-
options = names.extract_options!
|
189
|
-
mapping = names.inject({}) { |memo, name| memo[expanded_key(name)] = name; memo }
|
190
|
-
|
191
|
-
instrument(:fetch_multi, names) do
|
192
|
-
with do |connection|
|
193
|
-
results = connection.get_multi(mapping.keys)
|
194
|
-
|
195
|
-
connection.multi do
|
196
|
-
mapping.inject({}) do |memo, (expanded, name)|
|
197
|
-
memo[name] = results[expanded]
|
198
|
-
if memo[name].nil?
|
199
|
-
value = yield(name)
|
200
|
-
memo[name] = value
|
201
|
-
options = options.merge(:connection => connection)
|
202
|
-
write_entry(expanded, value, options)
|
203
|
-
end
|
204
|
-
|
205
|
-
memo
|
206
|
-
end
|
207
|
-
end
|
208
|
-
end
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
# Increment a cached value. This method uses the memcached incr atomic
|
213
|
-
# operator and can only be used on values written with the :raw option.
|
214
|
-
# Calling it on a value not stored with :raw will fail.
|
215
|
-
# :initial defaults to the amount passed in, as if the counter was initially zero.
|
216
|
-
# memcached counters cannot hold negative values.
|
217
|
-
def increment(name, amount = 1, options=nil)
|
218
|
-
options ||= {}
|
219
|
-
name = expanded_key name
|
220
|
-
initial = options.has_key?(:initial) ? options[:initial] : amount
|
221
|
-
expires_in = options[:expires_in]
|
222
|
-
instrument(:increment, name, :amount => amount) do
|
223
|
-
with { |c| c.incr(name, amount, expires_in, initial) }
|
224
|
-
end
|
225
|
-
rescue Dalli::DalliError => e
|
226
|
-
logger.error("DalliError: #{e.message}") if logger
|
227
|
-
raise if raise_errors?
|
228
|
-
nil
|
229
|
-
end
|
230
|
-
|
231
|
-
# Decrement a cached value. This method uses the memcached decr atomic
|
232
|
-
# operator and can only be used on values written with the :raw option.
|
233
|
-
# Calling it on a value not stored with :raw will fail.
|
234
|
-
# :initial defaults to zero, as if the counter was initially zero.
|
235
|
-
# memcached counters cannot hold negative values.
|
236
|
-
def decrement(name, amount = 1, options=nil)
|
237
|
-
options ||= {}
|
238
|
-
name = expanded_key name
|
239
|
-
initial = options.has_key?(:initial) ? options[:initial] : 0
|
240
|
-
expires_in = options[:expires_in]
|
241
|
-
instrument(:decrement, name, :amount => amount) do
|
242
|
-
with { |c| c.decr(name, amount, expires_in, initial) }
|
243
|
-
end
|
244
|
-
rescue Dalli::DalliError => e
|
245
|
-
logger.error("DalliError: #{e.message}") if logger
|
246
|
-
raise if raise_errors?
|
247
|
-
nil
|
248
|
-
end
|
249
|
-
|
250
|
-
# Clear the entire cache on all memcached servers. This method should
|
251
|
-
# be used with care when using a shared cache.
|
252
|
-
def clear(options=nil)
|
253
|
-
instrument(:clear, 'flushing all keys') do
|
254
|
-
with { |c| c.flush_all }
|
255
|
-
end
|
256
|
-
rescue Dalli::DalliError => e
|
257
|
-
logger.error("DalliError: #{e.message}") if logger
|
258
|
-
raise if raise_errors?
|
259
|
-
nil
|
260
|
-
end
|
261
|
-
|
262
|
-
# Clear any local cache
|
263
|
-
def cleanup(options=nil)
|
264
|
-
end
|
265
|
-
|
266
|
-
# Get the statistics from the memcached servers.
|
267
|
-
def stats
|
268
|
-
with { |c| c.stats }
|
269
|
-
end
|
270
|
-
|
271
|
-
def reset
|
272
|
-
with { |c| c.reset }
|
273
|
-
end
|
274
|
-
|
275
|
-
def logger
|
276
|
-
Dalli.logger
|
277
|
-
end
|
278
|
-
|
279
|
-
def logger=(new_logger)
|
280
|
-
Dalli.logger = new_logger
|
281
|
-
end
|
282
|
-
|
283
|
-
protected
|
284
|
-
|
285
|
-
# Read an entry from the cache.
|
286
|
-
def read_entry(key, options) # :nodoc:
|
287
|
-
entry = with { |c| c.get(key, options) }
|
288
|
-
# NB Backwards data compatibility, to be removed at some point
|
289
|
-
entry.is_a?(ActiveSupport::Cache::Entry) ? entry.value : entry
|
290
|
-
rescue Dalli::DalliError => e
|
291
|
-
logger.error("DalliError: #{e.message}") if logger
|
292
|
-
raise if raise_errors?
|
293
|
-
nil
|
294
|
-
end
|
295
|
-
|
296
|
-
# Write an entry to the cache.
|
297
|
-
def write_entry(key, value, options) # :nodoc:
|
298
|
-
# cleanup LocalCache
|
299
|
-
cleanup if options[:unless_exist]
|
300
|
-
method = options[:unless_exist] ? :add : :set
|
301
|
-
expires_in = options[:expires_in]
|
302
|
-
connection = options.delete(:connection)
|
303
|
-
connection.send(method, key, value, expires_in, options)
|
304
|
-
rescue Dalli::DalliError => e
|
305
|
-
logger.error("DalliError: #{e.message}") if logger
|
306
|
-
raise if raise_errors?
|
307
|
-
false
|
308
|
-
end
|
309
|
-
|
310
|
-
# Delete an entry from the cache.
|
311
|
-
def delete_entry(key, options) # :nodoc:
|
312
|
-
with { |c| c.delete(key) }
|
313
|
-
rescue Dalli::DalliError => e
|
314
|
-
logger.error("DalliError: #{e.message}") if logger
|
315
|
-
raise if raise_errors?
|
316
|
-
false
|
317
|
-
end
|
318
|
-
|
319
|
-
private
|
320
|
-
# Expand key to be a consistent string value. Invoke +cache_key+ if
|
321
|
-
# object responds to +cache_key+. Otherwise, to_param method will be
|
322
|
-
# called. If the key is a Hash, then keys will be sorted alphabetically.
|
323
|
-
def expanded_key(key) # :nodoc:
|
324
|
-
return key.cache_key.to_s if key.respond_to?(:cache_key)
|
325
|
-
|
326
|
-
case key
|
327
|
-
when Array
|
328
|
-
if key.size > 1
|
329
|
-
key = key.collect{|element| expanded_key(element)}
|
330
|
-
else
|
331
|
-
key = key.first
|
332
|
-
end
|
333
|
-
when Hash
|
334
|
-
key = key.sort_by { |k,_| k.to_s }.collect{|k,v| "#{k}=#{v}"}
|
335
|
-
end
|
336
|
-
|
337
|
-
key = key.to_param
|
338
|
-
if key.respond_to? :force_encoding
|
339
|
-
key = key.dup
|
340
|
-
key.force_encoding('binary')
|
341
|
-
end
|
342
|
-
key
|
343
|
-
end
|
344
|
-
|
345
|
-
def instrument(operation, key, options=nil)
|
346
|
-
log(operation, key, options)
|
347
|
-
|
348
|
-
payload = { :key => key }
|
349
|
-
payload.merge!(options) if options.is_a?(Hash)
|
350
|
-
ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload){ yield(payload) }
|
351
|
-
end
|
352
|
-
|
353
|
-
def log(operation, key, options=nil)
|
354
|
-
return unless logger && logger.debug? && !silence?
|
355
|
-
logger.debug("Cache #{operation}: #{key}#{options.blank? ? "" : " (#{options.inspect})"}")
|
356
|
-
end
|
357
|
-
|
358
|
-
def raise_errors?
|
359
|
-
!!@options[:raise_errors]
|
360
|
-
end
|
361
|
-
end
|
362
|
-
end
|
363
|
-
end
|