Class: Dalli::Server

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

Constant Summary

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



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) = parse_hostname(attribs)
  @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



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

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



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

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

#compressorObject



113
114
115
# File 'lib/dalli/server.rb', line 113

def compressor
  @options[:compressor]
end

#lock!Object



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

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.



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

def multi_response_abort
  @multi_buffer = nil
  @position = nil
  @inprogress = false
  failure!(RuntimeError.new('External timeout'))
rescue NetworkError
  true
end

#multi_response_completed?Boolean

Did the last call to #multi_response_start complete successfully?



131
132
133
# File 'lib/dalli/server.rb', line 131

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.



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
181
182
# File 'lib/dalli/server.rb', line 141

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, cas) = 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), cas]
      rescue DalliError
      end

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

  values
rescue SystemCallError, Timeout::Error, EOFError => e
  failure!(e)
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.



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

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
77
78
# File 'lib/dalli/server.rb', line 56

def request(op, *args)
  verify_state
  raise Dalli::NetworkError, "#{hostname}:#{port} is down: #{@error} #{@msg}. If you are sure it is running, ensure memcached version is > 1.4." 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 Timeout::Error
    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



109
110
111
# File 'lib/dalli/server.rb', line 109

def serializer
  @options[:serializer]
end

#unlock!Object



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

def unlock!
end