Class: Dalli::Protocol::Base

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/dalli/protocol/base.rb

Overview

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.

Direct Known Subclasses

Meta

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attribs, client_options = {}) ⇒ Base

Returns a new instance of Base.



23
24
25
26
27
28
29
30
# File 'lib/dalli/protocol/base.rb', line 23

def initialize(attribs, client_options = {})
  hostname, port, socket_type, @weight, user_creds = ServerConfigParser.parse(attribs)
  warn_uri_credentials(user_creds)
  @options = client_options.merge(user_creds)
  @raw_mode = client_options[:raw]
  @value_marshaller = @raw_mode ? StringMarshaller.new(@options) : ValueMarshaller.new(@options)
  @connection_manager = ConnectionManager.new(hostname, port, socket_type, @options)
end

Instance Attribute Details

#optionsObject

Returns the value of attribute options.



17
18
19
# File 'lib/dalli/protocol/base.rb', line 17

def options
  @options
end

#weightObject

Returns the value of attribute weight.



17
18
19
# File 'lib/dalli/protocol/base.rb', line 17

def weight
  @weight
end

Instance Method Details

#alive?Boolean

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

Returns:

  • (Boolean)


79
80
81
82
83
84
85
# File 'lib/dalli/protocol/base.rb', line 79

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!Object



87
# File 'lib/dalli/protocol/base.rb', line 87

def lock!; end

#pipeline_abortObject

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.



152
153
154
155
156
157
158
159
160
161
162
# File 'lib/dalli/protocol/base.rb', line 152

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?Boolean

Did the last call to #pipeline_response_setup complete successfully?

Returns:

  • (Boolean)


165
166
167
# File 'lib/dalli/protocol/base.rb', line 165

def pipeline_complete?
  !response_buffer.in_progress?
end

#pipeline_next_responses(&block) ⇒ Object

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?.

When a block is given, yields (key, value, cas) for each response, avoiding intermediate Hash allocation. Returns nil. Without a block, returns a Hash of { key => [value, cas] }.



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/dalli/protocol/base.rb', line 112

def pipeline_next_responses(&block)
  reconnect_on_pipeline_complete!
  values = nil

  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
    unless key.nil?
      if block
        yield key, value, cas
      else
        values ||= {}
        values[key] = [value, cas]
      end
    end

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

  values || {}
rescue SystemCallError, *TIMEOUT_ERRORS, *SSL_ERRORS, EOFError => e
  @connection_manager.error_on_request!(e)
end

#pipeline_response_setupObject

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.



96
97
98
99
100
101
102
# File 'lib/dalli/protocol/base.rb', line 96

def pipeline_response_setup
  verify_pipelined_state(:getkq)
  write_noop
  # Use ensure_ready instead of reset to preserve any data already buffered
  # during interleaved pipelined get draining
  response_buffer.ensure_ready
end

#quiet?Boolean Also known as: multi?

Returns:

  • (Boolean)


169
170
171
# File 'lib/dalli/protocol/base.rb', line 169

def quiet?
  Thread.current[::Dalli::QUIET]
end

#raw_mode?Boolean

Returns true if client is in raw mode (no serialization/compression). In raw mode, we can skip requesting bitflags from the server.

Returns:

  • (Boolean)


34
35
36
# File 'lib/dalli/protocol/base.rb', line 34

def raw_mode?
  @raw_mode
end

#request(opkey, *args) ⇒ Object

Chokepoint method for error handling and ensuring liveness



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/dalli/protocol/base.rb', line 39

def request(opkey, *args)
  verify_state(opkey)

  begin
    request_completed = false
    @connection_manager.start_request!
    response = send(opkey, *args)

    # pipelined_get/pipelined_get_interleaved emit query but don't read the response(s)
    @connection_manager.finish_request! unless %i[pipelined_get pipelined_get_interleaved].include?(opkey)

    request_completed = true
    response
  rescue Dalli::MarshalError => e
    log_marshal_err(args.first, e)
    raise
  rescue Dalli::DalliError
    raise
  rescue StandardError => e
    log_unexpected_err(e)
    close
    raise
  ensure
    # If the begin block didn't complete -- any exception, including a
    # non-StandardError such as Async::Stop or Thread#kill, which the
    # rescue clauses above never see -- tear down the connection so a
    # half-used client isn't returned to the pool.
    #
    # The local flag is deliberate: reading $ERROR_INFO here would see
    # the *outer* exception when `request` is called from inside a
    # rescue clause (a common cache-fallback shape), and would then
    # falsely tear down the pipelined_get happy path, which leaves
    # @request_in_progress true on purpose until the caller drains.
    close unless request_completed
  end
end

#unlock!Object



89
# File 'lib/dalli/protocol/base.rb', line 89

def unlock!; end