dalli 2.7.6 → 2.7.7
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/History.md +2 -0
- data/README.md +13 -3
- data/lib/action_dispatch/middleware/session/dalli_store.rb +1 -0
- data/lib/active_support/cache/dalli_store.rb +47 -21
- data/lib/dalli.rb +3 -0
- data/lib/dalli/cas/client.rb +1 -0
- data/lib/dalli/client.rb +1 -0
- 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 +18 -14
- data/lib/dalli/socket.rb +13 -5
- data/lib/dalli/version.rb +2 -1
- data/lib/rack/session/dalli.rb +143 -37
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: be20ed3535eb5ddc41d8d53e596adc7fe7e7d4241ff345e79f29fb1c6b732a01
|
4
|
+
data.tar.gz: 39e7c9ea59332d26a2b8ca7276664f3028bbe0b3db3481aa24bc478b5019f3aa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9ca82a471a6ae90c123c1888117e87fbc6e348a3401b3db77f5982574d5dc46e0765681df22815773c27d204fa9023841b0012db7a37ec72569eeeddc5e53a3b
|
7
|
+
data.tar.gz: 93cef3703a4b7c48e1127228b11d03565feba4523dfc1057f6689f50627b1bbb29c0fcea2d703e8500bfdb5fc70da2b975736032452610550f6acf7dace76025
|
data/History.md
CHANGED
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) [![Dependency Status](https://gemnasium.com/petergoldstein/dalli.svg)](https://gemnasium.com/petergoldstein/dalli) [![Code Climate](https://codeclimate.com/github/petergoldstein/dalli.svg)](https://codeclimate.com/github/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.
|
@@ -90,10 +90,12 @@ config.cache_store = :dalli_store
|
|
90
90
|
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
91
|
|
92
92
|
```ruby
|
93
|
-
config.cache_store = :dalli_store, 'cache-1.example.com', 'cache-2.example.com',
|
93
|
+
config.cache_store = :dalli_store, 'cache-1.example.com', 'cache-2.example.com:11211:2',
|
94
94
|
{ :namespace => NAME_OF_RAILS_APP, :expires_in => 1.day, :compress => true }
|
95
95
|
```
|
96
96
|
|
97
|
+
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` )
|
98
|
+
|
97
99
|
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
100
|
|
99
101
|
```ruby
|
@@ -147,6 +149,8 @@ end
|
|
147
149
|
Configuration
|
148
150
|
------------------------
|
149
151
|
|
152
|
+
**servers**: An Array of "host:port:weight" where weight allows you to distribute cache unevenly.
|
153
|
+
|
150
154
|
Dalli::Client accepts the following options. All times are in seconds.
|
151
155
|
|
152
156
|
**expires_in**: Global default for key TTL. Default is 0, which means no expiry.
|
@@ -178,10 +182,12 @@ If serving compressed data using nginx's HttpMemcachedModule, set `memcached_gzi
|
|
178
182
|
|
179
183
|
**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
184
|
|
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
|
185
|
+
**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
186
|
|
183
187
|
**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
188
|
|
189
|
+
**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.
|
190
|
+
|
185
191
|
**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
192
|
|
187
193
|
**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,6 +198,10 @@ If serving compressed data using nginx's HttpMemcachedModule, set `memcached_gzi
|
|
192
198
|
|
193
199
|
**cache_nils**: Boolean. If true Dalli will not treat cached `nil` values as 'not found' for `#fetch` operations. Default is false.
|
194
200
|
|
201
|
+
**raise_errors**: Boolean. When true DalliStore will reraise Dalli:DalliError instead swallowing the error. Default is false.
|
202
|
+
|
203
|
+
**instrument_errors**: Boolean. When true DalliStore will send notification of Dalli::DalliError via a 'cache_error.active_support' event. Default is false.
|
204
|
+
|
195
205
|
Features and Changes
|
196
206
|
------------------------
|
197
207
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# encoding: ascii
|
2
|
+
# frozen_string_literal: true
|
2
3
|
require 'dalli'
|
3
4
|
|
4
5
|
module ActiveSupport
|
@@ -98,24 +99,24 @@ module ActiveSupport
|
|
98
99
|
if block_given?
|
99
100
|
entry = not_found
|
100
101
|
unless options[:force]
|
101
|
-
entry =
|
102
|
+
entry = instrument_with_log(:read, namespaced_name, options) do |payload|
|
102
103
|
read_entry(namespaced_name, options).tap do |result|
|
103
104
|
if payload
|
104
105
|
payload[:super_operation] = :fetch
|
105
|
-
payload[:hit] =
|
106
|
+
payload[:hit] = not_found != result
|
106
107
|
end
|
107
108
|
end
|
108
109
|
end
|
109
110
|
end
|
110
111
|
|
111
|
-
if
|
112
|
-
result =
|
112
|
+
if not_found == entry
|
113
|
+
result = instrument_with_log(:generate, namespaced_name, options) do |payload|
|
113
114
|
yield
|
114
115
|
end
|
115
116
|
write(name, result, options)
|
116
117
|
result
|
117
118
|
else
|
118
|
-
|
119
|
+
instrument_with_log(:fetch_hit, namespaced_name, options) { |payload| }
|
119
120
|
entry
|
120
121
|
end
|
121
122
|
else
|
@@ -127,7 +128,7 @@ module ActiveSupport
|
|
127
128
|
options ||= {}
|
128
129
|
name = namespaced_key(name, options)
|
129
130
|
|
130
|
-
|
131
|
+
instrument_with_log(:read, name, options) do |payload|
|
131
132
|
entry = read_entry(name, options)
|
132
133
|
payload[:hit] = !entry.nil? if payload
|
133
134
|
entry
|
@@ -138,7 +139,7 @@ module ActiveSupport
|
|
138
139
|
options ||= {}
|
139
140
|
name = namespaced_key(name, options)
|
140
141
|
|
141
|
-
|
142
|
+
instrument_with_log(:write, name, options) do |payload|
|
142
143
|
with do |connection|
|
143
144
|
options = options.merge(:connection => connection)
|
144
145
|
write_entry(name, value, options)
|
@@ -158,7 +159,7 @@ module ActiveSupport
|
|
158
159
|
options ||= {}
|
159
160
|
name = namespaced_key(name, options)
|
160
161
|
|
161
|
-
|
162
|
+
instrument_with_log(:delete, name, options) do |payload|
|
162
163
|
delete_entry(name, options)
|
163
164
|
end
|
164
165
|
end
|
@@ -168,7 +169,7 @@ module ActiveSupport
|
|
168
169
|
def read_multi(*names)
|
169
170
|
options = names.extract_options!
|
170
171
|
mapping = names.inject({}) { |memo, name| memo[namespaced_key(name, options)] = name; memo }
|
171
|
-
|
172
|
+
instrument_with_log(:read_multi, mapping.keys) do
|
172
173
|
results = {}
|
173
174
|
if local_cache
|
174
175
|
mapping.each_key do |key|
|
@@ -199,7 +200,7 @@ module ActiveSupport
|
|
199
200
|
options = names.extract_options!
|
200
201
|
mapping = names.inject({}) { |memo, name| memo[namespaced_key(name, options)] = name; memo }
|
201
202
|
|
202
|
-
|
203
|
+
instrument_with_log(:fetch_multi, mapping.keys) do
|
203
204
|
with do |connection|
|
204
205
|
results = connection.get_multi(mapping.keys)
|
205
206
|
|
@@ -230,11 +231,12 @@ module ActiveSupport
|
|
230
231
|
name = namespaced_key(name, options)
|
231
232
|
initial = options.has_key?(:initial) ? options[:initial] : amount
|
232
233
|
expires_in = options[:expires_in]
|
233
|
-
|
234
|
+
instrument_with_log(:increment, name, :amount => amount) do
|
234
235
|
with { |c| c.incr(name, amount, expires_in, initial) }
|
235
236
|
end
|
236
237
|
rescue Dalli::DalliError => e
|
237
|
-
|
238
|
+
log_dalli_error(e)
|
239
|
+
instrument_error(e) if instrument_errors?
|
238
240
|
raise if raise_errors?
|
239
241
|
nil
|
240
242
|
end
|
@@ -249,11 +251,12 @@ module ActiveSupport
|
|
249
251
|
name = namespaced_key(name, options)
|
250
252
|
initial = options.has_key?(:initial) ? options[:initial] : 0
|
251
253
|
expires_in = options[:expires_in]
|
252
|
-
|
254
|
+
instrument_with_log(:decrement, name, :amount => amount) do
|
253
255
|
with { |c| c.decr(name, amount, expires_in, initial) }
|
254
256
|
end
|
255
257
|
rescue Dalli::DalliError => e
|
256
|
-
|
258
|
+
log_dalli_error(e)
|
259
|
+
instrument_error(e) if instrument_errors?
|
257
260
|
raise if raise_errors?
|
258
261
|
nil
|
259
262
|
end
|
@@ -261,11 +264,12 @@ module ActiveSupport
|
|
261
264
|
# Clear the entire cache on all memcached servers. This method should
|
262
265
|
# be used with care when using a shared cache.
|
263
266
|
def clear(options=nil)
|
264
|
-
|
267
|
+
instrument_with_log(:clear, 'flushing all keys') do
|
265
268
|
with { |c| c.flush_all }
|
266
269
|
end
|
267
270
|
rescue Dalli::DalliError => e
|
268
|
-
|
271
|
+
log_dalli_error(e)
|
272
|
+
instrument_error(e) if instrument_errors?
|
269
273
|
raise if raise_errors?
|
270
274
|
nil
|
271
275
|
end
|
@@ -299,7 +303,8 @@ module ActiveSupport
|
|
299
303
|
# NB Backwards data compatibility, to be removed at some point
|
300
304
|
entry.is_a?(ActiveSupport::Cache::Entry) ? entry.value : entry
|
301
305
|
rescue Dalli::DalliError => e
|
302
|
-
|
306
|
+
log_dalli_error(e)
|
307
|
+
instrument_error(e) if instrument_errors?
|
303
308
|
raise if raise_errors?
|
304
309
|
nil
|
305
310
|
end
|
@@ -313,7 +318,8 @@ module ActiveSupport
|
|
313
318
|
connection = options.delete(:connection)
|
314
319
|
connection.send(method, key, value, expires_in, options)
|
315
320
|
rescue Dalli::DalliError => e
|
316
|
-
|
321
|
+
log_dalli_error(e)
|
322
|
+
instrument_error(e) if instrument_errors?
|
317
323
|
raise if raise_errors?
|
318
324
|
false
|
319
325
|
end
|
@@ -322,7 +328,8 @@ module ActiveSupport
|
|
322
328
|
def delete_entry(key, options) # :nodoc:
|
323
329
|
with { |c| c.delete(key) }
|
324
330
|
rescue Dalli::DalliError => e
|
325
|
-
|
331
|
+
log_dalli_error(e)
|
332
|
+
instrument_error(e) if instrument_errors?
|
326
333
|
raise if raise_errors?
|
327
334
|
false
|
328
335
|
end
|
@@ -334,6 +341,7 @@ module ActiveSupport
|
|
334
341
|
namespace = options[:namespace] if options
|
335
342
|
prefix = namespace.is_a?(Proc) ? namespace.call : namespace
|
336
343
|
key = "#{prefix}:#{key}" if prefix
|
344
|
+
key = "#{key[0, 213]}:md5:#{Digest::MD5.hexdigest(key)}" if key && key.size > 250
|
337
345
|
key
|
338
346
|
end
|
339
347
|
alias :normalize_key :namespaced_key
|
@@ -363,12 +371,26 @@ module ActiveSupport
|
|
363
371
|
key
|
364
372
|
end
|
365
373
|
|
366
|
-
def
|
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)
|
367
379
|
log(operation, key, options)
|
368
380
|
|
369
381
|
payload = { :key => key }
|
370
382
|
payload.merge!(options) if options.is_a?(Hash)
|
371
|
-
|
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
|
372
394
|
end
|
373
395
|
|
374
396
|
def log(operation, key, options=nil)
|
@@ -380,6 +402,10 @@ module ActiveSupport
|
|
380
402
|
!!@options[:raise_errors]
|
381
403
|
end
|
382
404
|
|
405
|
+
def instrument_errors?
|
406
|
+
!!@options[:instrument_errors]
|
407
|
+
end
|
408
|
+
|
383
409
|
# Make sure LocalCache is giving raw values, not `Entry`s, and
|
384
410
|
# respect `raw` option.
|
385
411
|
module LocalCacheEntryUnwrapAndRaw # :nodoc:
|
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 < RuntimeError; 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
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
|
@@ -475,19 +474,24 @@ module Dalli
|
|
475
474
|
if value.bytesize <= @options[:value_max_bytes]
|
476
475
|
yield
|
477
476
|
else
|
478
|
-
|
477
|
+
message = "Value for #{key} over max size: #{@options[:value_max_bytes]} <= #{value.bytesize}"
|
478
|
+
raise Dalli::ValueOverMaxSize, message if @options[:error_when_over_max_size]
|
479
|
+
|
480
|
+
Dalli.logger.warn message
|
479
481
|
false
|
480
482
|
end
|
481
483
|
end
|
482
484
|
|
483
|
-
# https://
|
485
|
+
# https://github.com/memcached/memcached/blob/master/doc/protocol.txt#L79
|
484
486
|
# > 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
487
|
MAX_ACCEPTABLE_EXPIRATION_INTERVAL = 30*24*60*60 # 30 days
|
486
488
|
def sanitize_ttl(ttl)
|
487
489
|
ttl_as_i = ttl.to_i
|
488
490
|
return ttl_as_i if ttl_as_i <= MAX_ACCEPTABLE_EXPIRATION_INTERVAL
|
491
|
+
now = Time.now.to_i
|
492
|
+
return ttl_as_i if ttl_as_i > now # already a timestamp
|
489
493
|
Dalli.logger.debug "Expiration interval (#{ttl_as_i}) too long for Memcached, converting to an expiration timestamp"
|
490
|
-
|
494
|
+
now + ttl_as_i
|
491
495
|
end
|
492
496
|
|
493
497
|
# Implements the NullObject pattern to store an application-defined value for 'Key not found' responses.
|
@@ -604,7 +608,7 @@ module Dalli
|
|
604
608
|
RESPONSE = 0x81
|
605
609
|
|
606
610
|
# Response codes taken from:
|
607
|
-
# https://
|
611
|
+
# https://github.com/memcached/memcached/wiki/BinaryProtocolRevamped#response-status
|
608
612
|
RESPONSE_CODES = {
|
609
613
|
0 => 'No error',
|
610
614
|
1 => 'Key not found',
|
@@ -698,7 +702,7 @@ module Dalli
|
|
698
702
|
req = [REQUEST, OPCODES[:auth_negotiation], 0, 0, 0, 0, 0, 0, 0].pack(FORMAT[:noop])
|
699
703
|
write(req)
|
700
704
|
|
701
|
-
(extras,
|
705
|
+
(extras, _type, status, count) = read_header.unpack(NORMAL_HEADER)
|
702
706
|
raise Dalli::NetworkError, "Unexpected message format: #{extras} #{count}" unless extras == 0 && count > 0
|
703
707
|
content = read(count).gsub(/\u0000/, ' ')
|
704
708
|
return (Dalli.logger.debug("Authentication not required/supported by server")) if status == 0x81
|
@@ -711,7 +715,7 @@ module Dalli
|
|
711
715
|
req = [REQUEST, OPCODES[:auth_request], mechanism.bytesize, 0, 0, 0, mechanism.bytesize + msg.bytesize, 0, 0, mechanism, msg].pack(FORMAT[:auth_request])
|
712
716
|
write(req)
|
713
717
|
|
714
|
-
(extras,
|
718
|
+
(extras, _type, status, count) = read_header.unpack(NORMAL_HEADER)
|
715
719
|
raise Dalli::NetworkError, "Unexpected message format: #{extras} #{count}" unless extras == 0 && count > 0
|
716
720
|
content = read(count)
|
717
721
|
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,17 +7,118 @@ 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
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
72
|
+
|
73
|
+
# Determine the default TTL for newly-created sessions
|
74
|
+
@default_ttl = ttl @default_options[:expire_after]
|
75
|
+
|
76
|
+
# Normalize and validate passed options
|
77
|
+
cache, mserv, mopts, popts = extract_dalli_options options
|
78
|
+
|
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
|
87
|
+
|
88
|
+
if @pool.respond_to?(:alive!) # is a Dalli::Client
|
89
|
+
@mutex = Mutex.new
|
90
|
+
|
91
|
+
@pool.alive!
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def get_session(env, sid)
|
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
|
102
|
+
end
|
103
|
+
end
|
104
|
+
[sid, session]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def set_session(env, session_id, new_session, options)
|
109
|
+
return false unless session_id
|
110
|
+
|
111
|
+
with_block(env, false) do |dc|
|
112
|
+
dc.set(session_id, new_session, ttl(options[:expire_after]))
|
113
|
+
session_id
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def destroy_session(env, session_id, options)
|
118
|
+
with_block(env) do |dc|
|
119
|
+
dc.delete(session_id)
|
120
|
+
generate_sid_with(dc) unless options[:drop]
|
121
|
+
end
|
20
122
|
end
|
21
123
|
|
22
124
|
if defined?(Abstract::Persisted)
|
@@ -33,46 +135,47 @@ module Rack
|
|
33
135
|
end
|
34
136
|
end
|
35
137
|
|
36
|
-
|
37
|
-
while true
|
38
|
-
sid = super
|
39
|
-
break sid unless @pool.get(sid)
|
40
|
-
end
|
41
|
-
end
|
138
|
+
private
|
42
139
|
|
43
|
-
def
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
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
|
52
155
|
end
|
156
|
+
|
157
|
+
[nil, mserv, mopts, popts]
|
53
158
|
end
|
54
159
|
|
55
|
-
|
56
|
-
|
57
|
-
expiry = options[:expire_after]
|
58
|
-
expiry = expiry.nil? ? 0 : expiry + 1
|
160
|
+
# Capture generate_sid's super so we can call it from generate_sid_with
|
161
|
+
alias_method :generate_sid_super, :generate_sid
|
59
162
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
163
|
+
def generate_sid
|
164
|
+
# no way to check env['rack.multithread'] here so fall back on
|
165
|
+
# Dalli::Client or ConnectionPool's internal mutex cf. our own
|
166
|
+
@pool.with {|dc| generate_sid_with(dc) }
|
64
167
|
end
|
65
168
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
69
|
-
|
169
|
+
def generate_sid_with(dc)
|
170
|
+
while true
|
171
|
+
sid = generate_sid_super
|
172
|
+
break sid unless dc.get(sid)
|
70
173
|
end
|
71
174
|
end
|
72
175
|
|
73
|
-
def
|
74
|
-
@mutex.lock if env['rack.multithread']
|
75
|
-
|
176
|
+
def with_block(env, default=nil, &block)
|
177
|
+
@mutex.lock if @mutex and env['rack.multithread']
|
178
|
+
@pool.with(&block)
|
76
179
|
rescue ::Dalli::DalliError, Errno::ECONNREFUSED
|
77
180
|
raise if $!.message =~ /undefined class/
|
78
181
|
if $VERBOSE
|
@@ -81,9 +184,12 @@ module Rack
|
|
81
184
|
end
|
82
185
|
default
|
83
186
|
ensure
|
84
|
-
@mutex.unlock if @mutex.locked?
|
187
|
+
@mutex.unlock if @mutex and @mutex.locked?
|
85
188
|
end
|
86
189
|
|
190
|
+
def ttl(expire_after)
|
191
|
+
expire_after.nil? ? 0 : expire_after + 1
|
192
|
+
end
|
87
193
|
end
|
88
194
|
end
|
89
195
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
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.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter M. Goldstein
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2018-03-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: minitest
|
@@ -169,7 +169,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
169
169
|
version: '0'
|
170
170
|
requirements: []
|
171
171
|
rubyforge_project:
|
172
|
-
rubygems_version: 2.
|
172
|
+
rubygems_version: 2.7.5
|
173
173
|
signing_key:
|
174
174
|
specification_version: 4
|
175
175
|
summary: High performance memcached client for Ruby
|