dalli 3.1.5 → 3.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/{History.md → CHANGELOG.md} +55 -2
- data/Gemfile +9 -4
- data/README.md +8 -2
- data/lib/dalli/client.rb +10 -12
- data/lib/dalli/key_manager.rb +11 -3
- data/lib/dalli/pid_cache.rb +40 -0
- data/lib/dalli/pipelined_getter.rb +1 -1
- data/lib/dalli/protocol/base.rb +25 -9
- data/lib/dalli/protocol/binary/request_formatter.rb +3 -3
- data/lib/dalli/protocol/binary/response_processor.rb +2 -2
- data/lib/dalli/protocol/binary/sasl_authentication.rb +1 -1
- data/lib/dalli/protocol/binary.rb +0 -7
- data/lib/dalli/protocol/connection_manager.rb +29 -16
- data/lib/dalli/protocol/meta/key_regularizer.rb +31 -0
- data/lib/dalli/protocol/meta/request_formatter.rb +121 -0
- data/lib/dalli/protocol/meta/response_processor.rb +211 -0
- data/lib/dalli/protocol/meta.rb +178 -0
- data/lib/dalli/protocol/response_buffer.rb +1 -0
- data/lib/dalli/protocol/server_config_parser.rb +2 -0
- data/lib/dalli/protocol.rb +11 -0
- data/lib/dalli/ring.rb +4 -2
- data/lib/dalli/servers_arg_normalizer.rb +1 -1
- data/lib/dalli/socket.rb +1 -1
- data/lib/dalli/version.rb +1 -1
- data/lib/dalli.rb +1 -0
- data/lib/rack/session/dalli.rb +16 -7
- metadata +11 -96
@@ -0,0 +1,211 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dalli
|
4
|
+
module Protocol
|
5
|
+
class Meta
|
6
|
+
##
|
7
|
+
# Class that encapsulates logic for processing meta protocol responses
|
8
|
+
# from memcached. Includes logic for pulling data from an IO source
|
9
|
+
# and parsing into local values. Handles errors on unexpected values.
|
10
|
+
##
|
11
|
+
class ResponseProcessor
|
12
|
+
EN = 'EN'
|
13
|
+
END_TOKEN = 'END'
|
14
|
+
EX = 'EX'
|
15
|
+
HD = 'HD'
|
16
|
+
MN = 'MN'
|
17
|
+
NF = 'NF'
|
18
|
+
NS = 'NS'
|
19
|
+
OK = 'OK'
|
20
|
+
RESET = 'RESET'
|
21
|
+
STAT = 'STAT'
|
22
|
+
VA = 'VA'
|
23
|
+
VERSION = 'VERSION'
|
24
|
+
|
25
|
+
def initialize(io_source, value_marshaller)
|
26
|
+
@io_source = io_source
|
27
|
+
@value_marshaller = value_marshaller
|
28
|
+
end
|
29
|
+
|
30
|
+
def meta_get_with_value(cache_nils: false)
|
31
|
+
tokens = error_on_unexpected!([VA, EN, HD])
|
32
|
+
return cache_nils ? ::Dalli::NOT_FOUND : nil if tokens.first == EN
|
33
|
+
return true unless tokens.first == VA
|
34
|
+
|
35
|
+
@value_marshaller.retrieve(read_line, bitflags_from_tokens(tokens))
|
36
|
+
end
|
37
|
+
|
38
|
+
def meta_get_with_value_and_cas
|
39
|
+
tokens = error_on_unexpected!([VA, EN, HD])
|
40
|
+
return [nil, 0] if tokens.first == EN
|
41
|
+
|
42
|
+
cas = cas_from_tokens(tokens)
|
43
|
+
return [nil, cas] unless tokens.first == VA
|
44
|
+
|
45
|
+
[@value_marshaller.retrieve(read_line, bitflags_from_tokens(tokens)), cas]
|
46
|
+
end
|
47
|
+
|
48
|
+
def meta_get_without_value
|
49
|
+
tokens = error_on_unexpected!([EN, HD])
|
50
|
+
tokens.first == EN ? nil : true
|
51
|
+
end
|
52
|
+
|
53
|
+
def meta_set_with_cas
|
54
|
+
tokens = error_on_unexpected!([HD, NS, NF, EX])
|
55
|
+
return false unless tokens.first == HD
|
56
|
+
|
57
|
+
cas_from_tokens(tokens)
|
58
|
+
end
|
59
|
+
|
60
|
+
def meta_set_append_prepend
|
61
|
+
tokens = error_on_unexpected!([HD, NS, NF, EX])
|
62
|
+
return false unless tokens.first == HD
|
63
|
+
|
64
|
+
true
|
65
|
+
end
|
66
|
+
|
67
|
+
def meta_delete
|
68
|
+
tokens = error_on_unexpected!([HD, NF, EX])
|
69
|
+
tokens.first == HD
|
70
|
+
end
|
71
|
+
|
72
|
+
def decr_incr
|
73
|
+
tokens = error_on_unexpected!([VA, NF, NS, EX])
|
74
|
+
return false if [NS, EX].include?(tokens.first)
|
75
|
+
return nil if tokens.first == NF
|
76
|
+
|
77
|
+
read_line.to_i
|
78
|
+
end
|
79
|
+
|
80
|
+
def stats
|
81
|
+
tokens = error_on_unexpected!([END_TOKEN, STAT])
|
82
|
+
values = {}
|
83
|
+
while tokens.first != END_TOKEN
|
84
|
+
values[tokens[1]] = tokens[2]
|
85
|
+
tokens = next_line_to_tokens
|
86
|
+
end
|
87
|
+
values
|
88
|
+
end
|
89
|
+
|
90
|
+
def flush
|
91
|
+
error_on_unexpected!([OK])
|
92
|
+
|
93
|
+
true
|
94
|
+
end
|
95
|
+
|
96
|
+
def reset
|
97
|
+
error_on_unexpected!([RESET])
|
98
|
+
|
99
|
+
true
|
100
|
+
end
|
101
|
+
|
102
|
+
def version
|
103
|
+
tokens = error_on_unexpected!([VERSION])
|
104
|
+
tokens.last
|
105
|
+
end
|
106
|
+
|
107
|
+
def consume_all_responses_until_mn
|
108
|
+
tokens = next_line_to_tokens
|
109
|
+
|
110
|
+
tokens = next_line_to_tokens while tokens.first != MN
|
111
|
+
true
|
112
|
+
end
|
113
|
+
|
114
|
+
def tokens_from_header_buffer(buf)
|
115
|
+
header = header_from_buffer(buf)
|
116
|
+
tokens = header.split
|
117
|
+
header_len = header.bytesize + TERMINATOR.length
|
118
|
+
body_len = body_len_from_tokens(tokens)
|
119
|
+
[tokens, header_len, body_len]
|
120
|
+
end
|
121
|
+
|
122
|
+
def full_response_from_buffer(tokens, body, resp_size)
|
123
|
+
value = @value_marshaller.retrieve(body, bitflags_from_tokens(tokens))
|
124
|
+
[resp_size, tokens.first == VA, cas_from_tokens(tokens), key_from_tokens(tokens), value]
|
125
|
+
end
|
126
|
+
|
127
|
+
##
|
128
|
+
# This method returns an array of values used in a pipelined
|
129
|
+
# getk process. The first value is the number of bytes by
|
130
|
+
# which to advance the pointer in the buffer. If the
|
131
|
+
# complete response is found in the buffer, this will
|
132
|
+
# be the response size. Otherwise it is zero.
|
133
|
+
#
|
134
|
+
# The remaining three values in the array are the ResponseHeader,
|
135
|
+
# key, and value.
|
136
|
+
##
|
137
|
+
def getk_response_from_buffer(buf)
|
138
|
+
# There's no header in the buffer, so don't advance
|
139
|
+
return [0, nil, nil, nil, nil] unless contains_header?(buf)
|
140
|
+
|
141
|
+
tokens, header_len, body_len = tokens_from_header_buffer(buf)
|
142
|
+
|
143
|
+
# We have a complete response that has no body.
|
144
|
+
# This is either the response to the terminating
|
145
|
+
# noop or, if the status is not MN, an intermediate
|
146
|
+
# error response that needs to be discarded.
|
147
|
+
return [header_len, true, nil, nil, nil] if body_len.zero?
|
148
|
+
|
149
|
+
resp_size = header_len + body_len + TERMINATOR.length
|
150
|
+
# The header is in the buffer, but the body is not. As we don't have
|
151
|
+
# a complete response, don't advance the buffer
|
152
|
+
return [0, nil, nil, nil, nil] unless buf.bytesize >= resp_size
|
153
|
+
|
154
|
+
# The full response is in our buffer, so parse it and return
|
155
|
+
# the values
|
156
|
+
body = buf.slice(header_len, body_len)
|
157
|
+
full_response_from_buffer(tokens, body, resp_size)
|
158
|
+
end
|
159
|
+
|
160
|
+
def contains_header?(buf)
|
161
|
+
buf.include?(TERMINATOR)
|
162
|
+
end
|
163
|
+
|
164
|
+
def header_from_buffer(buf)
|
165
|
+
buf.split(TERMINATOR, 2).first
|
166
|
+
end
|
167
|
+
|
168
|
+
def error_on_unexpected!(expected_codes)
|
169
|
+
tokens = next_line_to_tokens
|
170
|
+
raise Dalli::DalliError, "Response error: #{tokens.first}" unless expected_codes.include?(tokens.first)
|
171
|
+
|
172
|
+
tokens
|
173
|
+
end
|
174
|
+
|
175
|
+
def bitflags_from_tokens(tokens)
|
176
|
+
value_from_tokens(tokens, 'f')&.to_i
|
177
|
+
end
|
178
|
+
|
179
|
+
def cas_from_tokens(tokens)
|
180
|
+
value_from_tokens(tokens, 'c')&.to_i
|
181
|
+
end
|
182
|
+
|
183
|
+
def key_from_tokens(tokens)
|
184
|
+
encoded_key = value_from_tokens(tokens, 'k')
|
185
|
+
base64_encoded = tokens.any?('b')
|
186
|
+
KeyRegularizer.decode(encoded_key, base64_encoded)
|
187
|
+
end
|
188
|
+
|
189
|
+
def body_len_from_tokens(tokens)
|
190
|
+
value_from_tokens(tokens, 's')&.to_i
|
191
|
+
end
|
192
|
+
|
193
|
+
def value_from_tokens(tokens, flag)
|
194
|
+
bitflags_token = tokens.find { |t| t.start_with?(flag) }
|
195
|
+
return 0 unless bitflags_token
|
196
|
+
|
197
|
+
bitflags_token[1..]
|
198
|
+
end
|
199
|
+
|
200
|
+
def read_line
|
201
|
+
@io_source.read_line&.chomp!(TERMINATOR)
|
202
|
+
end
|
203
|
+
|
204
|
+
def next_line_to_tokens
|
205
|
+
line = read_line
|
206
|
+
line&.split || []
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
require 'socket'
|
5
|
+
require 'timeout'
|
6
|
+
|
7
|
+
module Dalli
|
8
|
+
module Protocol
|
9
|
+
##
|
10
|
+
# Access point for a single Memcached server, accessed via Memcached's meta
|
11
|
+
# protocol. Contains logic for managing connection state to the server (retries, etc),
|
12
|
+
# formatting requests to the server, and unpacking responses.
|
13
|
+
##
|
14
|
+
class Meta < Base
|
15
|
+
TERMINATOR = "\r\n"
|
16
|
+
|
17
|
+
def response_processor
|
18
|
+
@response_processor ||= ResponseProcessor.new(@connection_manager, @value_marshaller)
|
19
|
+
end
|
20
|
+
|
21
|
+
# NOTE: Additional public methods should be overridden in Dalli::Threadsafe
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# Retrieval Commands
|
26
|
+
def get(key, options = nil)
|
27
|
+
encoded_key, base64 = KeyRegularizer.encode(key)
|
28
|
+
req = RequestFormatter.meta_get(key: encoded_key, base64: base64)
|
29
|
+
write(req)
|
30
|
+
response_processor.meta_get_with_value(cache_nils: cache_nils?(options))
|
31
|
+
end
|
32
|
+
|
33
|
+
def quiet_get_request(key)
|
34
|
+
encoded_key, base64 = KeyRegularizer.encode(key)
|
35
|
+
RequestFormatter.meta_get(key: encoded_key, return_cas: true, base64: base64, quiet: true)
|
36
|
+
end
|
37
|
+
|
38
|
+
def gat(key, ttl, options = nil)
|
39
|
+
ttl = TtlSanitizer.sanitize(ttl)
|
40
|
+
encoded_key, base64 = KeyRegularizer.encode(key)
|
41
|
+
req = RequestFormatter.meta_get(key: encoded_key, ttl: ttl, base64: base64)
|
42
|
+
write(req)
|
43
|
+
response_processor.meta_get_with_value(cache_nils: cache_nils?(options))
|
44
|
+
end
|
45
|
+
|
46
|
+
def touch(key, ttl)
|
47
|
+
ttl = TtlSanitizer.sanitize(ttl)
|
48
|
+
encoded_key, base64 = KeyRegularizer.encode(key)
|
49
|
+
req = RequestFormatter.meta_get(key: encoded_key, ttl: ttl, value: false, base64: base64)
|
50
|
+
write(req)
|
51
|
+
response_processor.meta_get_without_value
|
52
|
+
end
|
53
|
+
|
54
|
+
# TODO: This is confusing, as there's a cas command in memcached
|
55
|
+
# and this isn't it. Maybe rename? Maybe eliminate?
|
56
|
+
def cas(key)
|
57
|
+
encoded_key, base64 = KeyRegularizer.encode(key)
|
58
|
+
req = RequestFormatter.meta_get(key: encoded_key, value: true, return_cas: true, base64: base64)
|
59
|
+
write(req)
|
60
|
+
response_processor.meta_get_with_value_and_cas
|
61
|
+
end
|
62
|
+
|
63
|
+
# Storage Commands
|
64
|
+
def set(key, value, ttl, cas, options)
|
65
|
+
write_storage_req(:set, key, value, ttl, cas, options)
|
66
|
+
response_processor.meta_set_with_cas unless quiet?
|
67
|
+
end
|
68
|
+
|
69
|
+
def add(key, value, ttl, options)
|
70
|
+
write_storage_req(:add, key, value, ttl, nil, options)
|
71
|
+
response_processor.meta_set_with_cas unless quiet?
|
72
|
+
end
|
73
|
+
|
74
|
+
def replace(key, value, ttl, cas, options)
|
75
|
+
write_storage_req(:replace, key, value, ttl, cas, options)
|
76
|
+
response_processor.meta_set_with_cas unless quiet?
|
77
|
+
end
|
78
|
+
|
79
|
+
# rubocop:disable Metrics/ParameterLists
|
80
|
+
def write_storage_req(mode, key, raw_value, ttl = nil, cas = nil, options = {})
|
81
|
+
(value, bitflags) = @value_marshaller.store(key, raw_value, options)
|
82
|
+
ttl = TtlSanitizer.sanitize(ttl) if ttl
|
83
|
+
encoded_key, base64 = KeyRegularizer.encode(key)
|
84
|
+
req = RequestFormatter.meta_set(key: encoded_key, value: value,
|
85
|
+
bitflags: bitflags, cas: cas,
|
86
|
+
ttl: ttl, mode: mode, quiet: quiet?, base64: base64)
|
87
|
+
write(req)
|
88
|
+
end
|
89
|
+
# rubocop:enable Metrics/ParameterLists
|
90
|
+
|
91
|
+
def append(key, value)
|
92
|
+
write_append_prepend_req(:append, key, value)
|
93
|
+
response_processor.meta_set_append_prepend unless quiet?
|
94
|
+
end
|
95
|
+
|
96
|
+
def prepend(key, value)
|
97
|
+
write_append_prepend_req(:prepend, key, value)
|
98
|
+
response_processor.meta_set_append_prepend unless quiet?
|
99
|
+
end
|
100
|
+
|
101
|
+
# rubocop:disable Metrics/ParameterLists
|
102
|
+
def write_append_prepend_req(mode, key, value, ttl = nil, cas = nil, _options = {})
|
103
|
+
ttl = TtlSanitizer.sanitize(ttl) if ttl
|
104
|
+
encoded_key, base64 = KeyRegularizer.encode(key)
|
105
|
+
req = RequestFormatter.meta_set(key: encoded_key, value: value, base64: base64,
|
106
|
+
cas: cas, ttl: ttl, mode: mode, quiet: quiet?)
|
107
|
+
write(req)
|
108
|
+
end
|
109
|
+
# rubocop:enable Metrics/ParameterLists
|
110
|
+
|
111
|
+
# Delete Commands
|
112
|
+
def delete(key, cas)
|
113
|
+
encoded_key, base64 = KeyRegularizer.encode(key)
|
114
|
+
req = RequestFormatter.meta_delete(key: encoded_key, cas: cas,
|
115
|
+
base64: base64, quiet: quiet?)
|
116
|
+
write(req)
|
117
|
+
response_processor.meta_delete unless quiet?
|
118
|
+
end
|
119
|
+
|
120
|
+
# Arithmetic Commands
|
121
|
+
def decr(key, count, ttl, initial)
|
122
|
+
decr_incr false, key, count, ttl, initial
|
123
|
+
end
|
124
|
+
|
125
|
+
def incr(key, count, ttl, initial)
|
126
|
+
decr_incr true, key, count, ttl, initial
|
127
|
+
end
|
128
|
+
|
129
|
+
def decr_incr(incr, key, delta, ttl, initial)
|
130
|
+
ttl = initial ? TtlSanitizer.sanitize(ttl) : nil # Only set a TTL if we want to set a value on miss
|
131
|
+
encoded_key, base64 = KeyRegularizer.encode(key)
|
132
|
+
write(RequestFormatter.meta_arithmetic(key: encoded_key, delta: delta, initial: initial, incr: incr, ttl: ttl,
|
133
|
+
quiet: quiet?, base64: base64))
|
134
|
+
response_processor.decr_incr unless quiet?
|
135
|
+
end
|
136
|
+
|
137
|
+
# Other Commands
|
138
|
+
def flush(delay = 0)
|
139
|
+
write(RequestFormatter.flush(delay: delay))
|
140
|
+
response_processor.flush unless quiet?
|
141
|
+
end
|
142
|
+
|
143
|
+
# Noop is a keepalive operation but also used to demarcate the end of a set of pipelined commands.
|
144
|
+
# We need to read all the responses at once.
|
145
|
+
def noop
|
146
|
+
write_noop
|
147
|
+
response_processor.consume_all_responses_until_mn
|
148
|
+
end
|
149
|
+
|
150
|
+
def stats(info = nil)
|
151
|
+
write(RequestFormatter.stats(info))
|
152
|
+
response_processor.stats
|
153
|
+
end
|
154
|
+
|
155
|
+
def reset_stats
|
156
|
+
write(RequestFormatter.stats('reset'))
|
157
|
+
response_processor.reset
|
158
|
+
end
|
159
|
+
|
160
|
+
def version
|
161
|
+
write(RequestFormatter.version)
|
162
|
+
response_processor.version
|
163
|
+
end
|
164
|
+
|
165
|
+
def write_noop
|
166
|
+
write(RequestFormatter.meta_noop)
|
167
|
+
end
|
168
|
+
|
169
|
+
def authenticate_connection
|
170
|
+
raise Dalli::DalliError, 'Authentication not supported for the meta protocol.'
|
171
|
+
end
|
172
|
+
|
173
|
+
require_relative 'meta/key_regularizer'
|
174
|
+
require_relative 'meta/request_formatter'
|
175
|
+
require_relative 'meta/response_processor'
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
data/lib/dalli/protocol.rb
CHANGED
@@ -1,8 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'timeout'
|
4
|
+
|
3
5
|
module Dalli
|
4
6
|
module Protocol
|
5
7
|
# Preserved for backwards compatibility. Should be removed in 4.0
|
6
8
|
NOT_FOUND = ::Dalli::NOT_FOUND
|
9
|
+
|
10
|
+
# Ruby 3.2 raises IO::TimeoutError on blocking reads/writes, but
|
11
|
+
# it is not defined in earlier Ruby versions.
|
12
|
+
TIMEOUT_ERRORS =
|
13
|
+
if defined?(IO::TimeoutError)
|
14
|
+
[Timeout::Error, IO::TimeoutError]
|
15
|
+
else
|
16
|
+
[Timeout::Error]
|
17
|
+
end
|
7
18
|
end
|
8
19
|
end
|
data/lib/dalli/ring.rb
CHANGED
@@ -23,8 +23,10 @@ module Dalli
|
|
23
23
|
|
24
24
|
attr_accessor :servers, :continuum
|
25
25
|
|
26
|
-
def initialize(
|
27
|
-
@servers =
|
26
|
+
def initialize(servers_arg, protocol_implementation, options)
|
27
|
+
@servers = servers_arg.map do |s|
|
28
|
+
protocol_implementation.new(s, options)
|
29
|
+
end
|
28
30
|
@continuum = nil
|
29
31
|
@continuum = build_continuum(servers) if servers.size > 1
|
30
32
|
|
data/lib/dalli/socket.rb
CHANGED
data/lib/dalli/version.rb
CHANGED
data/lib/dalli.rb
CHANGED
@@ -65,6 +65,7 @@ require_relative 'dalli/protocol'
|
|
65
65
|
require_relative 'dalli/protocol/base'
|
66
66
|
require_relative 'dalli/protocol/binary'
|
67
67
|
require_relative 'dalli/protocol/connection_manager'
|
68
|
+
require_relative 'dalli/protocol/meta'
|
68
69
|
require_relative 'dalli/protocol/response_buffer'
|
69
70
|
require_relative 'dalli/protocol/server_config_parser'
|
70
71
|
require_relative 'dalli/protocol/ttl_sanitizer'
|
data/lib/rack/session/dalli.rb
CHANGED
@@ -82,6 +82,9 @@ module Rack
|
|
82
82
|
def write_session(_req, sid, session, options)
|
83
83
|
return false unless sid
|
84
84
|
|
85
|
+
key = memcached_key_from_sid(sid)
|
86
|
+
return false unless key
|
87
|
+
|
85
88
|
with_dalli_client(false) do |dc|
|
86
89
|
dc.set(memcached_key_from_sid(sid), session, ttl(options[:expire_after]))
|
87
90
|
sid
|
@@ -90,7 +93,8 @@ module Rack
|
|
90
93
|
|
91
94
|
def delete_session(_req, sid, options)
|
92
95
|
with_dalli_client do |dc|
|
93
|
-
|
96
|
+
key = memcached_key_from_sid(sid)
|
97
|
+
dc.delete(key) if key
|
94
98
|
generate_sid_with(dc) unless options[:drop]
|
95
99
|
end
|
96
100
|
end
|
@@ -98,20 +102,24 @@ module Rack
|
|
98
102
|
private
|
99
103
|
|
100
104
|
def memcached_key_from_sid(sid)
|
101
|
-
sid.private_id
|
105
|
+
sid.private_id if sid.respond_to?(:private_id)
|
102
106
|
end
|
103
107
|
|
104
108
|
def existing_session_for_sid(client, sid)
|
105
109
|
return nil unless sid && !sid.empty?
|
106
110
|
|
107
|
-
|
111
|
+
key = memcached_key_from_sid(sid)
|
112
|
+
return nil if key.nil?
|
113
|
+
|
114
|
+
client.get(key)
|
108
115
|
end
|
109
116
|
|
110
117
|
def create_sid_with_empty_session(client)
|
111
118
|
loop do
|
112
119
|
sid = generate_sid_with(client)
|
120
|
+
key = memcached_key_from_sid(sid)
|
113
121
|
|
114
|
-
break sid if client.add(
|
122
|
+
break sid if key && client.add(key, {}, @default_ttl)
|
115
123
|
end
|
116
124
|
end
|
117
125
|
|
@@ -119,7 +127,8 @@ module Rack
|
|
119
127
|
loop do
|
120
128
|
raw_sid = generate_sid
|
121
129
|
sid = raw_sid.is_a?(String) ? Rack::Session::SessionId.new(raw_sid) : raw_sid
|
122
|
-
|
130
|
+
key = memcached_key_from_sid(sid)
|
131
|
+
break sid unless key && client.get(key)
|
123
132
|
end
|
124
133
|
end
|
125
134
|
|
@@ -161,7 +170,7 @@ module Rack
|
|
161
170
|
def ensure_connection_pool_added!
|
162
171
|
require 'connection_pool'
|
163
172
|
rescue LoadError => e
|
164
|
-
warn "You don't have connection_pool installed in your application. "\
|
173
|
+
warn "You don't have connection_pool installed in your application. " \
|
165
174
|
'Please add it to your Gemfile and run bundle install'
|
166
175
|
raise e
|
167
176
|
end
|
@@ -169,7 +178,7 @@ module Rack
|
|
169
178
|
def with_dalli_client(result_on_error = nil, &block)
|
170
179
|
@data.with(&block)
|
171
180
|
rescue ::Dalli::DalliError, Errno::ECONNREFUSED
|
172
|
-
raise if
|
181
|
+
raise if $ERROR_INFO.message.include?('undefined class')
|
173
182
|
|
174
183
|
if $VERBOSE
|
175
184
|
warn "#{self} is unable to find memcached server."
|