class Rack::Session::Dalli
Rack::Session::Dalli
provides memcached based session management.
Constants
- DEFAULT_DALLI_OPTIONS
Don’t freeze this until we fix the specs/implementation rubocop:disable Style/MutableConstant
Attributes
Public Class Methods
Brings in a new Rack::Session::Dalli
middleware with the given ‘:memcache_server`. The server is either a hostname, or a host-with-port string in the form of “host_name:port”, or an array of such strings. For example:
use Rack::Session::Dalli, :memcache_server => "mc.example.com:1234"
If no ‘:memcache_server` option is specified, Rack::Session::Dalli
will connect to localhost, port 11211 (the default memcached port). If `:memcache_server` is set to nil, Dalli::Client
will look for ENV and use that value if it is available, or fall back to the same default behavior described above.
Rack::Session::Dalli
accepts the same options as Dalli::Client
, so it’s worth reviewing its documentation. Perhaps most importantly, if you don’t specify a ‘:namespace` option, Rack::Session::Dalli
will default to using ’rack:session’.
It is not recommended to set ‘:expires_in`. Instead, use `:expire_after`, which will control both the expiration of the client cookie as well as the expiration of the corresponding entry in memcached.
Rack::Session::Dalli
also accepts a host of options that control how the sessions and session cookies are managed, including the aforementioned ‘:expire_after` option. Please see the documentation for Rack::Session::Abstract::Persisted for a detailed explanation of these options and their default values.
Finally, if your web application is multithreaded, the Rack::Session::Dalli
middleware can become a source of contention. You can use a connection pool of Dalli
clients by passing in the ‘:pool_size` and/or `:pool_timeout` options. For example:
use Rack::Session::Dalli, :memcache_server => "mc.example.com:1234", :pool_size => 10
You must include the ‘connection_pool` gem in your project if you wish to use pool support. Please see the documentation for ConnectionPool for more information about it and its default options (which would only be applicable if you supplied one of the two options, but not both).
# File lib/rack/session/dalli.rb, line 64 def initialize(app, options = {}) # Parent uses DEFAULT_OPTIONS to build @default_options for Rack::Session super # Determine the default TTL for newly-created sessions @default_ttl = ttl(@default_options[:expire_after]) @data = build_data_source(options) end
Public Instance Methods
# File lib/rack/session/dalli.rb, line 91 def delete_session(_req, sid, options) with_dalli_client do |dc| dc.delete(memcached_key_from_sid(sid)) generate_sid_with(dc) unless options[:drop] end end
# File lib/rack/session/dalli.rb, line 73 def find_session(_req, sid) with_dalli_client([nil, {}]) do |dc| existing_session = existing_session_for_sid(dc, sid) return [sid, existing_session] unless existing_session.nil? [create_sid_with_empty_session(dc), {}] end end
# File lib/rack/session/dalli.rb, line 82 def write_session(_req, sid, session, options) return false unless sid with_dalli_client(false) do |dc| dc.set(memcached_key_from_sid(sid), session, ttl(options[:expire_after])) sid end end
Private Instance Methods
# File lib/rack/session/dalli.rb, line 126 def build_data_source(options) server_configurations, client_options, pool_options = extract_dalli_options(options) if pool_options.empty? ::Dalli::Client.new(server_configurations, client_options) else ensure_connection_pool_added! ConnectionPool.new(pool_options) do ::Dalli::Client.new(server_configurations, client_options.merge(threadsafe: false)) end end end
# File lib/rack/session/dalli.rb, line 110 def create_sid_with_empty_session(client) loop do sid = generate_sid_with(client) break sid if client.add(memcached_key_from_sid(sid), {}, @default_ttl) end end
# File lib/rack/session/dalli.rb, line 161 def ensure_connection_pool_added! require 'connection_pool' rescue LoadError => e warn "You don't have connection_pool installed in your application. "\ 'Please add it to your Gemfile and run bundle install' raise e end
# File lib/rack/session/dalli.rb, line 104 def existing_session_for_sid(client, sid) return nil unless sid && !sid.empty? client.get(memcached_key_from_sid(sid)) end
# File lib/rack/session/dalli.rb, line 139 def extract_dalli_options(options) raise 'Rack::Session::Dalli no longer supports the :cache option.' if options[:cache] client_options = retrieve_client_options(options) server_configurations = client_options.delete(:memcache_server) [server_configurations, client_options, retrieve_pool_options(options)] end
# File lib/rack/session/dalli.rb, line 118 def generate_sid_with(client) loop do raw_sid = generate_sid sid = raw_sid.is_a?(String) ? Rack::Session::SessionId.new(raw_sid) : raw_sid break sid unless client.get(memcached_key_from_sid(sid)) end end
# File lib/rack/session/dalli.rb, line 100 def memcached_key_from_sid(sid) sid.private_id end
# File lib/rack/session/dalli.rb, line 148 def retrieve_client_options(options) # Filter out Rack::Session-specific options and apply our defaults filtered_opts = options.reject { |k, _| DEFAULT_OPTIONS.key? k } DEFAULT_DALLI_OPTIONS.merge(filtered_opts) end
# File lib/rack/session/dalli.rb, line 154 def retrieve_pool_options(options) {}.tap do |pool_options| pool_options[:size] = options.delete(:pool_size) if options[:pool_size] pool_options[:timeout] = options.delete(:pool_timeout) if options[:pool_timeout] end end
# File lib/rack/session/dalli.rb, line 181 def ttl(expire_after) expire_after.nil? ? 0 : expire_after + 1 end
# File lib/rack/session/dalli.rb, line 169 def with_dalli_client(result_on_error = nil, &block) @data.with(&block) rescue ::Dalli::DalliError, Errno::ECONNREFUSED raise if /undefined class/.match?($ERROR_INFO.message) if $VERBOSE warn "#{self} is unable to find memcached server." warn $ERROR_INFO.inspect end result_on_error end