dalli 2.0.1 → 3.2.8
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 +7 -0
- data/CHANGELOG.md +671 -0
- data/Gemfile +15 -3
- data/LICENSE +1 -1
- data/README.md +33 -148
- data/lib/dalli/cas/client.rb +3 -0
- data/lib/dalli/client.rb +293 -131
- data/lib/dalli/compressor.rb +40 -0
- data/lib/dalli/key_manager.rb +121 -0
- data/lib/dalli/options.rb +22 -4
- data/lib/dalli/pid_cache.rb +40 -0
- data/lib/dalli/pipelined_getter.rb +177 -0
- data/lib/dalli/protocol/base.rb +250 -0
- data/lib/dalli/protocol/binary/request_formatter.rb +117 -0
- data/lib/dalli/protocol/binary/response_header.rb +36 -0
- data/lib/dalli/protocol/binary/response_processor.rb +239 -0
- data/lib/dalli/protocol/binary/sasl_authentication.rb +60 -0
- data/lib/dalli/protocol/binary.rb +173 -0
- data/lib/dalli/protocol/connection_manager.rb +255 -0
- 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 +54 -0
- data/lib/dalli/protocol/server_config_parser.rb +86 -0
- data/lib/dalli/protocol/ttl_sanitizer.rb +45 -0
- data/lib/dalli/protocol/value_compressor.rb +85 -0
- data/lib/dalli/protocol/value_marshaller.rb +59 -0
- data/lib/dalli/protocol/value_serializer.rb +91 -0
- data/lib/dalli/protocol.rb +19 -0
- data/lib/dalli/ring.rb +98 -50
- data/lib/dalli/server.rb +4 -524
- data/lib/dalli/servers_arg_normalizer.rb +54 -0
- data/lib/dalli/socket.rb +154 -53
- data/lib/dalli/version.rb +5 -1
- data/lib/dalli.rb +49 -13
- data/lib/rack/session/dalli.rb +169 -26
- metadata +53 -88
- data/History.md +0 -262
- data/Performance.md +0 -42
- data/Rakefile +0 -39
- data/dalli.gemspec +0 -28
- data/lib/action_dispatch/middleware/session/dalli_store.rb +0 -76
- data/lib/active_support/cache/dalli_store.rb +0 -203
- data/test/abstract_unit.rb +0 -281
- data/test/benchmark_test.rb +0 -187
- data/test/helper.rb +0 -41
- data/test/memcached_mock.rb +0 -113
- data/test/test_active_support.rb +0 -163
- data/test/test_dalli.rb +0 -461
- data/test/test_encoding.rb +0 -43
- data/test/test_failover.rb +0 -107
- data/test/test_network.rb +0 -54
- data/test/test_ring.rb +0 -85
- data/test/test_sasl.rb +0 -83
- data/test/test_session_store.rb +0 -224
data/lib/dalli/socket.rb
CHANGED
@@ -1,76 +1,177 @@
|
|
1
|
-
|
2
|
-
require 'kgio'
|
3
|
-
puts "Using kgio socket IO" if defined?($TESTING) && $TESTING
|
1
|
+
# frozen_string_literal: true
|
4
2
|
|
5
|
-
|
6
|
-
|
3
|
+
require 'openssl'
|
4
|
+
require 'rbconfig'
|
7
5
|
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
module Dalli
|
7
|
+
##
|
8
|
+
# Various socket implementations used by Dalli.
|
9
|
+
##
|
10
|
+
module Socket
|
11
|
+
##
|
12
|
+
# Common methods for all socket implementations.
|
13
|
+
##
|
14
|
+
module InstanceMethods
|
15
|
+
def readfull(count)
|
16
|
+
value = String.new(capacity: count + 1)
|
17
|
+
loop do
|
18
|
+
result = read_nonblock(count - value.bytesize, exception: false)
|
19
|
+
value << result if append_to_buffer?(result)
|
20
|
+
break if value.bytesize == count
|
21
|
+
end
|
22
|
+
value
|
23
|
+
end
|
11
24
|
|
12
|
-
|
13
|
-
|
14
|
-
|
25
|
+
def read_available
|
26
|
+
value = +''
|
27
|
+
loop do
|
28
|
+
result = read_nonblock(8196, exception: false)
|
29
|
+
break if WAIT_RCS.include?(result)
|
30
|
+
raise Errno::ECONNRESET, "Connection reset: #{logged_options.inspect}" unless result
|
15
31
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
sock
|
23
|
-
end
|
32
|
+
value << result
|
33
|
+
end
|
34
|
+
value
|
35
|
+
end
|
36
|
+
|
37
|
+
WAIT_RCS = %i[wait_writable wait_readable].freeze
|
24
38
|
|
25
|
-
|
39
|
+
def append_to_buffer?(result)
|
40
|
+
raise Timeout::Error, "IO timeout: #{logged_options.inspect}" if nonblock_timed_out?(result)
|
41
|
+
raise Errno::ECONNRESET, "Connection reset: #{logged_options.inspect}" unless result
|
26
42
|
|
27
|
-
|
28
|
-
value = ''
|
29
|
-
loop do
|
30
|
-
value << kgio_read!(count - value.bytesize)
|
31
|
-
break if value.bytesize == count
|
43
|
+
!WAIT_RCS.include?(result)
|
32
44
|
end
|
33
|
-
value
|
34
|
-
end
|
35
45
|
|
36
|
-
|
46
|
+
def nonblock_timed_out?(result)
|
47
|
+
return true if result == :wait_readable && !wait_readable(options[:socket_timeout])
|
37
48
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
end
|
49
|
+
# TODO: Do we actually need this? Looks to be only used in read_nonblock
|
50
|
+
result == :wait_writable && !wait_writable(options[:socket_timeout])
|
51
|
+
end
|
42
52
|
|
43
|
-
|
53
|
+
FILTERED_OUT_OPTIONS = %i[username password].freeze
|
54
|
+
def logged_options
|
55
|
+
options.reject { |k, _| FILTERED_OUT_OPTIONS.include? k }
|
56
|
+
end
|
57
|
+
end
|
44
58
|
|
45
|
-
|
46
|
-
|
47
|
-
|
59
|
+
##
|
60
|
+
# Wraps the below TCP socket class in the case where the client
|
61
|
+
# has configured a TLS/SSL connection between Dalli and the
|
62
|
+
# Memcached server.
|
63
|
+
##
|
64
|
+
class SSLSocket < ::OpenSSL::SSL::SSLSocket
|
65
|
+
include Dalli::Socket::InstanceMethods
|
66
|
+
def options
|
67
|
+
io.options
|
68
|
+
end
|
48
69
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
70
|
+
unless method_defined?(:wait_readable)
|
71
|
+
def wait_readable(timeout = nil)
|
72
|
+
to_io.wait_readable(timeout)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
unless method_defined?(:wait_writable)
|
77
|
+
def wait_writable(timeout = nil)
|
78
|
+
to_io.wait_writable(timeout)
|
79
|
+
end
|
55
80
|
end
|
56
81
|
end
|
57
82
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
83
|
+
##
|
84
|
+
# A standard TCP socket between the Dalli client and the Memcached server.
|
85
|
+
##
|
86
|
+
class TCP < TCPSocket
|
87
|
+
include Dalli::Socket::InstanceMethods
|
88
|
+
# options - supports enhanced logging in the case of a timeout
|
89
|
+
attr_accessor :options
|
90
|
+
|
91
|
+
def self.open(host, port, options = {})
|
92
|
+
create_socket_with_timeout(host, port, options) do |sock|
|
93
|
+
sock.options = { host: host, port: port }.merge(options)
|
94
|
+
init_socket_options(sock, options)
|
95
|
+
|
96
|
+
options[:ssl_context] ? wrapping_ssl_socket(sock, host, options[:ssl_context]) : sock
|
64
97
|
end
|
65
|
-
|
66
|
-
|
67
|
-
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.create_socket_with_timeout(host, port, options)
|
101
|
+
# Check that TCPSocket#initialize was not overwritten by resolv-replace gem
|
102
|
+
# (part of ruby standard library since 3.0.0, should be removed in 3.4.0),
|
103
|
+
# as it does not handle keyword arguments correctly.
|
104
|
+
# To check this we are using the fact that resolv-replace
|
105
|
+
# aliases TCPSocket#initialize method to #original_resolv_initialize.
|
106
|
+
# https://github.com/ruby/resolv-replace/blob/v0.1.1/lib/resolv-replace.rb#L21
|
107
|
+
if RUBY_VERSION >= '3.0' &&
|
108
|
+
!::TCPSocket.private_instance_methods.include?(:original_resolv_initialize)
|
109
|
+
sock = new(host, port, connect_timeout: options[:socket_timeout])
|
110
|
+
yield(sock)
|
68
111
|
else
|
69
|
-
|
112
|
+
Timeout.timeout(options[:socket_timeout]) do
|
113
|
+
sock = new(host, port)
|
114
|
+
yield(sock)
|
115
|
+
end
|
70
116
|
end
|
71
117
|
end
|
72
|
-
|
118
|
+
|
119
|
+
def self.init_socket_options(sock, options)
|
120
|
+
sock.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, true)
|
121
|
+
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true) if options[:keepalive]
|
122
|
+
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_RCVBUF, options[:rcvbuf]) if options[:rcvbuf]
|
123
|
+
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_SNDBUF, options[:sndbuf]) if options[:sndbuf]
|
124
|
+
|
125
|
+
return unless options[:socket_timeout]
|
126
|
+
|
127
|
+
seconds, fractional = options[:socket_timeout].divmod(1)
|
128
|
+
microseconds = fractional * 1_000_000
|
129
|
+
timeval = [seconds, microseconds].pack('l_2')
|
130
|
+
|
131
|
+
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_RCVTIMEO, timeval)
|
132
|
+
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_SNDTIMEO, timeval)
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.wrapping_ssl_socket(tcp_socket, host, ssl_context)
|
136
|
+
ssl_socket = Dalli::Socket::SSLSocket.new(tcp_socket, ssl_context)
|
137
|
+
ssl_socket.hostname = host
|
138
|
+
ssl_socket.sync_close = true
|
139
|
+
ssl_socket.connect
|
140
|
+
ssl_socket
|
141
|
+
end
|
73
142
|
end
|
74
143
|
|
144
|
+
if /mingw|mswin/.match?(RbConfig::CONFIG['host_os'])
|
145
|
+
##
|
146
|
+
# UNIX domain sockets are not supported on Windows platforms.
|
147
|
+
##
|
148
|
+
class UNIX
|
149
|
+
def initialize(*_args)
|
150
|
+
raise Dalli::DalliError, 'Unix sockets are not supported on Windows platform.'
|
151
|
+
end
|
152
|
+
end
|
153
|
+
else
|
154
|
+
|
155
|
+
##
|
156
|
+
# UNIX represents a UNIX domain socket, which is an interprocess communication
|
157
|
+
# mechanism between processes on the same host. Used when the Memcached server
|
158
|
+
# is running on the same machine as the Dalli client.
|
159
|
+
##
|
160
|
+
class UNIX < UNIXSocket
|
161
|
+
include Dalli::Socket::InstanceMethods
|
162
|
+
|
163
|
+
# options - supports enhanced logging in the case of a timeout
|
164
|
+
# server - used to support IO.select in the pipelined getter
|
165
|
+
attr_accessor :options
|
166
|
+
|
167
|
+
def self.open(path, options = {})
|
168
|
+
Timeout.timeout(options[:socket_timeout]) do
|
169
|
+
sock = new(path)
|
170
|
+
sock.options = { path: path }.merge(options)
|
171
|
+
sock
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
75
176
|
end
|
76
177
|
end
|
data/lib/dalli/version.rb
CHANGED
data/lib/dalli.rb
CHANGED
@@ -1,32 +1,50 @@
|
|
1
|
-
|
2
|
-
require 'dalli/ring'
|
3
|
-
require 'dalli/server'
|
4
|
-
require 'dalli/socket'
|
5
|
-
require 'dalli/version'
|
6
|
-
require 'dalli/options'
|
1
|
+
# frozen_string_literal: true
|
7
2
|
|
3
|
+
##
|
4
|
+
# Namespace for all Dalli code.
|
5
|
+
##
|
8
6
|
module Dalli
|
7
|
+
autoload :Server, 'dalli/server'
|
8
|
+
|
9
9
|
# generic error
|
10
10
|
class DalliError < RuntimeError; end
|
11
|
+
|
11
12
|
# socket/server communication error
|
12
13
|
class NetworkError < DalliError; end
|
14
|
+
|
13
15
|
# no server available/alive error
|
14
16
|
class RingError < DalliError; end
|
15
|
-
|
17
|
+
|
18
|
+
# application error in marshalling serialization
|
16
19
|
class MarshalError < DalliError; end
|
17
20
|
|
21
|
+
# application error in marshalling deserialization or decompression
|
22
|
+
class UnmarshalError < DalliError; end
|
23
|
+
|
24
|
+
# payload too big for memcached
|
25
|
+
class ValueOverMaxSize < DalliError; end
|
26
|
+
|
27
|
+
# operation is not permitted in a multi block
|
28
|
+
class NotPermittedMultiOpError < DalliError; end
|
29
|
+
|
30
|
+
# Implements the NullObject pattern to store an application-defined value for 'Key not found' responses.
|
31
|
+
class NilObject; end # rubocop:disable Lint/EmptyClass
|
32
|
+
NOT_FOUND = NilObject.new
|
33
|
+
|
34
|
+
QUIET = :dalli_multi
|
35
|
+
|
18
36
|
def self.logger
|
19
|
-
@logger ||=
|
37
|
+
@logger ||= rails_logger || default_logger
|
20
38
|
end
|
21
39
|
|
22
40
|
def self.rails_logger
|
23
41
|
(defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger) ||
|
24
|
-
|
42
|
+
(defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER.respond_to?(:debug) && RAILS_DEFAULT_LOGGER)
|
25
43
|
end
|
26
44
|
|
27
45
|
def self.default_logger
|
28
46
|
require 'logger'
|
29
|
-
l = Logger.new(
|
47
|
+
l = Logger.new($stdout)
|
30
48
|
l.level = Logger::INFO
|
31
49
|
l
|
32
50
|
end
|
@@ -36,6 +54,24 @@ module Dalli
|
|
36
54
|
end
|
37
55
|
end
|
38
56
|
|
39
|
-
|
40
|
-
|
41
|
-
|
57
|
+
require_relative 'dalli/version'
|
58
|
+
|
59
|
+
require_relative 'dalli/compressor'
|
60
|
+
require_relative 'dalli/client'
|
61
|
+
require_relative 'dalli/key_manager'
|
62
|
+
require_relative 'dalli/pipelined_getter'
|
63
|
+
require_relative 'dalli/ring'
|
64
|
+
require_relative 'dalli/protocol'
|
65
|
+
require_relative 'dalli/protocol/base'
|
66
|
+
require_relative 'dalli/protocol/binary'
|
67
|
+
require_relative 'dalli/protocol/connection_manager'
|
68
|
+
require_relative 'dalli/protocol/meta'
|
69
|
+
require_relative 'dalli/protocol/response_buffer'
|
70
|
+
require_relative 'dalli/protocol/server_config_parser'
|
71
|
+
require_relative 'dalli/protocol/ttl_sanitizer'
|
72
|
+
require_relative 'dalli/protocol/value_compressor'
|
73
|
+
require_relative 'dalli/protocol/value_marshaller'
|
74
|
+
require_relative 'dalli/protocol/value_serializer'
|
75
|
+
require_relative 'dalli/servers_arg_normalizer'
|
76
|
+
require_relative 'dalli/socket'
|
77
|
+
require_relative 'dalli/options'
|
data/lib/rack/session/dalli.rb
CHANGED
@@ -1,52 +1,195 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rack/session/abstract/id'
|
2
4
|
require 'dalli'
|
5
|
+
require 'connection_pool'
|
6
|
+
require 'English'
|
3
7
|
|
4
8
|
module Rack
|
5
9
|
module Session
|
6
|
-
|
7
|
-
|
10
|
+
# Rack::Session::Dalli provides memcached based session management.
|
11
|
+
class Dalli < Abstract::PersistedSecure
|
12
|
+
attr_reader :data
|
8
13
|
|
9
|
-
|
10
|
-
|
11
|
-
|
14
|
+
# Don't freeze this until we fix the specs/implementation
|
15
|
+
# rubocop:disable Style/MutableConstant
|
16
|
+
DEFAULT_DALLI_OPTIONS = {
|
17
|
+
namespace: 'rack:session'
|
18
|
+
}
|
19
|
+
# rubocop:enable Style/MutableConstant
|
12
20
|
|
13
|
-
|
21
|
+
# Brings in a new Rack::Session::Dalli middleware with the given
|
22
|
+
# `:memcache_server`. The server is either a hostname, or a
|
23
|
+
# host-with-port string in the form of "host_name:port", or an array of
|
24
|
+
# such strings. For example:
|
25
|
+
#
|
26
|
+
# use Rack::Session::Dalli,
|
27
|
+
# :memcache_server => "mc.example.com:1234"
|
28
|
+
#
|
29
|
+
# If no `:memcache_server` option is specified, Rack::Session::Dalli will
|
30
|
+
# connect to localhost, port 11211 (the default memcached port). If
|
31
|
+
# `:memcache_server` is set to nil, Dalli::Client will look for
|
32
|
+
# ENV['MEMCACHE_SERVERS'] and use that value if it is available, or fall
|
33
|
+
# back to the same default behavior described above.
|
34
|
+
#
|
35
|
+
# Rack::Session::Dalli accepts the same options as Dalli::Client, so
|
36
|
+
# it's worth reviewing its documentation. Perhaps most importantly,
|
37
|
+
# if you don't specify a `:namespace` option, Rack::Session::Dalli
|
38
|
+
# will default to using 'rack:session'.
|
39
|
+
#
|
40
|
+
# It is not recommended to set `:expires_in`. Instead, use `:expire_after`,
|
41
|
+
# which will control both the expiration of the client cookie as well
|
42
|
+
# as the expiration of the corresponding entry in memcached.
|
43
|
+
#
|
44
|
+
# Rack::Session::Dalli also accepts a host of options that control how
|
45
|
+
# the sessions and session cookies are managed, including the
|
46
|
+
# aforementioned `:expire_after` option. Please see the documentation for
|
47
|
+
# Rack::Session::Abstract::Persisted for a detailed explanation of these
|
48
|
+
# options and their default values.
|
49
|
+
#
|
50
|
+
# Finally, if your web application is multithreaded, the
|
51
|
+
# Rack::Session::Dalli middleware can become a source of contention. You
|
52
|
+
# can use a connection pool of Dalli clients by passing in the
|
53
|
+
# `:pool_size` and/or `:pool_timeout` options. For example:
|
54
|
+
#
|
55
|
+
# use Rack::Session::Dalli,
|
56
|
+
# :memcache_server => "mc.example.com:1234",
|
57
|
+
# :pool_size => 10
|
58
|
+
#
|
59
|
+
# You must include the `connection_pool` gem in your project if you wish
|
60
|
+
# to use pool support. Please see the documentation for ConnectionPool
|
61
|
+
# for more information about it and its default options (which would only
|
62
|
+
# be applicable if you supplied one of the two options, but not both).
|
63
|
+
#
|
64
|
+
def initialize(app, options = {})
|
65
|
+
# Parent uses DEFAULT_OPTIONS to build @default_options for Rack::Session
|
14
66
|
super
|
15
|
-
|
16
|
-
|
17
|
-
@
|
67
|
+
|
68
|
+
# Determine the default TTL for newly-created sessions
|
69
|
+
@default_ttl = ttl(@default_options[:expire_after])
|
70
|
+
@data = build_data_source(options)
|
71
|
+
end
|
72
|
+
|
73
|
+
def find_session(_req, sid)
|
74
|
+
with_dalli_client([nil, {}]) do |dc|
|
75
|
+
existing_session = existing_session_for_sid(dc, sid)
|
76
|
+
return [sid, existing_session] unless existing_session.nil?
|
77
|
+
|
78
|
+
[create_sid_with_empty_session(dc), {}]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def write_session(_req, sid, session, options)
|
83
|
+
return false unless sid
|
84
|
+
|
85
|
+
key = memcached_key_from_sid(sid)
|
86
|
+
return false unless key
|
87
|
+
|
88
|
+
with_dalli_client(false) do |dc|
|
89
|
+
dc.set(memcached_key_from_sid(sid), session, ttl(options[:expire_after]))
|
90
|
+
sid
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def delete_session(_req, sid, options)
|
95
|
+
with_dalli_client do |dc|
|
96
|
+
key = memcached_key_from_sid(sid)
|
97
|
+
dc.delete(key) if key
|
98
|
+
generate_sid_with(dc) unless options[:drop]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def memcached_key_from_sid(sid)
|
105
|
+
sid.private_id if sid.respond_to?(:private_id)
|
106
|
+
end
|
107
|
+
|
108
|
+
def existing_session_for_sid(client, sid)
|
109
|
+
return nil unless sid && !sid.empty?
|
110
|
+
|
111
|
+
key = memcached_key_from_sid(sid)
|
112
|
+
return nil if key.nil?
|
113
|
+
|
114
|
+
client.get(key)
|
115
|
+
end
|
116
|
+
|
117
|
+
def create_sid_with_empty_session(client)
|
118
|
+
loop do
|
119
|
+
sid = generate_sid_with(client)
|
120
|
+
key = memcached_key_from_sid(sid)
|
121
|
+
|
122
|
+
break sid if key && client.add(key, {}, @default_ttl)
|
123
|
+
end
|
18
124
|
end
|
19
125
|
|
20
|
-
def
|
126
|
+
def generate_sid_with(client)
|
21
127
|
loop do
|
22
|
-
|
23
|
-
|
128
|
+
raw_sid = generate_sid
|
129
|
+
sid = raw_sid.is_a?(String) ? Rack::Session::SessionId.new(raw_sid) : raw_sid
|
130
|
+
key = memcached_key_from_sid(sid)
|
131
|
+
break sid unless key && client.get(key)
|
24
132
|
end
|
25
133
|
end
|
26
134
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
135
|
+
def build_data_source(options)
|
136
|
+
server_configurations, client_options, pool_options = extract_dalli_options(options)
|
137
|
+
|
138
|
+
if pool_options.empty?
|
139
|
+
::Dalli::Client.new(server_configurations, client_options)
|
140
|
+
else
|
141
|
+
ensure_connection_pool_added!
|
142
|
+
ConnectionPool.new(pool_options) do
|
143
|
+
::Dalli::Client.new(server_configurations, client_options.merge(threadsafe: false))
|
32
144
|
end
|
33
145
|
end
|
34
|
-
[sid, session]
|
35
146
|
end
|
36
147
|
|
37
|
-
def
|
38
|
-
|
39
|
-
expiry = expiry.nil? ? 0 : expiry + 1
|
148
|
+
def extract_dalli_options(options)
|
149
|
+
raise 'Rack::Session::Dalli no longer supports the :cache option.' if options[:cache]
|
40
150
|
|
41
|
-
|
42
|
-
|
151
|
+
client_options = retrieve_client_options(options)
|
152
|
+
server_configurations = client_options.delete(:memcache_server)
|
153
|
+
|
154
|
+
[server_configurations, client_options, retrieve_pool_options(options)]
|
43
155
|
end
|
44
156
|
|
45
|
-
def
|
46
|
-
|
47
|
-
|
157
|
+
def retrieve_client_options(options)
|
158
|
+
# Filter out Rack::Session-specific options and apply our defaults
|
159
|
+
filtered_opts = options.reject { |k, _| DEFAULT_OPTIONS.key? k }
|
160
|
+
DEFAULT_DALLI_OPTIONS.merge(filtered_opts)
|
48
161
|
end
|
49
162
|
|
163
|
+
def retrieve_pool_options(options)
|
164
|
+
{}.tap do |pool_options|
|
165
|
+
pool_options[:size] = options.delete(:pool_size) if options[:pool_size]
|
166
|
+
pool_options[:timeout] = options.delete(:pool_timeout) if options[:pool_timeout]
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def ensure_connection_pool_added!
|
171
|
+
require 'connection_pool'
|
172
|
+
rescue LoadError => e
|
173
|
+
warn "You don't have connection_pool installed in your application. " \
|
174
|
+
'Please add it to your Gemfile and run bundle install'
|
175
|
+
raise e
|
176
|
+
end
|
177
|
+
|
178
|
+
def with_dalli_client(result_on_error = nil, &block)
|
179
|
+
@data.with(&block)
|
180
|
+
rescue ::Dalli::DalliError, Errno::ECONNREFUSED
|
181
|
+
raise if $ERROR_INFO.message.include?('undefined class')
|
182
|
+
|
183
|
+
if $VERBOSE
|
184
|
+
warn "#{self} is unable to find memcached server."
|
185
|
+
warn $ERROR_INFO.inspect
|
186
|
+
end
|
187
|
+
result_on_error
|
188
|
+
end
|
189
|
+
|
190
|
+
def ttl(expire_after)
|
191
|
+
expire_after.nil? ? 0 : expire_after + 1
|
192
|
+
end
|
50
193
|
end
|
51
194
|
end
|
52
195
|
end
|