class Dalli::Protocol::Base

Base class for a single Memcached server, containing logic common to all protocols. Contains logic for managing connection state to the server and value handling.

Constants

ALLOWED_QUIET_OPS

NOTE: Additional public methods should be overridden in Dalli::Threadsafe

Attributes

options[RW]
weight[RW]

Public Class Methods

new(attribs, client_options = {}) click to toggle source
# File lib/dalli/protocol/base.rb, line 23
def initialize(attribs, client_options = {})
  hostname, port, socket_type, @weight, user_creds = ServerConfigParser.parse(attribs)
  @options = client_options.merge(user_creds)
  @value_marshaller = ValueMarshaller.new(@options)
  @connection_manager = ConnectionManager.new(hostname, port, socket_type, @options)
end

Public Instance Methods

alive?() click to toggle source

Boolean method used by clients of this class to determine if this particular memcached instance is available for use.

# File lib/dalli/protocol/base.rb, line 50
def alive?
  ensure_connected!
rescue Dalli::NetworkError
  # ensure_connected! raises a NetworkError if connection fails.  We
  # want to capture that error and convert it to a boolean value here.
  false
end
lock!() click to toggle source
# File lib/dalli/protocol/base.rb, line 58
def lock!; end
multi?()
Alias for: quiet?
password() click to toggle source
# File lib/dalli/protocol/base.rb, line 133
def password
  @options[:password] || ENV['MEMCACHE_PASSWORD']
end
pipeline_abort() click to toggle source

Abort current pipelined get. Generally used to signal an external timeout during pipelined get. The underlying socket is disconnected, and the exception is swallowed.

Returns nothing.

# File lib/dalli/protocol/base.rb, line 112
def pipeline_abort
  response_buffer.clear
  @connection_manager.abort_request!
  return true unless connected?

  # Closes the connection, which ensures that our connection
  # is in a clean state for future requests
  @connection_manager.error_on_request!('External timeout')
rescue NetworkError
  true
end
pipeline_complete?() click to toggle source

Did the last call to pipeline_response_setup complete successfully?

# File lib/dalli/protocol/base.rb, line 125
def pipeline_complete?
  !response_buffer.in_progress?
end
pipeline_next_responses() click to toggle source

Attempt to receive and parse as many key/value pairs as possible from this server. After pipeline_response_setup, this should be invoked repeatedly whenever this server’s socket is readable until pipeline_complete?.

Returns a Hash of kv pairs received.

# File lib/dalli/protocol/base.rb, line 80
def pipeline_next_responses
  reconnect_on_pipeline_complete!
  values = {}

  response_buffer.read

  status, cas, key, value = response_buffer.process_single_getk_response
  # status is not nil only if we have a full response to parse
  # in the buffer
  until status.nil?
    # If the status is ok and key is nil, then this is the response
    # to the noop at the end of the pipeline
    finish_pipeline && break if status && key.nil?

    # If the status is ok and the key is not nil, then this is a
    # getkq response with a value that we want to set in the response hash
    values[key] = [value, cas] unless key.nil?

    # Get the next response from the buffer
    status, cas, key, value = response_buffer.process_single_getk_response
  end

  values
rescue SystemCallError, Timeout::Error, EOFError => e
  @connection_manager.error_on_request!(e)
end
pipeline_response_setup() click to toggle source

Start reading key/value pairs from this connection. This is usually called after a series of GETKQ commands. A NOOP is sent, and the server begins flushing responses for kv pairs that were found.

Returns nothing.

# File lib/dalli/protocol/base.rb, line 67
def pipeline_response_setup
  verify_state(:getkq)
  write_noop
  response_buffer.reset
  @connection_manager.start_request!
end
quiet?() click to toggle source
# File lib/dalli/protocol/base.rb, line 141
def quiet?
  Thread.current[::Dalli::QUIET]
end
Also aliased as: multi?
request(opkey, *args) click to toggle source

Chokepoint method for error handling and ensuring liveness

# File lib/dalli/protocol/base.rb, line 31
def request(opkey, *args)
  verify_state(opkey)

  begin
    send(opkey, *args)
  rescue Dalli::MarshalError => e
    log_marshal_err(args.first, e)
    raise
  rescue Dalli::DalliError
    raise
  rescue StandardError => e
    log_unexpected_err(e)
    down!
  end
end
require_auth?() click to toggle source
# File lib/dalli/protocol/base.rb, line 137
def require_auth?
  !username.nil?
end
unlock!() click to toggle source
# File lib/dalli/protocol/base.rb, line 60
def unlock!; end
username() click to toggle source
# File lib/dalli/protocol/base.rb, line 129
def username
  @options[:username] || ENV['MEMCACHE_USERNAME']
end

Private Instance Methods

cache_nils?(opts) click to toggle source
# File lib/dalli/protocol/base.rb, line 189
def cache_nils?(opts)
  return false unless opts.is_a?(Hash)

  opts[:cache_nils] ? true : false
end
connect() click to toggle source
# File lib/dalli/protocol/base.rb, line 195
def connect
  @connection_manager.establish_connection
  authenticate_connection if require_auth?
  @version = version # Connect socket if not authed
  up!
rescue Dalli::DalliError
  raise
end
ensure_connected!() click to toggle source

The socket connection to the underlying server is initialized as a side effect of this call. In fact, this is the ONLY place where that socket connection is initialized.

Both this method and connect need to be in this class so we can do auth as required

Since this is invoked exclusively in verify_state!, we don’t need to worry about thread safety. Using it elsewhere may require revisiting that assumption.

# File lib/dalli/protocol/base.rb, line 181
def ensure_connected!
  return true if connected?
  return false unless reconnect_down_server?

  connect # This call needs to be in this class so we can do auth
  connected?
end
finish_pipeline() click to toggle source

Called after the noop response is received at the end of a set of pipelined gets

# File lib/dalli/protocol/base.rb, line 219
def finish_pipeline
  response_buffer.clear
  @connection_manager.finish_request!

  true # to simplify response
end
log_marshal_err(key, err) click to toggle source
# File lib/dalli/protocol/base.rb, line 230
def log_marshal_err(key, err)
  Dalli.logger.error "Marshalling error for key '#{key}': #{err.message}"
  Dalli.logger.error 'You are trying to cache a Ruby object which cannot be serialized to memcached.'
end
log_unexpected_err(err) click to toggle source
# File lib/dalli/protocol/base.rb, line 235
def log_unexpected_err(err)
  Dalli.logger.error "Unexpected exception during Dalli request: #{err.class.name}: #{err.message}"
  Dalli.logger.error err.backtrace.join("\n\t")
end
pipelined_get(keys) click to toggle source
# File lib/dalli/protocol/base.rb, line 204
def pipelined_get(keys)
  req = +''
  keys.each do |key|
    req << quiet_get_request(key)
  end
  # Could send noop here instead of in pipeline_response_setup
  write(req)
end
reconnect_on_pipeline_complete!() click to toggle source
# File lib/dalli/protocol/base.rb, line 226
def reconnect_on_pipeline_complete!
  @connection_manager.reconnect! 'pipelined get has completed' if pipeline_complete?
end
response_buffer() click to toggle source
# File lib/dalli/protocol/base.rb, line 213
def response_buffer
  @response_buffer ||= ResponseBuffer.new(@connection_manager, response_processor)
end
verify_allowed_quiet!(opkey) click to toggle source
# File lib/dalli/protocol/base.rb, line 151
def verify_allowed_quiet!(opkey)
  return if ALLOWED_QUIET_OPS.include?(opkey)

  raise Dalli::NotPermittedMultiOpError, "The operation #{opkey} is not allowed in a quiet block."
end
verify_state(opkey) click to toggle source

Checks to see if we can execute the specified operation. Checks whether the connection is in use, and whether the command is allowed

# File lib/dalli/protocol/base.rb, line 161
def verify_state(opkey)
  @connection_manager.confirm_ready!
  verify_allowed_quiet!(opkey) if quiet?

  # The ensure_connected call has the side effect of connecting the
  # underlying socket if it is not connected, or there's been a disconnect
  # because of timeout or other error.  Method raises an error
  # if it can't connect
  raise_down_error unless ensure_connected!
end