dalli 2.7.6 → 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.
- checksums.yaml +5 -5
- data/Gemfile +10 -0
- data/History.md +38 -0
- data/README.md +18 -30
- data/lib/action_dispatch/middleware/session/dalli_store.rb +1 -0
- data/lib/active_support/cache/dalli_store.rb +64 -26
- data/lib/dalli.rb +3 -0
- data/lib/dalli/cas/client.rb +1 -0
- data/lib/dalli/client.rb +21 -8
- data/lib/dalli/compressor.rb +2 -1
- data/lib/dalli/options.rb +1 -0
- data/lib/dalli/railtie.rb +1 -0
- data/lib/dalli/ring.rb +3 -3
- data/lib/dalli/server.rb +21 -14
- data/lib/dalli/socket.rb +13 -5
- data/lib/dalli/version.rb +2 -1
- data/lib/rack/session/dalli.rb +135 -38
- metadata +9 -122
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4bc4fe873fe9319c9297219b7908d576470551d0e011d1299e54ebc162256a68
|
4
|
+
data.tar.gz: b60393783d05152ad67aa12298f6867127f973fbf07cd2d0dc90ab96b1d7971f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1348b528243f253c111ad74e17a0ba20d8d14eb29d4f41e15f60bb2c87505c6d09404217c868f1623bd68f1ad70a644efd09fd1acca0ba2a1d0b6395387967ae
|
7
|
+
data.tar.gz: 108c5477c6399ef838a8df14e4878509ec5f7c22c33d012c19a9deb635901e9251a52c392c080fd6eb65dfb54e8084ee70cf6668a6914fd1336120aabb19b4f9
|
data/Gemfile
CHANGED
data/History.md
CHANGED
@@ -1,6 +1,44 @@
|
|
1
1
|
Dalli Changelog
|
2
2
|
=====================
|
3
3
|
|
4
|
+
2.7.11
|
5
|
+
==========
|
6
|
+
- DEPRECATION: :dalli_store will be removed in Dalli 3.0.
|
7
|
+
Use Rails' official :mem_cache_store instead.
|
8
|
+
https://guides.rubyonrails.org/caching_with_rails.html
|
9
|
+
- Add new `digest_class` option to Dalli::Client [#724]
|
10
|
+
- Don't treat NameError as a network error [#728]
|
11
|
+
- Handle nested comma separated server strings (sambostock)
|
12
|
+
|
13
|
+
2.7.10
|
14
|
+
==========
|
15
|
+
- Revert frozen string change (schneems)
|
16
|
+
- Advertise supports_cached_versioning? in DalliStore (schneems)
|
17
|
+
- Better detection of fork support, to allow specs to run under Truffle Ruby (deepj)
|
18
|
+
- Update logging for over max size to log as error (aeroastro)
|
19
|
+
|
20
|
+
2.7.9
|
21
|
+
==========
|
22
|
+
- Fix behavior for Rails 5.2+ cache_versioning (GriwMF)
|
23
|
+
- Ensure fetch provides the key to the fallback block as an argument (0exp)
|
24
|
+
- Assorted performance improvements (schneems)
|
25
|
+
|
26
|
+
2.7.8
|
27
|
+
==========
|
28
|
+
- Rails 5.2 compatibility (pbougie)
|
29
|
+
- Fix Session Cache compatibility (pixeltrix)
|
30
|
+
|
31
|
+
2.7.7
|
32
|
+
==========
|
33
|
+
- Support large cache keys on fetch multi (sobrinho)
|
34
|
+
- Not found checks no longer trigger the result's equality method (dannyfallon)
|
35
|
+
- Use SVG build badges (olleolleolle)
|
36
|
+
- Travis updates (junaruga, tiarly, petergoldstein)
|
37
|
+
- Update default down_retry_delay (jaredhales)
|
38
|
+
- Close kgio socket after IO.select timeouts
|
39
|
+
- Documentation updates (tipair)
|
40
|
+
- Instrument DalliStore errors with instrument_errors configuration option. (btatnall)
|
41
|
+
|
4
42
|
2.7.6
|
5
43
|
==========
|
6
44
|
- Rails 5.0.0.beta2 compatibility (yui-knk, petergoldstein)
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Dalli [![Build Status](https://secure.travis-ci.org/petergoldstein/dalli.
|
1
|
+
Dalli [![Build Status](https://secure.travis-ci.org/petergoldstein/dalli.svg)](http://travis-ci.org/petergoldstein/dalli)
|
2
2
|
=====
|
3
3
|
|
4
4
|
Dalli is a high performance pure Ruby client for accessing memcached servers. It works with memcached 1.4+ only as it uses the newer binary protocol. It should be considered a replacement for the memcache-client gem.
|
@@ -9,26 +9,6 @@ The name is a variant of Salvador Dali for his famous painting [The Persistence
|
|
9
9
|
|
10
10
|
Dalli's initial development was sponsored by [CouchBase](http://www.couchbase.com/). Many thanks to them!
|
11
11
|
|
12
|
-
|
13
|
-
Design
|
14
|
-
------------
|
15
|
-
|
16
|
-
Mike Perham decided to write Dalli after maintaining memcache-client for two years for a few specific reasons:
|
17
|
-
|
18
|
-
0. The code is mostly old and gross. The bulk of the code is a single 1000 line .rb file.
|
19
|
-
1. It has a lot of options that are infrequently used which complicate the codebase.
|
20
|
-
2. The implementation has no single point to attach monitoring hooks.
|
21
|
-
3. Uses the old text protocol, which hurts raw performance.
|
22
|
-
|
23
|
-
So a few notes. Dalli:
|
24
|
-
|
25
|
-
0. uses the exact same algorithm to choose a server so existing memcached clusters with TBs of data will work identically to memcache-client.
|
26
|
-
1. is approximately 20% faster than memcache-client (which itself was heavily optimized) in Ruby 1.9.2.
|
27
|
-
2. contains explicit "chokepoint" methods which handle all requests; these can be hooked into by monitoring tools (NewRelic, Rack::Bug, etc) to track memcached usage.
|
28
|
-
3. supports SASL for use in managed environments, e.g. Heroku.
|
29
|
-
4. provides proper failover with recovery and adjustable timeouts
|
30
|
-
|
31
|
-
|
32
12
|
Supported Ruby versions and implementations
|
33
13
|
------------------------------------------------
|
34
14
|
|
@@ -66,11 +46,9 @@ dc.set('abc', 123)
|
|
66
46
|
value = dc.get('abc')
|
67
47
|
```
|
68
48
|
|
69
|
-
The test suite requires memcached 1.4.3+ with SASL enabled (brew install memcached --enable-sasl ; mv /usr/bin/memcached /usr/bin/memcached.old). Currently only supports the PLAIN mechanism.
|
70
|
-
|
71
|
-
Dalli has no runtime dependencies and never will. You can optionally install the 'kgio' gem to
|
72
|
-
give Dalli a 20-30% performance boost.
|
49
|
+
The test suite requires memcached 1.4.3+ with SASL enabled (`brew install memcached --enable-sasl ; mv /usr/bin/memcached /usr/bin/memcached.old`). Currently only supports the PLAIN mechanism.
|
73
50
|
|
51
|
+
Dalli has no runtime dependencies.
|
74
52
|
|
75
53
|
Usage with Rails 3.x and 4.x
|
76
54
|
---------------------------
|
@@ -90,10 +68,12 @@ config.cache_store = :dalli_store
|
|
90
68
|
Here's a more comprehensive example that sets a reasonable default for maximum cache entry lifetime (one day), enables compression for large values and namespaces all entries for this rails app. Remove the namespace if you have multiple apps which share cached values.
|
91
69
|
|
92
70
|
```ruby
|
93
|
-
config.cache_store = :dalli_store, 'cache-1.example.com', 'cache-2.example.com',
|
71
|
+
config.cache_store = :dalli_store, 'cache-1.example.com', 'cache-2.example.com:11211:2',
|
94
72
|
{ :namespace => NAME_OF_RAILS_APP, :expires_in => 1.day, :compress => true }
|
95
73
|
```
|
96
74
|
|
75
|
+
You can specify a port and a weight by appending to the server name. You may wish to increase the weight of a server with more memory configured. (e.g. to specify port 11211 and a weight of 2, append `:11211:2` )
|
76
|
+
|
97
77
|
If your servers are specified in `ENV["MEMCACHE_SERVERS"]` (e.g. on Heroku when using a third-party hosted addon), simply provide `nil` for the servers:
|
98
78
|
|
99
79
|
```ruby
|
@@ -147,6 +127,8 @@ end
|
|
147
127
|
Configuration
|
148
128
|
------------------------
|
149
129
|
|
130
|
+
**servers**: An Array of "host:port:weight" where weight allows you to distribute cache unevenly.
|
131
|
+
|
150
132
|
Dalli::Client accepts the following options. All times are in seconds.
|
151
133
|
|
152
134
|
**expires_in**: Global default for key TTL. Default is 0, which means no expiry.
|
@@ -155,7 +137,7 @@ Dalli::Client accepts the following options. All times are in seconds.
|
|
155
137
|
|
156
138
|
**failover**: Boolean, if true Dalli will failover to another server if the main server for a key is down. Default is true.
|
157
139
|
|
158
|
-
**threadsafe**: Boolean. If true Dalli ensures that only one thread is using a socket at a given time.
|
140
|
+
**threadsafe**: Boolean. If true Dalli ensures that only one thread is using a socket at a given time. Default is true. You can set to false if you are using the Client within a thread-safe connection pool.
|
159
141
|
|
160
142
|
**serializer**: The serializer to use for objects being stored (ex. JSON).
|
161
143
|
Default is Marshal.
|
@@ -178,10 +160,12 @@ If serving compressed data using nginx's HttpMemcachedModule, set `memcached_gzi
|
|
178
160
|
|
179
161
|
**socket_failure_delay**: Before retrying a socket operation, the process sleeps for this amount of time. Default is 0.01. Set to nil for no delay.
|
180
162
|
|
181
|
-
**down_retry_delay**: When a server has been marked down due to many failures, the server will be checked again for being alive only after this amount of time. Don't set this value too low, otherwise each request which tries the failed server might hang for the maximum **socket_timeout**. Default is
|
163
|
+
**down_retry_delay**: When a server has been marked down due to many failures, the server will be checked again for being alive only after this amount of time. Don't set this value too low, otherwise each request which tries the failed server might hang for the maximum **socket_timeout**. Default is 60 seconds.
|
182
164
|
|
183
165
|
**value_max_bytes**: The maximum size of a value in memcached. Defaults to 1MB, this can be increased with memcached's -I parameter. You must also configure Dalli to allow the larger size here.
|
184
166
|
|
167
|
+
**error_when_over_max_size**: Boolean. If true, Dalli will throw a Dalli::ValueOverMaxSize exception when trying to store data larger than **value_max_bytes**. Defaults to false, meaning only a warning is logged.
|
168
|
+
|
185
169
|
**username**: The username to use for authenticating this client instance against a SASL-enabled memcached server. Heroku users should not need to use this normally.
|
186
170
|
|
187
171
|
**password**: The password to use for authenticating this client instance against a SASL-enabled memcached server. Heroku users should not need to use this normally.
|
@@ -192,10 +176,14 @@ If serving compressed data using nginx's HttpMemcachedModule, set `memcached_gzi
|
|
192
176
|
|
193
177
|
**cache_nils**: Boolean. If true Dalli will not treat cached `nil` values as 'not found' for `#fetch` operations. Default is false.
|
194
178
|
|
179
|
+
**raise_errors**: Boolean. When true DalliStore will reraise Dalli:DalliError instead swallowing the error. Default is false.
|
180
|
+
|
181
|
+
**instrument_errors**: Boolean. When true DalliStore will send notification of Dalli::DalliError via a 'cache_error.active_support' event. Default is false.
|
182
|
+
|
195
183
|
Features and Changes
|
196
184
|
------------------------
|
197
185
|
|
198
|
-
By default, Dalli is thread-safe.
|
186
|
+
By default, Dalli is thread-safe. Disable thread-safety at your own peril.
|
199
187
|
|
200
188
|
Dalli does not need anything special in Unicorn/Passenger since 2.0.4.
|
201
189
|
It will detect sockets shared with child processes and gracefully reopen the
|
@@ -208,7 +196,7 @@ Note that Dalli does not require ActiveSupport or Rails. You can safely use it
|
|
208
196
|
Helping Out
|
209
197
|
-------------
|
210
198
|
|
211
|
-
If you have a fix you wish to provide, please fork the code, fix in your local project and then send a pull request on github. Please ensure that you include a test which verifies your fix and update History.md with a one sentence description of your fix so you get credit as a contributor.
|
199
|
+
If you have a fix you wish to provide, please fork the code, fix in your local project and then send a pull request on github. Please ensure that you include a test which verifies your fix and update `History.md` with a one sentence description of your fix so you get credit as a contributor.
|
212
200
|
|
213
201
|
We're not accepting new compressors. They are trivial to add in an initializer. See #385 (LZ4), #406 (Snappy)
|
214
202
|
|
@@ -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
|
@@ -98,24 +108,24 @@ module ActiveSupport
|
|
98
108
|
if block_given?
|
99
109
|
entry = not_found
|
100
110
|
unless options[:force]
|
101
|
-
entry =
|
111
|
+
entry = instrument_with_log(:read, namespaced_name, options) do |payload|
|
102
112
|
read_entry(namespaced_name, options).tap do |result|
|
103
113
|
if payload
|
104
114
|
payload[:super_operation] = :fetch
|
105
|
-
payload[:hit] =
|
115
|
+
payload[:hit] = not_found != result
|
106
116
|
end
|
107
117
|
end
|
108
118
|
end
|
109
119
|
end
|
110
120
|
|
111
|
-
if
|
112
|
-
result =
|
113
|
-
yield
|
121
|
+
if not_found == entry
|
122
|
+
result = instrument_with_log(:generate, namespaced_name, options) do |payload|
|
123
|
+
yield(name)
|
114
124
|
end
|
115
125
|
write(name, result, options)
|
116
126
|
result
|
117
127
|
else
|
118
|
-
|
128
|
+
instrument_with_log(:fetch_hit, namespaced_name, options) { |payload| }
|
119
129
|
entry
|
120
130
|
end
|
121
131
|
else
|
@@ -127,7 +137,7 @@ module ActiveSupport
|
|
127
137
|
options ||= {}
|
128
138
|
name = namespaced_key(name, options)
|
129
139
|
|
130
|
-
|
140
|
+
instrument_with_log(:read, name, options) do |payload|
|
131
141
|
entry = read_entry(name, options)
|
132
142
|
payload[:hit] = !entry.nil? if payload
|
133
143
|
entry
|
@@ -138,7 +148,7 @@ module ActiveSupport
|
|
138
148
|
options ||= {}
|
139
149
|
name = namespaced_key(name, options)
|
140
150
|
|
141
|
-
|
151
|
+
instrument_with_log(:write, name, options) do |payload|
|
142
152
|
with do |connection|
|
143
153
|
options = options.merge(:connection => connection)
|
144
154
|
write_entry(name, value, options)
|
@@ -158,7 +168,7 @@ module ActiveSupport
|
|
158
168
|
options ||= {}
|
159
169
|
name = namespaced_key(name, options)
|
160
170
|
|
161
|
-
|
171
|
+
instrument_with_log(:delete, name, options) do |payload|
|
162
172
|
delete_entry(name, options)
|
163
173
|
end
|
164
174
|
end
|
@@ -168,7 +178,7 @@ module ActiveSupport
|
|
168
178
|
def read_multi(*names)
|
169
179
|
options = names.extract_options!
|
170
180
|
mapping = names.inject({}) { |memo, name| memo[namespaced_key(name, options)] = name; memo }
|
171
|
-
|
181
|
+
instrument_with_log(:read_multi, mapping.keys) do
|
172
182
|
results = {}
|
173
183
|
if local_cache
|
174
184
|
mapping.each_key do |key|
|
@@ -199,7 +209,7 @@ module ActiveSupport
|
|
199
209
|
options = names.extract_options!
|
200
210
|
mapping = names.inject({}) { |memo, name| memo[namespaced_key(name, options)] = name; memo }
|
201
211
|
|
202
|
-
|
212
|
+
instrument_with_log(:fetch_multi, mapping.keys) do
|
203
213
|
with do |connection|
|
204
214
|
results = connection.get_multi(mapping.keys)
|
205
215
|
|
@@ -230,11 +240,12 @@ module ActiveSupport
|
|
230
240
|
name = namespaced_key(name, options)
|
231
241
|
initial = options.has_key?(:initial) ? options[:initial] : amount
|
232
242
|
expires_in = options[:expires_in]
|
233
|
-
|
243
|
+
instrument_with_log(:increment, name, :amount => amount) do
|
234
244
|
with { |c| c.incr(name, amount, expires_in, initial) }
|
235
245
|
end
|
236
246
|
rescue Dalli::DalliError => e
|
237
|
-
|
247
|
+
log_dalli_error(e)
|
248
|
+
instrument_error(e) if instrument_errors?
|
238
249
|
raise if raise_errors?
|
239
250
|
nil
|
240
251
|
end
|
@@ -249,11 +260,12 @@ module ActiveSupport
|
|
249
260
|
name = namespaced_key(name, options)
|
250
261
|
initial = options.has_key?(:initial) ? options[:initial] : 0
|
251
262
|
expires_in = options[:expires_in]
|
252
|
-
|
263
|
+
instrument_with_log(:decrement, name, :amount => amount) do
|
253
264
|
with { |c| c.decr(name, amount, expires_in, initial) }
|
254
265
|
end
|
255
266
|
rescue Dalli::DalliError => e
|
256
|
-
|
267
|
+
log_dalli_error(e)
|
268
|
+
instrument_error(e) if instrument_errors?
|
257
269
|
raise if raise_errors?
|
258
270
|
nil
|
259
271
|
end
|
@@ -261,11 +273,12 @@ module ActiveSupport
|
|
261
273
|
# Clear the entire cache on all memcached servers. This method should
|
262
274
|
# be used with care when using a shared cache.
|
263
275
|
def clear(options=nil)
|
264
|
-
|
276
|
+
instrument_with_log(:clear, 'flushing all keys') do
|
265
277
|
with { |c| c.flush_all }
|
266
278
|
end
|
267
279
|
rescue Dalli::DalliError => e
|
268
|
-
|
280
|
+
log_dalli_error(e)
|
281
|
+
instrument_error(e) if instrument_errors?
|
269
282
|
raise if raise_errors?
|
270
283
|
nil
|
271
284
|
end
|
@@ -299,7 +312,8 @@ module ActiveSupport
|
|
299
312
|
# NB Backwards data compatibility, to be removed at some point
|
300
313
|
entry.is_a?(ActiveSupport::Cache::Entry) ? entry.value : entry
|
301
314
|
rescue Dalli::DalliError => e
|
302
|
-
|
315
|
+
log_dalli_error(e)
|
316
|
+
instrument_error(e) if instrument_errors?
|
303
317
|
raise if raise_errors?
|
304
318
|
nil
|
305
319
|
end
|
@@ -313,7 +327,8 @@ module ActiveSupport
|
|
313
327
|
connection = options.delete(:connection)
|
314
328
|
connection.send(method, key, value, expires_in, options)
|
315
329
|
rescue Dalli::DalliError => e
|
316
|
-
|
330
|
+
log_dalli_error(e)
|
331
|
+
instrument_error(e) if instrument_errors?
|
317
332
|
raise if raise_errors?
|
318
333
|
false
|
319
334
|
end
|
@@ -322,7 +337,8 @@ module ActiveSupport
|
|
322
337
|
def delete_entry(key, options) # :nodoc:
|
323
338
|
with { |c| c.delete(key) }
|
324
339
|
rescue Dalli::DalliError => e
|
325
|
-
|
340
|
+
log_dalli_error(e)
|
341
|
+
instrument_error(e) if instrument_errors?
|
326
342
|
raise if raise_errors?
|
327
343
|
false
|
328
344
|
end
|
@@ -330,18 +346,22 @@ module ActiveSupport
|
|
330
346
|
private
|
331
347
|
|
332
348
|
def namespaced_key(key, options)
|
349
|
+
digest_class = @options[:digest_class] || ::Digest::MD5
|
333
350
|
key = expanded_key(key)
|
334
351
|
namespace = options[:namespace] if options
|
335
352
|
prefix = namespace.is_a?(Proc) ? namespace.call : namespace
|
336
353
|
key = "#{prefix}:#{key}" if prefix
|
354
|
+
key = "#{key[0, 213]}:md5:#{digest_class.hexdigest(key)}" if key && key.size > 250
|
337
355
|
key
|
338
356
|
end
|
339
357
|
alias :normalize_key :namespaced_key
|
340
358
|
|
341
|
-
# Expand key to be a consistent string value.
|
342
|
-
#
|
343
|
-
#
|
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.
|
344
363
|
def expanded_key(key) # :nodoc:
|
364
|
+
return key.cache_key_with_version.to_s if key.respond_to?(:cache_key_with_version)
|
345
365
|
return key.cache_key.to_s if key.respond_to?(:cache_key)
|
346
366
|
|
347
367
|
case key
|
@@ -363,12 +383,26 @@ module ActiveSupport
|
|
363
383
|
key
|
364
384
|
end
|
365
385
|
|
366
|
-
def
|
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)
|
367
391
|
log(operation, key, options)
|
368
392
|
|
369
393
|
payload = { :key => key }
|
370
394
|
payload.merge!(options) if options.is_a?(Hash)
|
371
|
-
|
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
|
372
406
|
end
|
373
407
|
|
374
408
|
def log(operation, key, options=nil)
|
@@ -380,12 +414,16 @@ module ActiveSupport
|
|
380
414
|
!!@options[:raise_errors]
|
381
415
|
end
|
382
416
|
|
417
|
+
def instrument_errors?
|
418
|
+
!!@options[:instrument_errors]
|
419
|
+
end
|
420
|
+
|
383
421
|
# Make sure LocalCache is giving raw values, not `Entry`s, and
|
384
422
|
# respect `raw` option.
|
385
423
|
module LocalCacheEntryUnwrapAndRaw # :nodoc:
|
386
424
|
protected
|
387
425
|
def read_entry(key, options)
|
388
|
-
retval = super
|
426
|
+
retval = super(key, **options)
|
389
427
|
if retval.is_a? ActiveSupport::Cache::Entry
|
390
428
|
# Must have come from LocalStore, unwrap it
|
391
429
|
if options[:raw]
|
data/lib/dalli.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'dalli/compressor'
|
2
3
|
require 'dalli/client'
|
3
4
|
require 'dalli/ring'
|
@@ -18,6 +19,8 @@ module Dalli
|
|
18
19
|
class MarshalError < DalliError; end
|
19
20
|
# application error in marshalling deserialization or decompression
|
20
21
|
class UnmarshalError < DalliError; end
|
22
|
+
# payload too big for memcached
|
23
|
+
class ValueOverMaxSize < DalliError; end
|
21
24
|
|
22
25
|
def self.logger
|
23
26
|
@logger ||= (rails_logger || default_logger)
|
data/lib/dalli/cas/client.rb
CHANGED
data/lib/dalli/client.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'digest/md5'
|
2
3
|
require 'set'
|
3
4
|
|
@@ -28,6 +29,7 @@ module Dalli
|
|
28
29
|
# - :serializer - defaults to Marshal
|
29
30
|
# - :compressor - defaults to zlib
|
30
31
|
# - :cache_nils - defaults to false, if true Dalli will not treat cached nil values as 'not found' for #fetch operations.
|
32
|
+
# - :digest_class - defaults to Digest::MD5, allows you to pass in an object that responds to the hexdigest method, useful for injecting a FIPS compliant hash object.
|
31
33
|
#
|
32
34
|
def initialize(servers=nil, options={})
|
33
35
|
@servers = normalize_servers(servers || ENV["MEMCACHE_SERVERS"] || '127.0.0.1:11211')
|
@@ -63,7 +65,10 @@ module Dalli
|
|
63
65
|
# If a block is given, yields key/value pairs one at a time.
|
64
66
|
# Otherwise returns a hash of { 'key' => 'value', 'key2' => 'value1' }
|
65
67
|
def get_multi(*keys)
|
66
|
-
|
68
|
+
check_keys = keys.flatten
|
69
|
+
check_keys.compact!
|
70
|
+
|
71
|
+
return {} if check_keys.empty?
|
67
72
|
if block_given?
|
68
73
|
get_multi_yielder(keys) {|k, data| yield k, data.first}
|
69
74
|
else
|
@@ -291,7 +296,9 @@ module Dalli
|
|
291
296
|
end
|
292
297
|
|
293
298
|
def mapped_keys(keys)
|
294
|
-
keys.flatten
|
299
|
+
keys_array = keys.flatten
|
300
|
+
keys_array.map! { |a| validate_key(a.to_s) }
|
301
|
+
keys_array
|
295
302
|
end
|
296
303
|
|
297
304
|
def make_multi_get_requests(groups)
|
@@ -325,13 +332,15 @@ module Dalli
|
|
325
332
|
|
326
333
|
##
|
327
334
|
# Normalizes the argument into an array of servers.
|
328
|
-
# If the argument is a string, it's expected that the URIs are comma separated e.g.
|
335
|
+
# If the argument is a string, or an array containing strings, it's expected that the URIs are comma separated e.g.
|
329
336
|
# "memcache1.example.com:11211,memcache2.example.com:11211,memcache3.example.com:11211"
|
330
337
|
def normalize_servers(servers)
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
338
|
+
Array(servers).flat_map do |server|
|
339
|
+
if server.is_a? String
|
340
|
+
server.split(",")
|
341
|
+
else
|
342
|
+
server
|
343
|
+
end
|
335
344
|
end
|
336
345
|
end
|
337
346
|
|
@@ -372,8 +381,9 @@ module Dalli
|
|
372
381
|
raise ArgumentError, "key cannot be blank" if !key || key.length == 0
|
373
382
|
key = key_with_namespace(key)
|
374
383
|
if key.length > 250
|
384
|
+
digest_class = @options[:digest_class] || ::Digest::MD5
|
375
385
|
max_length_before_namespace = 212 - (namespace || '').size
|
376
|
-
key = "#{key[0, max_length_before_namespace]}:md5:#{
|
386
|
+
key = "#{key[0, max_length_before_namespace]}:md5:#{digest_class.hexdigest(key)}"
|
377
387
|
end
|
378
388
|
return key
|
379
389
|
end
|
@@ -401,6 +411,9 @@ module Dalli
|
|
401
411
|
rescue NoMethodError
|
402
412
|
raise ArgumentError, "cannot convert :expires_in => #{opts[:expires_in].inspect} to an integer"
|
403
413
|
end
|
414
|
+
if opts[:digest_class] && !opts[:digest_class].respond_to?(:hexdigest)
|
415
|
+
raise ArgumentError, "The digest_class object must respond to the hexdigest method"
|
416
|
+
end
|
404
417
|
opts
|
405
418
|
end
|
406
419
|
|
data/lib/dalli/compressor.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'zlib'
|
2
3
|
require 'stringio'
|
3
4
|
|
@@ -14,7 +15,7 @@ module Dalli
|
|
14
15
|
|
15
16
|
class GzipCompressor
|
16
17
|
def self.compress(data)
|
17
|
-
io = StringIO.new("w")
|
18
|
+
io = StringIO.new(String.new(""), "w")
|
18
19
|
gz = Zlib::GzipWriter.new(io)
|
19
20
|
gz.write(data)
|
20
21
|
gz.close
|
data/lib/dalli/options.rb
CHANGED
data/lib/dalli/railtie.rb
CHANGED
data/lib/dalli/ring.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'digest/sha1'
|
2
3
|
require 'zlib'
|
3
4
|
|
@@ -20,7 +21,7 @@ module Dalli
|
|
20
21
|
continuum << Dalli::Ring::Entry.new(value, server)
|
21
22
|
end
|
22
23
|
end
|
23
|
-
@continuum = continuum.
|
24
|
+
@continuum = continuum.sort_by(&:value)
|
24
25
|
end
|
25
26
|
|
26
27
|
threadsafe! unless options[:threadsafe] == false
|
@@ -110,7 +111,6 @@ module Dalli
|
|
110
111
|
def binary_search(ary, value)
|
111
112
|
upper = ary.size - 1
|
112
113
|
lower = 0
|
113
|
-
idx = 0
|
114
114
|
|
115
115
|
while (lower <= upper) do
|
116
116
|
idx = (lower + upper) / 2
|
@@ -124,7 +124,7 @@ module Dalli
|
|
124
124
|
lower = idx + 1
|
125
125
|
end
|
126
126
|
end
|
127
|
-
|
127
|
+
upper
|
128
128
|
end
|
129
129
|
end
|
130
130
|
|
data/lib/dalli/server.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'socket'
|
2
3
|
require 'timeout'
|
3
4
|
|
@@ -14,7 +15,7 @@ module Dalli
|
|
14
15
|
DEFAULT_WEIGHT = 1
|
15
16
|
DEFAULTS = {
|
16
17
|
# seconds between trying to contact a remote server
|
17
|
-
:down_retry_delay =>
|
18
|
+
:down_retry_delay => 60,
|
18
19
|
# connect/read/write timeout for socket operations
|
19
20
|
:socket_timeout => 0.5,
|
20
21
|
# times a socket operation may fail before considering the server dead
|
@@ -23,6 +24,8 @@ module Dalli
|
|
23
24
|
:socket_failure_delay => 0.01,
|
24
25
|
# max size of value in bytes (default is 1 MB, can be overriden with "memcached -I <size>")
|
25
26
|
:value_max_bytes => 1024 * 1024,
|
27
|
+
# surpassing value_max_bytes either warns (false) or throws (true)
|
28
|
+
:error_when_over_max_size => false,
|
26
29
|
:compressor => Compressor,
|
27
30
|
# min byte size to attempt compression
|
28
31
|
:compression_min_size => 1024,
|
@@ -65,16 +68,12 @@ module Dalli
|
|
65
68
|
raise Dalli::NetworkError, "#{name} is down: #{@error} #{@msg}. If you are sure it is running, ensure memcached version is > 1.4." unless alive?
|
66
69
|
begin
|
67
70
|
send(op, *args)
|
68
|
-
rescue Dalli::NetworkError
|
69
|
-
raise
|
70
71
|
rescue Dalli::MarshalError => ex
|
71
72
|
Dalli.logger.error "Marshalling error for key '#{args.first}': #{ex.message}"
|
72
73
|
Dalli.logger.error "You are trying to cache a Ruby object which cannot be serialized to memcached."
|
73
74
|
Dalli.logger.error ex.backtrace.join("\n\t")
|
74
75
|
false
|
75
|
-
rescue Dalli::DalliError
|
76
|
-
raise
|
77
|
-
rescue Timeout::Error
|
76
|
+
rescue Dalli::DalliError, Dalli::NetworkError, Dalli::ValueOverMaxSize, Timeout::Error
|
78
77
|
raise
|
79
78
|
rescue => ex
|
80
79
|
Dalli.logger.error "Unexpected exception during Dalli request: #{ex.class.name}: #{ex.message}"
|
@@ -128,7 +127,7 @@ module Dalli
|
|
128
127
|
def multi_response_start
|
129
128
|
verify_state
|
130
129
|
write_noop
|
131
|
-
@multi_buffer = ''
|
130
|
+
@multi_buffer = String.new('')
|
132
131
|
@position = 0
|
133
132
|
@inprogress = true
|
134
133
|
end
|
@@ -274,7 +273,7 @@ module Dalli
|
|
274
273
|
end
|
275
274
|
|
276
275
|
def send_multiget(keys)
|
277
|
-
req = ""
|
276
|
+
req = String.new("")
|
278
277
|
keys.each do |key|
|
279
278
|
req << [REQUEST, OPCODES[:getkq], key.bytesize, 0, 0, 0, key.bytesize, 0, 0, key].pack(FORMAT[:getkq])
|
280
279
|
end
|
@@ -448,6 +447,9 @@ module Dalli
|
|
448
447
|
rescue ArgumentError
|
449
448
|
raise if $!.message !~ /undefined class|marshal data too short/
|
450
449
|
raise UnmarshalError, "Unable to unmarshal value: #{$!.message}"
|
450
|
+
rescue NameError
|
451
|
+
raise if $!.message !~ /uninitialized constant/
|
452
|
+
raise UnmarshalError, "Unable to unmarshal value: #{$!.message}"
|
451
453
|
rescue Zlib::Error
|
452
454
|
raise UnmarshalError, "Unable to uncompress value: #{$!.message}"
|
453
455
|
end
|
@@ -475,19 +477,24 @@ module Dalli
|
|
475
477
|
if value.bytesize <= @options[:value_max_bytes]
|
476
478
|
yield
|
477
479
|
else
|
478
|
-
|
480
|
+
message = "Value for #{key} over max size: #{@options[:value_max_bytes]} <= #{value.bytesize}"
|
481
|
+
raise Dalli::ValueOverMaxSize, message if @options[:error_when_over_max_size]
|
482
|
+
|
483
|
+
Dalli.logger.error "#{message} - this value may be truncated by memcached"
|
479
484
|
false
|
480
485
|
end
|
481
486
|
end
|
482
487
|
|
483
|
-
# https://
|
488
|
+
# https://github.com/memcached/memcached/blob/master/doc/protocol.txt#L79
|
484
489
|
# > An expiration time, in seconds. Can be up to 30 days. After 30 days, is treated as a unix timestamp of an exact date.
|
485
490
|
MAX_ACCEPTABLE_EXPIRATION_INTERVAL = 30*24*60*60 # 30 days
|
486
491
|
def sanitize_ttl(ttl)
|
487
492
|
ttl_as_i = ttl.to_i
|
488
493
|
return ttl_as_i if ttl_as_i <= MAX_ACCEPTABLE_EXPIRATION_INTERVAL
|
494
|
+
now = Time.now.to_i
|
495
|
+
return ttl_as_i if ttl_as_i > now # already a timestamp
|
489
496
|
Dalli.logger.debug "Expiration interval (#{ttl_as_i}) too long for Memcached, converting to an expiration timestamp"
|
490
|
-
|
497
|
+
now + ttl_as_i
|
491
498
|
end
|
492
499
|
|
493
500
|
# Implements the NullObject pattern to store an application-defined value for 'Key not found' responses.
|
@@ -604,7 +611,7 @@ module Dalli
|
|
604
611
|
RESPONSE = 0x81
|
605
612
|
|
606
613
|
# Response codes taken from:
|
607
|
-
# https://
|
614
|
+
# https://github.com/memcached/memcached/wiki/BinaryProtocolRevamped#response-status
|
608
615
|
RESPONSE_CODES = {
|
609
616
|
0 => 'No error',
|
610
617
|
1 => 'Key not found',
|
@@ -698,7 +705,7 @@ module Dalli
|
|
698
705
|
req = [REQUEST, OPCODES[:auth_negotiation], 0, 0, 0, 0, 0, 0, 0].pack(FORMAT[:noop])
|
699
706
|
write(req)
|
700
707
|
|
701
|
-
(extras,
|
708
|
+
(extras, _type, status, count) = read_header.unpack(NORMAL_HEADER)
|
702
709
|
raise Dalli::NetworkError, "Unexpected message format: #{extras} #{count}" unless extras == 0 && count > 0
|
703
710
|
content = read(count).gsub(/\u0000/, ' ')
|
704
711
|
return (Dalli.logger.debug("Authentication not required/supported by server")) if status == 0x81
|
@@ -711,7 +718,7 @@ module Dalli
|
|
711
718
|
req = [REQUEST, OPCODES[:auth_request], mechanism.bytesize, 0, 0, 0, mechanism.bytesize + msg.bytesize, 0, 0, mechanism, msg].pack(FORMAT[:auth_request])
|
712
719
|
write(req)
|
713
720
|
|
714
|
-
(extras,
|
721
|
+
(extras, _type, status, count) = read_header.unpack(NORMAL_HEADER)
|
715
722
|
raise Dalli::NetworkError, "Unexpected message format: #{extras} #{count}" unless extras == 0 && count > 0
|
716
723
|
content = read(count)
|
717
724
|
return Dalli.logger.info("Dalli/SASL: #{content}") if status == 0
|
data/lib/dalli/socket.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'rbconfig'
|
2
3
|
|
3
4
|
module Dalli::Server::TCPSocketOptions
|
@@ -27,7 +28,7 @@ begin
|
|
27
28
|
alias :write :kgio_write
|
28
29
|
|
29
30
|
def readfull(count)
|
30
|
-
value = ''
|
31
|
+
value = String.new('')
|
31
32
|
while true
|
32
33
|
value << kgio_read!(count - value.bytesize)
|
33
34
|
break if value.bytesize == count
|
@@ -36,7 +37,7 @@ begin
|
|
36
37
|
end
|
37
38
|
|
38
39
|
def read_available
|
39
|
-
value = ''
|
40
|
+
value = String.new('')
|
40
41
|
while true
|
41
42
|
ret = kgio_tryread(8196)
|
42
43
|
case ret
|
@@ -63,6 +64,9 @@ begin
|
|
63
64
|
sock.server = server
|
64
65
|
sock.kgio_wait_writable
|
65
66
|
sock
|
67
|
+
rescue Timeout::Error
|
68
|
+
sock.close if sock
|
69
|
+
raise
|
66
70
|
end
|
67
71
|
end
|
68
72
|
|
@@ -74,6 +78,9 @@ begin
|
|
74
78
|
sock.server = server
|
75
79
|
sock.kgio_wait_writable
|
76
80
|
sock
|
81
|
+
rescue Timeout::Error
|
82
|
+
sock.close if sock
|
83
|
+
raise
|
77
84
|
end
|
78
85
|
end
|
79
86
|
|
@@ -88,7 +95,7 @@ rescue LoadError
|
|
88
95
|
module Dalli::Server::KSocket
|
89
96
|
module InstanceMethods
|
90
97
|
def readfull(count)
|
91
|
-
value = ''
|
98
|
+
value = String.new('')
|
92
99
|
begin
|
93
100
|
while true
|
94
101
|
value << read_nonblock(count - value.bytesize)
|
@@ -98,14 +105,15 @@ rescue LoadError
|
|
98
105
|
if IO.select([self], nil, nil, options[:socket_timeout])
|
99
106
|
retry
|
100
107
|
else
|
101
|
-
|
108
|
+
safe_options = options.reject{|k,v| [:username, :password].include? k}
|
109
|
+
raise Timeout::Error, "IO timeout: #{safe_options.inspect}"
|
102
110
|
end
|
103
111
|
end
|
104
112
|
value
|
105
113
|
end
|
106
114
|
|
107
115
|
def read_available
|
108
|
-
value = ''
|
116
|
+
value = String.new('')
|
109
117
|
while true
|
110
118
|
begin
|
111
119
|
value << read_nonblock(8196)
|
data/lib/dalli/version.rb
CHANGED
data/lib/rack/session/dalli.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'rack/session/abstract/id'
|
2
3
|
require 'dalli'
|
3
4
|
|
@@ -6,46 +7,98 @@ module Rack
|
|
6
7
|
class Dalli < defined?(Abstract::Persisted) ? Abstract::Persisted : Abstract::ID
|
7
8
|
attr_reader :pool, :mutex
|
8
9
|
|
9
|
-
|
10
|
+
DEFAULT_DALLI_OPTIONS = {
|
10
11
|
:namespace => 'rack:session',
|
11
12
|
:memcache_server => 'localhost:11211'
|
13
|
+
}
|
12
14
|
|
15
|
+
# Brings in a new Rack::Session::Dalli middleware with the given
|
16
|
+
# `:memcache_server`. The server is either a hostname, or a
|
17
|
+
# host-with-port string in the form of "host_name:port", or an array of
|
18
|
+
# such strings. For example:
|
19
|
+
#
|
20
|
+
# use Rack::Session::Dalli,
|
21
|
+
# :memcache_server => "mc.example.com:1234"
|
22
|
+
#
|
23
|
+
# If no `:memcache_server` option is specified, Rack::Session::Dalli will
|
24
|
+
# connect to localhost, port 11211 (the default memcached port). If
|
25
|
+
# `:memcache_server` is set to nil, Dalli::Client will look for
|
26
|
+
# ENV['MEMCACHE_SERVERS'] and use that value if it is available, or fall
|
27
|
+
# back to the same default behavior described above.
|
28
|
+
#
|
29
|
+
# Rack::Session::Dalli is intended to be a drop-in replacement for
|
30
|
+
# Rack::Session::Memcache. It accepts additional options that control the
|
31
|
+
# behavior of Rack::Session, Dalli::Client, and an optional
|
32
|
+
# ConnectionPool. First and foremost, if you wish to instantiate your own
|
33
|
+
# Dalli::Client (or ConnectionPool) and use that instead of letting
|
34
|
+
# Rack::Session::Dalli instantiate it on your behalf, simply pass it in
|
35
|
+
# as the `:cache` option. Please note that you will be responsible for
|
36
|
+
# setting the namespace and any other options on Dalli::Client.
|
37
|
+
#
|
38
|
+
# Secondly, if you're not using the `:cache` option, Rack::Session::Dalli
|
39
|
+
# accepts the same options as Dalli::Client, so it's worth reviewing its
|
40
|
+
# documentation. Perhaps most importantly, if you don't specify a
|
41
|
+
# `:namespace` option, Rack::Session::Dalli will default to using
|
42
|
+
# "rack:session".
|
43
|
+
#
|
44
|
+
# Whether you are using the `:cache` option or not, it is not recommend
|
45
|
+
# to set `:expires_in`. Instead, use `:expire_after`, which will control
|
46
|
+
# both the expiration of the client cookie as well as the expiration of
|
47
|
+
# the corresponding entry in memcached.
|
48
|
+
#
|
49
|
+
# Rack::Session::Dalli also accepts a host of options that control how
|
50
|
+
# the sessions and session cookies are managed, including the
|
51
|
+
# aforementioned `:expire_after` option. Please see the documentation for
|
52
|
+
# Rack::Session::Abstract::Persisted for a detailed explanation of these
|
53
|
+
# options and their default values.
|
54
|
+
#
|
55
|
+
# Finally, if your web application is multithreaded, the
|
56
|
+
# Rack::Session::Dalli middleware can become a source of contention. You
|
57
|
+
# can use a connection pool of Dalli clients by passing in the
|
58
|
+
# `:pool_size` and/or `:pool_timeout` options. For example:
|
59
|
+
#
|
60
|
+
# use Rack::Session::Dalli,
|
61
|
+
# :memcache_server => "mc.example.com:1234",
|
62
|
+
# :pool_size => 10
|
63
|
+
#
|
64
|
+
# You must include the `connection_pool` gem in your project if you wish
|
65
|
+
# to use pool support. Please see the documentation for ConnectionPool
|
66
|
+
# for more information about it and its default options (which would only
|
67
|
+
# be applicable if you supplied one of the two options, but not both).
|
68
|
+
#
|
13
69
|
def initialize(app, options={})
|
70
|
+
# Parent uses DEFAULT_OPTIONS to build @default_options for Rack::Session
|
14
71
|
super
|
15
|
-
@mutex = Mutex.new
|
16
|
-
mserv = @default_options[:memcache_server]
|
17
|
-
mopts = @default_options.reject{|k,v| !DEFAULT_OPTIONS.include? k }
|
18
|
-
@pool = options[:cache] || ::Dalli::Client.new(mserv, mopts)
|
19
|
-
@pool.alive!
|
20
|
-
end
|
21
72
|
|
22
|
-
|
23
|
-
|
24
|
-
get_session req.env, sid
|
25
|
-
end
|
73
|
+
# Determine the default TTL for newly-created sessions
|
74
|
+
@default_ttl = ttl @default_options[:expire_after]
|
26
75
|
|
27
|
-
|
28
|
-
|
29
|
-
end
|
76
|
+
# Normalize and validate passed options
|
77
|
+
cache, mserv, mopts, popts = extract_dalli_options options
|
30
78
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
79
|
+
@pool =
|
80
|
+
if cache # caller passed a Dalli::Client or ConnectionPool instance
|
81
|
+
cache
|
82
|
+
elsif popts # caller passed ConnectionPool options
|
83
|
+
ConnectionPool.new(popts) { ::Dalli::Client.new(mserv, mopts) }
|
84
|
+
else
|
85
|
+
::Dalli::Client.new(mserv, mopts)
|
86
|
+
end
|
35
87
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
88
|
+
if @pool.respond_to?(:alive!) # is a Dalli::Client
|
89
|
+
@mutex = Mutex.new
|
90
|
+
|
91
|
+
@pool.alive!
|
40
92
|
end
|
41
93
|
end
|
42
94
|
|
43
95
|
def get_session(env, sid)
|
44
|
-
|
45
|
-
unless sid and !sid.empty? and session =
|
46
|
-
sid, session =
|
47
|
-
unless
|
48
|
-
|
96
|
+
with_block(env, [nil, {}]) do |dc|
|
97
|
+
unless sid and !sid.empty? and session = dc.get(sid)
|
98
|
+
old_sid, sid, session = sid, generate_sid_with(dc), {}
|
99
|
+
unless dc.add(sid, session, @default_ttl)
|
100
|
+
sid = old_sid
|
101
|
+
redo # generate a new sid and try again
|
49
102
|
end
|
50
103
|
end
|
51
104
|
[sid, session]
|
@@ -54,25 +107,66 @@ module Rack
|
|
54
107
|
|
55
108
|
def set_session(env, session_id, new_session, options)
|
56
109
|
return false unless session_id
|
57
|
-
expiry = options[:expire_after]
|
58
|
-
expiry = expiry.nil? ? 0 : expiry + 1
|
59
110
|
|
60
|
-
|
61
|
-
|
111
|
+
with_block(env, false) do |dc|
|
112
|
+
dc.set(session_id, new_session, ttl(options[:expire_after]))
|
62
113
|
session_id
|
63
114
|
end
|
64
115
|
end
|
65
116
|
|
66
117
|
def destroy_session(env, session_id, options)
|
67
|
-
|
68
|
-
|
69
|
-
|
118
|
+
with_block(env) do |dc|
|
119
|
+
dc.delete(session_id)
|
120
|
+
generate_sid_with(dc) unless options[:drop]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
if defined?(Abstract::Persisted)
|
125
|
+
def find_session(req, sid)
|
126
|
+
get_session req.env, sid
|
127
|
+
end
|
128
|
+
|
129
|
+
def write_session(req, sid, session, options)
|
130
|
+
set_session req.env, sid, session, options
|
131
|
+
end
|
132
|
+
|
133
|
+
def delete_session(req, sid, options)
|
134
|
+
destroy_session req.env, sid, options
|
70
135
|
end
|
71
136
|
end
|
72
137
|
|
73
|
-
|
74
|
-
|
75
|
-
|
138
|
+
private
|
139
|
+
|
140
|
+
def extract_dalli_options(options)
|
141
|
+
return [options[:cache]] if options[:cache]
|
142
|
+
|
143
|
+
# Filter out Rack::Session-specific options and apply our defaults
|
144
|
+
mopts = DEFAULT_DALLI_OPTIONS.merge \
|
145
|
+
options.reject {|k, _| DEFAULT_OPTIONS.key? k }
|
146
|
+
mserv = mopts.delete :memcache_server
|
147
|
+
|
148
|
+
if mopts[:pool_size] || mopts[:pool_timeout]
|
149
|
+
popts = {}
|
150
|
+
popts[:size] = mopts.delete :pool_size if mopts[:pool_size]
|
151
|
+
popts[:timeout] = mopts.delete :pool_timeout if mopts[:pool_timeout]
|
152
|
+
|
153
|
+
# For a connection pool, locking is handled at the pool level
|
154
|
+
mopts[:threadsafe] = false unless mopts.key? :threadsafe
|
155
|
+
end
|
156
|
+
|
157
|
+
[nil, mserv, mopts, popts]
|
158
|
+
end
|
159
|
+
|
160
|
+
def generate_sid_with(dc)
|
161
|
+
while true
|
162
|
+
sid = generate_sid
|
163
|
+
break sid unless dc.get(sid)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def with_block(env, default=nil, &block)
|
168
|
+
@mutex.lock if @mutex and env['rack.multithread']
|
169
|
+
@pool.with(&block)
|
76
170
|
rescue ::Dalli::DalliError, Errno::ECONNREFUSED
|
77
171
|
raise if $!.message =~ /undefined class/
|
78
172
|
if $VERBOSE
|
@@ -81,9 +175,12 @@ module Rack
|
|
81
175
|
end
|
82
176
|
default
|
83
177
|
ensure
|
84
|
-
@mutex.unlock if @mutex.locked?
|
178
|
+
@mutex.unlock if @mutex and @mutex.locked?
|
85
179
|
end
|
86
180
|
|
181
|
+
def ttl(expire_after)
|
182
|
+
expire_after.nil? ? 0 : expire_after + 1
|
183
|
+
end
|
87
184
|
end
|
88
185
|
end
|
89
186
|
end
|
metadata
CHANGED
@@ -1,128 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dalli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.7.
|
4
|
+
version: 2.7.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter M. Goldstein
|
8
8
|
- Mike Perham
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
13
|
-
dependencies:
|
14
|
-
- !ruby/object:Gem::Dependency
|
15
|
-
name: minitest
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
requirements:
|
18
|
-
- - ">="
|
19
|
-
- !ruby/object:Gem::Version
|
20
|
-
version: 4.2.0
|
21
|
-
type: :development
|
22
|
-
prerelease: false
|
23
|
-
version_requirements: !ruby/object:Gem::Requirement
|
24
|
-
requirements:
|
25
|
-
- - ">="
|
26
|
-
- !ruby/object:Gem::Version
|
27
|
-
version: 4.2.0
|
28
|
-
- !ruby/object:Gem::Dependency
|
29
|
-
name: mocha
|
30
|
-
requirement: !ruby/object:Gem::Requirement
|
31
|
-
requirements:
|
32
|
-
- - ">="
|
33
|
-
- !ruby/object:Gem::Version
|
34
|
-
version: '0'
|
35
|
-
type: :development
|
36
|
-
prerelease: false
|
37
|
-
version_requirements: !ruby/object:Gem::Requirement
|
38
|
-
requirements:
|
39
|
-
- - ">="
|
40
|
-
- !ruby/object:Gem::Version
|
41
|
-
version: '0'
|
42
|
-
- !ruby/object:Gem::Dependency
|
43
|
-
name: rails
|
44
|
-
requirement: !ruby/object:Gem::Requirement
|
45
|
-
requirements:
|
46
|
-
- - "~>"
|
47
|
-
- !ruby/object:Gem::Version
|
48
|
-
version: '4'
|
49
|
-
type: :development
|
50
|
-
prerelease: false
|
51
|
-
version_requirements: !ruby/object:Gem::Requirement
|
52
|
-
requirements:
|
53
|
-
- - "~>"
|
54
|
-
- !ruby/object:Gem::Version
|
55
|
-
version: '4'
|
56
|
-
- !ruby/object:Gem::Dependency
|
57
|
-
name: rake
|
58
|
-
requirement: !ruby/object:Gem::Requirement
|
59
|
-
requirements:
|
60
|
-
- - ">="
|
61
|
-
- !ruby/object:Gem::Version
|
62
|
-
version: '0'
|
63
|
-
type: :development
|
64
|
-
prerelease: false
|
65
|
-
version_requirements: !ruby/object:Gem::Requirement
|
66
|
-
requirements:
|
67
|
-
- - ">="
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
-
version: '0'
|
70
|
-
- !ruby/object:Gem::Dependency
|
71
|
-
name: appraisal
|
72
|
-
requirement: !ruby/object:Gem::Requirement
|
73
|
-
requirements:
|
74
|
-
- - ">="
|
75
|
-
- !ruby/object:Gem::Version
|
76
|
-
version: '0'
|
77
|
-
type: :development
|
78
|
-
prerelease: false
|
79
|
-
version_requirements: !ruby/object:Gem::Requirement
|
80
|
-
requirements:
|
81
|
-
- - ">="
|
82
|
-
- !ruby/object:Gem::Version
|
83
|
-
version: '0'
|
84
|
-
- !ruby/object:Gem::Dependency
|
85
|
-
name: connection_pool
|
86
|
-
requirement: !ruby/object:Gem::Requirement
|
87
|
-
requirements:
|
88
|
-
- - ">="
|
89
|
-
- !ruby/object:Gem::Version
|
90
|
-
version: '0'
|
91
|
-
type: :development
|
92
|
-
prerelease: false
|
93
|
-
version_requirements: !ruby/object:Gem::Requirement
|
94
|
-
requirements:
|
95
|
-
- - ">="
|
96
|
-
- !ruby/object:Gem::Version
|
97
|
-
version: '0'
|
98
|
-
- !ruby/object:Gem::Dependency
|
99
|
-
name: rdoc
|
100
|
-
requirement: !ruby/object:Gem::Requirement
|
101
|
-
requirements:
|
102
|
-
- - ">="
|
103
|
-
- !ruby/object:Gem::Version
|
104
|
-
version: '0'
|
105
|
-
type: :development
|
106
|
-
prerelease: false
|
107
|
-
version_requirements: !ruby/object:Gem::Requirement
|
108
|
-
requirements:
|
109
|
-
- - ">="
|
110
|
-
- !ruby/object:Gem::Version
|
111
|
-
version: '0'
|
112
|
-
- !ruby/object:Gem::Dependency
|
113
|
-
name: simplecov
|
114
|
-
requirement: !ruby/object:Gem::Requirement
|
115
|
-
requirements:
|
116
|
-
- - ">="
|
117
|
-
- !ruby/object:Gem::Version
|
118
|
-
version: '0'
|
119
|
-
type: :development
|
120
|
-
prerelease: false
|
121
|
-
version_requirements: !ruby/object:Gem::Requirement
|
122
|
-
requirements:
|
123
|
-
- - ">="
|
124
|
-
- !ruby/object:Gem::Version
|
125
|
-
version: '0'
|
12
|
+
date: 2020-09-26 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
126
14
|
description: High performance memcached client for Ruby
|
127
15
|
email:
|
128
16
|
- peter.m.goldstein@gmail.com
|
@@ -152,9 +40,8 @@ homepage: https://github.com/petergoldstein/dalli
|
|
152
40
|
licenses:
|
153
41
|
- MIT
|
154
42
|
metadata: {}
|
155
|
-
post_install_message:
|
156
|
-
rdoc_options:
|
157
|
-
- "--charset=UTF-8"
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
158
45
|
require_paths:
|
159
46
|
- lib
|
160
47
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -168,9 +55,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
168
55
|
- !ruby/object:Gem::Version
|
169
56
|
version: '0'
|
170
57
|
requirements: []
|
171
|
-
rubyforge_project:
|
172
|
-
rubygems_version: 2.
|
173
|
-
signing_key:
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 2.7.6.2
|
60
|
+
signing_key:
|
174
61
|
specification_version: 4
|
175
62
|
summary: High performance memcached client for Ruby
|
176
63
|
test_files: []
|