Class: Dalli::Server

Inherits:
Object
  • Object
show all
Defined in:
lib/dalli/server.rb

Constant Summary collapse

DEFAULTS =
{
  # seconds between trying to contact a remote server
  :down_retry_delay => 1,
  # connect/read/write timeout for socket operations
  :socket_timeout => 0.5,
  # times a socket operation may fail before considering the server dead
  :socket_max_failures => 2,
  # amount of time to sleep between retries when a failure occurs
  :socket_failure_delay => 0.01,
  # max size of value in bytes (default is 1 MB, can be overriden with "memcached -I <size>")
  :value_max_bytes => 1024 * 1024,
  :compressor => Compressor,
  # min byte size to attempt compression
  :compression_min_size => 1024,
  # max byte size for compression
  :compression_max_size => false,
  :serializer => Marshal,
  :username => nil,
  :password => nil,
  :keepalive => true
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attribs, options = {}) ⇒ Server

Returns a new instance of Server.


34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/dalli/server.rb', line 34

def initialize(attribs, options = {})
  (@hostname, @port, @weight) = attribs.split(':')
  @port ||= 11211
  @port = Integer(@port)
  @weight ||= 1
  @weight = Integer(@weight)
  @fail_count = 0
  @down_at = nil
  @last_down_at = nil
  @options = DEFAULTS.merge(options)
  @sock = nil
  @msg = nil
  @error = nil
  @pid = nil
  @inprogress = nil
end

Instance Attribute Details

#hostnameObject

Returns the value of attribute hostname


6
7
8
# File 'lib/dalli/server.rb', line 6

def hostname
  @hostname
end

#optionsObject

Returns the value of attribute options


9
10
11
# File 'lib/dalli/server.rb', line 9

def options
  @options
end

#portObject

Returns the value of attribute port


7
8
9
# File 'lib/dalli/server.rb', line 7

def port
  @port
end

#sockObject (readonly)

Returns the value of attribute sock


10
11
12
# File 'lib/dalli/server.rb', line 10

def sock
  @sock
end

#weightObject

Returns the value of attribute weight


8
9
10
# File 'lib/dalli/server.rb', line 8

def weight
  @weight
end

Instance Method Details

#alive?Boolean

Returns:

  • (Boolean)

78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/dalli/server.rb', line 78

def alive?
  return true if @sock

  if @last_down_at && @last_down_at + options[:down_retry_delay] >= Time.now
    time = @last_down_at + options[:down_retry_delay] - Time.now
    Dalli.logger.debug { "down_retry_delay not reached for #{hostname}:#{port} (%.3f seconds left)" % time }
    return false
  end

  connect
  !!@sock
rescue Dalli::NetworkError
  false
end

#closeObject


93
94
95
96
97
98
99
# File 'lib/dalli/server.rb', line 93

def close
  return unless @sock
  @sock.close rescue nil
  @sock = nil
  @pid = nil
  @inprogress = false
end

#compressorObject


111
112
113
# File 'lib/dalli/server.rb', line 111

def compressor
  @options[:compressor]
end

#lock!Object


101
102
# File 'lib/dalli/server.rb', line 101

def lock!
end

#multi_response_abortObject

Abort an earlier #multi_response_start. Used to signal an external timeout. The underlying socket is disconnected, and the exception is swallowed.

Returns nothing.


187
188
189
190
191
192
193
194
# File 'lib/dalli/server.rb', line 187

def multi_response_abort
  @multi_buffer = nil
  @position = nil
  @inprogress = false
  failure!
rescue NetworkError
  true
end

#multi_response_completed?Boolean

Did the last call to #multi_response_start complete successfully?

Returns:

  • (Boolean)

129
130
131
# File 'lib/dalli/server.rb', line 129

def multi_response_completed?
  @multi_buffer.nil?
end

#multi_response_nonblockObject

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

Returns a Hash of kv pairs received.


139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/dalli/server.rb', line 139

def multi_response_nonblock
  raise 'multi_response has completed' if @multi_buffer.nil?

  @multi_buffer << @sock.read_available
  buf = @multi_buffer
  pos = @position
  values = {}

  while buf.bytesize - pos >= 24
    header = buf.slice(pos, 24)
    (key_length, _, body_length) = header.unpack(KV_HEADER)

    if key_length == 0
      # all done!
      @multi_buffer = nil
      @position = nil
      @inprogress = false
      break

    elsif buf.bytesize - pos >= 24 + body_length
      flags = buf.slice(pos + 24, 4).unpack('N')[0]
      key = buf.slice(pos + 24 + 4, key_length)
      value = buf.slice(pos + 24 + 4 + key_length, body_length - key_length - 4) if body_length - key_length - 4 > 0

      pos = pos + 24 + body_length

      begin
        values[key] = deserialize(value, flags)
      rescue DalliError
      end

    else
      # not enough data yet, wait for more
      break
    end
  end
  @position = pos

  values
rescue SystemCallError, Timeout::Error, EOFError
  failure!
end

#multi_response_startObject

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.


120
121
122
123
124
125
126
# File 'lib/dalli/server.rb', line 120

def multi_response_start
  verify_state
  write_noop
  @multi_buffer = ''
  @position = 0
  @inprogress = true
end

#nameObject


51
52
53
# File 'lib/dalli/server.rb', line 51

def name
  "#{@hostname}:#{@port}"
end

#request(op, *args) ⇒ Object

Chokepoint method for instrumentation


56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/dalli/server.rb', line 56

def request(op, *args)
  verify_state
  raise Dalli::NetworkError, "#{hostname}:#{port} is down: #{@error} #{@msg}" unless alive?
  begin
    send(op, *args)
  rescue Dalli::NetworkError
    raise
  rescue Dalli::MarshalError => ex
    Dalli.logger.error "Marshalling error for key '#{args.first}': #{ex.message}"
    Dalli.logger.error "You are trying to cache a Ruby object which cannot be serialized to memcached."
    Dalli.logger.error ex.backtrace.join("\n\t")
    false
  rescue Dalli::DalliError
    raise
  rescue => ex
    Dalli.logger.error "Unexpected exception in Dalli: #{ex.class.name}: #{ex.message}"
    Dalli.logger.error "This is a bug in Dalli, please enter an issue in Github if it does not already exist."
    Dalli.logger.error ex.backtrace.join("\n\t")
    down!
  end
end

#serializerObject


107
108
109
# File 'lib/dalli/server.rb', line 107

def serializer
  @options[:serializer]
end

#unlock!Object


104
105
# File 'lib/dalli/server.rb', line 104

def unlock!
end