dalli 2.7.11 → 3.2.4

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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/{History.md → CHANGELOG.md} +152 -0
  3. data/Gemfile +14 -8
  4. data/README.md +30 -198
  5. data/lib/dalli/cas/client.rb +1 -57
  6. data/lib/dalli/client.rb +226 -265
  7. data/lib/dalli/compressor.rb +12 -2
  8. data/lib/dalli/key_manager.rb +121 -0
  9. data/lib/dalli/options.rb +6 -7
  10. data/lib/dalli/pid_cache.rb +40 -0
  11. data/lib/dalli/pipelined_getter.rb +177 -0
  12. data/lib/dalli/protocol/base.rb +239 -0
  13. data/lib/dalli/protocol/binary/request_formatter.rb +117 -0
  14. data/lib/dalli/protocol/binary/response_header.rb +36 -0
  15. data/lib/dalli/protocol/binary/response_processor.rb +239 -0
  16. data/lib/dalli/protocol/binary/sasl_authentication.rb +60 -0
  17. data/lib/dalli/protocol/binary.rb +173 -0
  18. data/lib/dalli/protocol/connection_manager.rb +254 -0
  19. data/lib/dalli/protocol/meta/key_regularizer.rb +31 -0
  20. data/lib/dalli/protocol/meta/request_formatter.rb +121 -0
  21. data/lib/dalli/protocol/meta/response_processor.rb +211 -0
  22. data/lib/dalli/protocol/meta.rb +178 -0
  23. data/lib/dalli/protocol/response_buffer.rb +54 -0
  24. data/lib/dalli/protocol/server_config_parser.rb +86 -0
  25. data/lib/dalli/protocol/ttl_sanitizer.rb +45 -0
  26. data/lib/dalli/protocol/value_compressor.rb +85 -0
  27. data/lib/dalli/protocol/value_marshaller.rb +59 -0
  28. data/lib/dalli/protocol/value_serializer.rb +91 -0
  29. data/lib/dalli/protocol.rb +8 -0
  30. data/lib/dalli/ring.rb +94 -83
  31. data/lib/dalli/server.rb +3 -749
  32. data/lib/dalli/servers_arg_normalizer.rb +54 -0
  33. data/lib/dalli/socket.rb +117 -137
  34. data/lib/dalli/version.rb +4 -1
  35. data/lib/dalli.rb +42 -14
  36. data/lib/rack/session/dalli.rb +103 -94
  37. metadata +29 -10
  38. data/lib/action_dispatch/middleware/session/dalli_store.rb +0 -82
  39. data/lib/active_support/cache/dalli_store.rb +0 -441
  40. data/lib/dalli/railtie.rb +0 -8
@@ -1,16 +1,22 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'rack/session/abstract/id'
3
4
  require 'dalli'
5
+ require 'connection_pool'
6
+ require 'English'
4
7
 
5
8
  module Rack
6
9
  module Session
7
- class Dalli < defined?(Abstract::Persisted) ? Abstract::Persisted : Abstract::ID
8
- attr_reader :pool, :mutex
10
+ # Rack::Session::Dalli provides memcached based session management.
11
+ class Dalli < Abstract::PersistedSecure
12
+ attr_reader :data
9
13
 
14
+ # Don't freeze this until we fix the specs/implementation
15
+ # rubocop:disable Style/MutableConstant
10
16
  DEFAULT_DALLI_OPTIONS = {
11
- :namespace => 'rack:session',
12
- :memcache_server => 'localhost:11211'
17
+ namespace: 'rack:session'
13
18
  }
19
+ # rubocop:enable Style/MutableConstant
14
20
 
15
21
  # Brings in a new Rack::Session::Dalli middleware with the given
16
22
  # `:memcache_server`. The server is either a hostname, or a
@@ -26,25 +32,14 @@ module Rack
26
32
  # ENV['MEMCACHE_SERVERS'] and use that value if it is available, or fall
27
33
  # back to the same default behavior described above.
28
34
  #
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".
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'.
43
39
  #
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.
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.
48
43
  #
49
44
  # Rack::Session::Dalli also accepts a host of options that control how
50
45
  # the sessions and session cookies are managed, including the
@@ -66,116 +61,130 @@ module Rack
66
61
  # for more information about it and its default options (which would only
67
62
  # be applicable if you supplied one of the two options, but not both).
68
63
  #
69
- def initialize(app, options={})
64
+ def initialize(app, options = {})
70
65
  # Parent uses DEFAULT_OPTIONS to build @default_options for Rack::Session
71
66
  super
72
67
 
73
68
  # 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
69
+ @default_ttl = ttl(@default_options[:expire_after])
70
+ @data = build_data_source(options)
71
+ end
87
72
 
88
- if @pool.respond_to?(:alive!) # is a Dalli::Client
89
- @mutex = Mutex.new
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?
90
77
 
91
- @pool.alive!
78
+ [create_sid_with_empty_session(dc), {}]
92
79
  end
93
80
  end
94
81
 
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
82
+ def write_session(_req, sid, session, options)
83
+ return false unless sid
107
84
 
108
- def set_session(env, session_id, new_session, options)
109
- return false unless session_id
85
+ key = memcached_key_from_sid(sid)
86
+ return false unless key
110
87
 
111
- with_block(env, false) do |dc|
112
- dc.set(session_id, new_session, ttl(options[:expire_after]))
113
- session_id
88
+ with_dalli_client(false) do |dc|
89
+ dc.set(memcached_key_from_sid(sid), session, ttl(options[:expire_after]))
90
+ sid
114
91
  end
115
92
  end
116
93
 
117
- def destroy_session(env, session_id, options)
118
- with_block(env) do |dc|
119
- dc.delete(session_id)
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
120
98
  generate_sid_with(dc) unless options[:drop]
121
99
  end
122
100
  end
123
101
 
124
- if defined?(Abstract::Persisted)
125
- def find_session(req, sid)
126
- get_session req.env, sid
127
- end
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
128
116
 
129
- def write_session(req, sid, session, options)
130
- set_session req.env, sid, session, options
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)
131
123
  end
124
+ end
132
125
 
133
- def delete_session(req, sid, options)
134
- destroy_session req.env, sid, options
126
+ def generate_sid_with(client)
127
+ loop do
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)
135
132
  end
136
133
  end
137
134
 
138
- private
135
+ def build_data_source(options)
136
+ server_configurations, client_options, pool_options = extract_dalli_options(options)
139
137
 
140
- def extract_dalli_options(options)
141
- return [options[:cache]] if options[:cache]
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))
144
+ end
145
+ end
146
+ end
142
147
 
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
148
+ def extract_dalli_options(options)
149
+ raise 'Rack::Session::Dalli no longer supports the :cache option.' if options[:cache]
147
150
 
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]
151
+ client_options = retrieve_client_options(options)
152
+ server_configurations = client_options.delete(:memcache_server)
152
153
 
153
- # For a connection pool, locking is handled at the pool level
154
- mopts[:threadsafe] = false unless mopts.key? :threadsafe
155
- end
154
+ [server_configurations, client_options, retrieve_pool_options(options)]
155
+ end
156
156
 
157
- [nil, mserv, mopts, popts]
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)
158
161
  end
159
162
 
160
- def generate_sid_with(dc)
161
- while true
162
- sid = generate_sid
163
- break sid unless dc.get(sid)
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]
164
167
  end
165
168
  end
166
169
 
167
- def with_block(env, default=nil, &block)
168
- @mutex.lock if @mutex and env['rack.multithread']
169
- @pool.with(&block)
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)
170
180
  rescue ::Dalli::DalliError, Errno::ECONNREFUSED
171
- raise if $!.message =~ /undefined class/
181
+ raise if $ERROR_INFO.message.include?('undefined class')
182
+
172
183
  if $VERBOSE
173
184
  warn "#{self} is unable to find memcached server."
174
- warn $!.inspect
185
+ warn $ERROR_INFO.inspect
175
186
  end
176
- default
177
- ensure
178
- @mutex.unlock if @mutex and @mutex.locked?
187
+ result_on_error
179
188
  end
180
189
 
181
190
  def ttl(expire_after)
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.11
4
+ version: 3.2.4
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: 2020-09-26 00:00:00.000000000 Z
12
+ date: 2023-02-17 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: High performance memcached client for Ruby
15
15
  email:
@@ -19,27 +19,47 @@ executables: []
19
19
  extensions: []
20
20
  extra_rdoc_files: []
21
21
  files:
22
+ - CHANGELOG.md
22
23
  - Gemfile
23
- - History.md
24
24
  - LICENSE
25
25
  - README.md
26
- - lib/action_dispatch/middleware/session/dalli_store.rb
27
- - lib/active_support/cache/dalli_store.rb
28
26
  - lib/dalli.rb
29
27
  - lib/dalli/cas/client.rb
30
28
  - lib/dalli/client.rb
31
29
  - lib/dalli/compressor.rb
30
+ - lib/dalli/key_manager.rb
32
31
  - lib/dalli/options.rb
33
- - lib/dalli/railtie.rb
32
+ - lib/dalli/pid_cache.rb
33
+ - lib/dalli/pipelined_getter.rb
34
+ - lib/dalli/protocol.rb
35
+ - lib/dalli/protocol/base.rb
36
+ - lib/dalli/protocol/binary.rb
37
+ - lib/dalli/protocol/binary/request_formatter.rb
38
+ - lib/dalli/protocol/binary/response_header.rb
39
+ - lib/dalli/protocol/binary/response_processor.rb
40
+ - lib/dalli/protocol/binary/sasl_authentication.rb
41
+ - lib/dalli/protocol/connection_manager.rb
42
+ - lib/dalli/protocol/meta.rb
43
+ - lib/dalli/protocol/meta/key_regularizer.rb
44
+ - lib/dalli/protocol/meta/request_formatter.rb
45
+ - lib/dalli/protocol/meta/response_processor.rb
46
+ - lib/dalli/protocol/response_buffer.rb
47
+ - lib/dalli/protocol/server_config_parser.rb
48
+ - lib/dalli/protocol/ttl_sanitizer.rb
49
+ - lib/dalli/protocol/value_compressor.rb
50
+ - lib/dalli/protocol/value_marshaller.rb
51
+ - lib/dalli/protocol/value_serializer.rb
34
52
  - lib/dalli/ring.rb
35
53
  - lib/dalli/server.rb
54
+ - lib/dalli/servers_arg_normalizer.rb
36
55
  - lib/dalli/socket.rb
37
56
  - lib/dalli/version.rb
38
57
  - lib/rack/session/dalli.rb
39
58
  homepage: https://github.com/petergoldstein/dalli
40
59
  licenses:
41
60
  - MIT
42
- metadata: {}
61
+ metadata:
62
+ rubygems_mfa_required: 'true'
43
63
  post_install_message:
44
64
  rdoc_options: []
45
65
  require_paths:
@@ -48,15 +68,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
48
68
  requirements:
49
69
  - - ">="
50
70
  - !ruby/object:Gem::Version
51
- version: '0'
71
+ version: '2.6'
52
72
  required_rubygems_version: !ruby/object:Gem::Requirement
53
73
  requirements:
54
74
  - - ">="
55
75
  - !ruby/object:Gem::Version
56
76
  version: '0'
57
77
  requirements: []
58
- rubyforge_project:
59
- rubygems_version: 2.7.6.2
78
+ rubygems_version: 3.4.5
60
79
  signing_key:
61
80
  specification_version: 4
62
81
  summary: High performance memcached client for Ruby
@@ -1,82 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'active_support/cache'
3
- require 'action_dispatch/middleware/session/abstract_store'
4
- require 'dalli'
5
-
6
- # Dalli-based session store for Rails 3.0.
7
- module ActionDispatch
8
- module Session
9
- class DalliStore < AbstractStore
10
- def initialize(app, options = {})
11
- # Support old :expires option
12
- options[:expire_after] ||= options[:expires]
13
-
14
- super
15
-
16
- @default_options = { :namespace => 'rack:session' }.merge(@default_options)
17
-
18
- @pool = options[:cache] || begin
19
- Dalli::Client.new(
20
- @default_options[:memcache_server], @default_options)
21
- end
22
- @namespace = @default_options[:namespace]
23
-
24
- @raise_errors = !!@default_options[:raise_errors]
25
-
26
- super
27
- end
28
-
29
- def reset
30
- @pool.reset
31
- end
32
-
33
- private
34
-
35
- def get_session(env, sid)
36
- sid = generate_sid unless sid and !sid.empty?
37
- begin
38
- session = @pool.get(sid) || {}
39
- rescue Dalli::DalliError => ex
40
- # re-raise ArgumentError so Rails' session abstract_store.rb can autoload any missing models
41
- raise ArgumentError, ex.message if ex.message =~ /unmarshal/
42
- Rails.logger.warn("Session::DalliStore#get: #{ex.message}")
43
- session = {}
44
- end
45
- [sid, session]
46
- end
47
-
48
- def set_session(env, sid, session_data, options = nil)
49
- options ||= env[ENV_SESSION_OPTIONS_KEY]
50
- expiry = options[:expire_after]
51
- @pool.set(sid, session_data, expiry)
52
- sid
53
- rescue Dalli::DalliError
54
- Rails.logger.warn("Session::DalliStore#set: #{$!.message}")
55
- raise if @raise_errors
56
- false
57
- end
58
-
59
- def destroy_session(env, session_id, options)
60
- begin
61
- @pool.delete(session_id)
62
- rescue Dalli::DalliError
63
- Rails.logger.warn("Session::DalliStore#destroy_session: #{$!.message}")
64
- raise if @raise_errors
65
- end
66
- return nil if options[:drop]
67
- generate_sid
68
- end
69
-
70
- def destroy(env)
71
- if sid = current_session_id(env)
72
- @pool.delete(sid)
73
- end
74
- rescue Dalli::DalliError
75
- Rails.logger.warn("Session::DalliStore#destroy: #{$!.message}")
76
- raise if @raise_errors
77
- false
78
- end
79
-
80
- end
81
- end
82
- end