dalli 2.7.6 → 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.
Files changed (40) hide show
  1. checksums.yaml +5 -5
  2. data/{History.md → CHANGELOG.md} +218 -0
  3. data/Gemfile +17 -1
  4. data/README.md +30 -210
  5. data/lib/dalli/cas/client.rb +2 -57
  6. data/lib/dalli/client.rb +228 -254
  7. data/lib/dalli/compressor.rb +13 -2
  8. data/lib/dalli/key_manager.rb +121 -0
  9. data/lib/dalli/options.rb +7 -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 +250 -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 +255 -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 +19 -0
  30. data/lib/dalli/ring.rb +95 -84
  31. data/lib/dalli/server.rb +4 -743
  32. data/lib/dalli/servers_arg_normalizer.rb +54 -0
  33. data/lib/dalli/socket.rb +142 -127
  34. data/lib/dalli/version.rb +5 -1
  35. data/lib/dalli.rb +46 -15
  36. data/lib/rack/session/dalli.rb +156 -50
  37. metadata +36 -128
  38. data/lib/action_dispatch/middleware/session/dalli_store.rb +0 -81
  39. data/lib/active_support/cache/dalli_store.rb +0 -403
  40. data/lib/dalli/railtie.rb +0 -7
@@ -1,89 +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
- class Dalli < defined?(Abstract::Persisted) ? Abstract::Persisted : Abstract::ID
7
- attr_reader :pool, :mutex
10
+ # Rack::Session::Dalli provides memcached based session management.
11
+ class Dalli < Abstract::PersistedSecure
12
+ attr_reader :data
8
13
 
9
- DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \
10
- :namespace => 'rack:session',
11
- :memcache_server => 'localhost:11211'
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
- def initialize(app, options={})
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
- @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!
67
+
68
+ # Determine the default TTL for newly-created sessions
69
+ @default_ttl = ttl(@default_options[:expire_after])
70
+ @data = build_data_source(options)
20
71
  end
21
72
 
22
- if defined?(Abstract::Persisted)
23
- def find_session(req, sid)
24
- get_session req.env, sid
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), {}]
25
79
  end
80
+ end
26
81
 
27
- def write_session(req, sid, session, options)
28
- set_session req.env, sid, session, options
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
29
91
  end
92
+ end
30
93
 
31
- def delete_session(req, sid, options)
32
- destroy_session req.env, sid, options
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]
33
99
  end
34
100
  end
35
101
 
36
- def generate_sid
37
- while true
38
- sid = super
39
- break sid unless @pool.get(sid)
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)
40
123
  end
41
124
  end
42
125
 
43
- def get_session(env, sid)
44
- with_lock(env, [nil, {}]) do
45
- unless sid and !sid.empty? and session = @pool.get(sid)
46
- sid, session = generate_sid, {}
47
- unless @pool.add(sid, session)
48
- raise "Session collision on '#{sid.inspect}'"
49
- end
50
- end
51
- [sid, session]
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)
52
132
  end
53
133
  end
54
134
 
55
- def set_session(env, session_id, new_session, options)
56
- return false unless session_id
57
- expiry = options[:expire_after]
58
- expiry = expiry.nil? ? 0 : expiry + 1
135
+ def build_data_source(options)
136
+ server_configurations, client_options, pool_options = extract_dalli_options(options)
59
137
 
60
- with_lock(env, false) do
61
- @pool.set session_id, new_session, expiry
62
- session_id
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
63
145
  end
64
146
  end
65
147
 
66
- def destroy_session(env, session_id, options)
67
- with_lock(env) do
68
- @pool.delete(session_id)
69
- generate_sid unless options[:drop]
148
+ def extract_dalli_options(options)
149
+ raise 'Rack::Session::Dalli no longer supports the :cache option.' if options[:cache]
150
+
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)]
155
+ end
156
+
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)
161
+ end
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]
70
167
  end
71
168
  end
72
169
 
73
- def with_lock(env, default=nil)
74
- @mutex.lock if env['rack.multithread']
75
- yield
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)
76
180
  rescue ::Dalli::DalliError, Errno::ECONNREFUSED
77
- raise if $!.message =~ /undefined class/
181
+ raise if $ERROR_INFO.message.include?('undefined class')
182
+
78
183
  if $VERBOSE
79
184
  warn "#{self} is unable to find memcached server."
80
- warn $!.inspect
185
+ warn $ERROR_INFO.inspect
81
186
  end
82
- default
83
- ensure
84
- @mutex.unlock if @mutex.locked?
187
+ result_on_error
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,128 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dalli
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.6
4
+ version: 3.2.8
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: 2016-02-14 00:00:00.000000000 Z
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: 2024-02-12 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
@@ -131,46 +19,66 @@ executables: []
131
19
  extensions: []
132
20
  extra_rdoc_files: []
133
21
  files:
22
+ - CHANGELOG.md
134
23
  - Gemfile
135
- - History.md
136
24
  - LICENSE
137
25
  - README.md
138
- - lib/action_dispatch/middleware/session/dalli_store.rb
139
- - lib/active_support/cache/dalli_store.rb
140
26
  - lib/dalli.rb
141
27
  - lib/dalli/cas/client.rb
142
28
  - lib/dalli/client.rb
143
29
  - lib/dalli/compressor.rb
30
+ - lib/dalli/key_manager.rb
144
31
  - lib/dalli/options.rb
145
- - 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
146
52
  - lib/dalli/ring.rb
147
53
  - lib/dalli/server.rb
54
+ - lib/dalli/servers_arg_normalizer.rb
148
55
  - lib/dalli/socket.rb
149
56
  - lib/dalli/version.rb
150
57
  - lib/rack/session/dalli.rb
151
58
  homepage: https://github.com/petergoldstein/dalli
152
59
  licenses:
153
60
  - MIT
154
- metadata: {}
155
- post_install_message:
156
- rdoc_options:
157
- - "--charset=UTF-8"
61
+ metadata:
62
+ bug_tracker_uri: https://github.com/petergoldstein/dalli/issues
63
+ changelog_uri: https://github.com/petergoldstein/dalli/blob/main/CHANGELOG.md
64
+ rubygems_mfa_required: 'true'
65
+ post_install_message:
66
+ rdoc_options: []
158
67
  require_paths:
159
68
  - lib
160
69
  required_ruby_version: !ruby/object:Gem::Requirement
161
70
  requirements:
162
71
  - - ">="
163
72
  - !ruby/object:Gem::Version
164
- version: '0'
73
+ version: '2.6'
165
74
  required_rubygems_version: !ruby/object:Gem::Requirement
166
75
  requirements:
167
76
  - - ">="
168
77
  - !ruby/object:Gem::Version
169
78
  version: '0'
170
79
  requirements: []
171
- rubyforge_project:
172
- rubygems_version: 2.4.5.1
173
- signing_key:
80
+ rubygems_version: 3.5.6
81
+ signing_key:
174
82
  specification_version: 4
175
83
  summary: High performance memcached client for Ruby
176
84
  test_files: []
@@ -1,81 +0,0 @@
1
- require 'active_support/cache'
2
- require 'action_dispatch/middleware/session/abstract_store'
3
- require 'dalli'
4
-
5
- # Dalli-based session store for Rails 3.0.
6
- module ActionDispatch
7
- module Session
8
- class DalliStore < AbstractStore
9
- def initialize(app, options = {})
10
- # Support old :expires option
11
- options[:expire_after] ||= options[:expires]
12
-
13
- super
14
-
15
- @default_options = { :namespace => 'rack:session' }.merge(@default_options)
16
-
17
- @pool = options[:cache] || begin
18
- Dalli::Client.new(
19
- @default_options[:memcache_server], @default_options)
20
- end
21
- @namespace = @default_options[:namespace]
22
-
23
- @raise_errors = !!@default_options[:raise_errors]
24
-
25
- super
26
- end
27
-
28
- def reset
29
- @pool.reset
30
- end
31
-
32
- private
33
-
34
- def get_session(env, sid)
35
- sid = generate_sid unless sid and !sid.empty?
36
- begin
37
- session = @pool.get(sid) || {}
38
- rescue Dalli::DalliError => ex
39
- # re-raise ArgumentError so Rails' session abstract_store.rb can autoload any missing models
40
- raise ArgumentError, ex.message if ex.message =~ /unmarshal/
41
- Rails.logger.warn("Session::DalliStore#get: #{ex.message}")
42
- session = {}
43
- end
44
- [sid, session]
45
- end
46
-
47
- def set_session(env, sid, session_data, options = nil)
48
- options ||= env[ENV_SESSION_OPTIONS_KEY]
49
- expiry = options[:expire_after]
50
- @pool.set(sid, session_data, expiry)
51
- sid
52
- rescue Dalli::DalliError
53
- Rails.logger.warn("Session::DalliStore#set: #{$!.message}")
54
- raise if @raise_errors
55
- false
56
- end
57
-
58
- def destroy_session(env, session_id, options)
59
- begin
60
- @pool.delete(session_id)
61
- rescue Dalli::DalliError
62
- Rails.logger.warn("Session::DalliStore#destroy_session: #{$!.message}")
63
- raise if @raise_errors
64
- end
65
- return nil if options[:drop]
66
- generate_sid
67
- end
68
-
69
- def destroy(env)
70
- if sid = current_session_id(env)
71
- @pool.delete(sid)
72
- end
73
- rescue Dalli::DalliError
74
- Rails.logger.warn("Session::DalliStore#destroy: #{$!.message}")
75
- raise if @raise_errors
76
- false
77
- end
78
-
79
- end
80
- end
81
- end