class Memcache::Server

Constants

CONNECT_TIMEOUT
DEFAULT_PORT
ESCAPE
READ_RETRY_DELAY
UNESCAPE

Attributes

host[R]
port[R]
retry_at[R]
status[R]

Public Class Methods

new(opts) click to toggle source
# File lib/memcache/server.rb, line 13
def initialize(opts)
  @host         = opts[:host]
  @port         = opts[:port] || DEFAULT_PORT
  @strict_reads = opts[:strict_reads]
  @status       = 'NOT CONNECTED'
  @retry_at     = nil
  @socket       = nil
end

Public Instance Methods

add(key, value, expiry = 0, flags = 0) click to toggle source
# File lib/memcache/server.rb, line 138
def add(key, value, expiry = 0, flags = 0)
  response = write_command("add #{cache_key(key)} #{flags.to_i} #{expiry.to_i} #{value.to_s.size}", value)
  response == "STORED\r\n" ? value : nil
end
alive?() click to toggle source
# File lib/memcache/server.rb, line 34
def alive?
  @retry_at.nil? or @retry_at < Time.now
end
append(key, value) click to toggle source
# File lib/memcache/server.rb, line 148
def append(key, value)
  response = write_command("append #{cache_key(key)} 0 0 #{value.to_s.size}", value)
  response == "STORED\r\n"
end
cas(key, value, cas, expiry = 0, flags = 0) click to toggle source
# File lib/memcache/server.rb, line 133
def cas(key, value, cas, expiry = 0, flags = 0)
  response = write_command("cas #{cache_key(key)} #{flags.to_i} #{expiry.to_i} #{value.to_s.size} #{cas.to_i}", value)
  response == "STORED\r\n" ? value : nil
end
clone() click to toggle source
# File lib/memcache/server.rb, line 22
def clone
  self.class.new(:host => host, :port => port, :strict_reads => strict_reads?)
end
close(error = nil) click to toggle source
# File lib/memcache/server.rb, line 42
def close(error = nil)
  # Close the socket. If there is an error, mark the server dead.
  @socket.close if @socket and not @socket.closed?
  @socket = nil

  if error
    @retry_at = Time.now + READ_RETRY_DELAY
    @status   = "DEAD: %s: %s, will retry at %s" % [error.class, error.message, @retry_at]
  else
    @retry_at = nil
    @status   = "NOT CONNECTED"
  end
end
count() click to toggle source
# File lib/memcache/server.rb, line 72
def count
  stats['curr_items']
end
decr(key, amount = 1) click to toggle source
# File lib/memcache/server.rb, line 117
def decr(key, amount = 1)
  raise Error, "decr requires unsigned value" if amount < 0
  response = write_command("decr #{cache_key(key)} #{amount}")
  response == "NOT_FOUND\r\n" ? nil : response.slice(0..-3).to_i
end
delete(key) click to toggle source
# File lib/memcache/server.rb, line 123
def delete(key)
  write_command("delete #{cache_key(key)}") == "DELETED\r\n" ? true : nil
end
flush_all(delay = nil) click to toggle source
# File lib/memcache/server.rb, line 76
def flush_all(delay = nil)
  write_command("flush_all #{delay}")
end
get(keys, cas = nil) click to toggle source
# File lib/memcache/server.rb, line 80
def get(keys, cas = nil)
  return get([keys], cas)[keys.to_s] unless keys.kind_of?(Array)
  return {} if keys.empty?

  method = cas ? 'gets' : 'get'

  results = {}
  keys = keys.collect {|key| cache_key(key)}

  read_command("#{method} #{keys.join(' ')}") do |response|
    if cas
      key, flags, length, cas = match_response!(response, /^VALUE ([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+)/)
    else
      key, flags, length = match_response!(response, /^VALUE ([^\s]+) ([^\s]+) ([^\s]+)/)
    end

    value = socket.read(length.to_i)
    match_response!(socket.read(2), "\r\n")

    result = {
      :value => value,
      :flags => flags.to_i,
    }
    result[:cas] = cas if cas

    key = input_key(key)
    results[key] = result
  end
  results
end
incr(key, amount = 1) click to toggle source
# File lib/memcache/server.rb, line 111
def incr(key, amount = 1)
  raise Error, "incr requires unsigned value" if amount < 0
  response = write_command("incr #{cache_key(key)} #{amount}")
  response == "NOT_FOUND\r\n" ? nil : response.slice(0..-3).to_i
end
inspect() click to toggle source
# File lib/memcache/server.rb, line 26
def inspect
  "<#{self.class.name}: %s:%d (%s)>" % [@host, @port, @status]
end
name() click to toggle source
# File lib/memcache/server.rb, line 30
def name
  "#{host}:#{port}"
end
prepend(key, value) click to toggle source
# File lib/memcache/server.rb, line 153
def prepend(key, value)
  response = write_command("prepend #{cache_key(key)} 0 0 #{value.to_s.size}", value)
  response == "STORED\r\n"
end
replace(key, value, expiry = 0, flags = 0) click to toggle source
# File lib/memcache/server.rb, line 143
def replace(key, value, expiry = 0, flags = 0)
  response = write_command("replace #{cache_key(key)} #{flags.to_i} #{expiry.to_i} #{value.to_s.size}", value)
  response == "STORED\r\n" ? value : nil
end
set(key, value, expiry = 0, flags = 0) click to toggle source
# File lib/memcache/server.rb, line 127
def set(key, value, expiry = 0, flags = 0)
  return delete(key) if value.nil?
  write_command("set #{cache_key(key)} #{flags.to_i} #{expiry.to_i} #{value.to_s.size}", value)
  value
end
stats() click to toggle source
# File lib/memcache/server.rb, line 56
def stats
  stats = {}
  read_command('stats') do |response|
    key, value = match_response!(response, /^STAT ([\w]+) (-?[\w\.\:]+)/)

    if ['rusage_user', 'rusage_system'].include?(key)
      seconds, microseconds = value.split(/:/, 2)
      microseconds ||= 0
      stats[key] = Float(seconds) + (Float(microseconds) / 1_000_000)
    else
      stats[key] = (value =~ /^-?\d+$/ ? value.to_i : value)
    end
  end
  stats
end
strict_reads?() click to toggle source
# File lib/memcache/server.rb, line 38
def strict_reads?
  @strict_reads
end

Protected Instance Methods

cache_key(key) click to toggle source
Calls superclass method Memcache::Base#cache_key
# File lib/memcache/server.rb, line 176
def cache_key(key)
  key = key.gsub(/[\s\]/) {|c| ESCAPE[c]}
  super(key)
end
input_key(key) click to toggle source
# File lib/memcache/server.rb, line 170
def input_key(key)
  key = key[prefix.size..-1] if prefix # Remove prefix from key.
  key = key.gsub(/\./) {|c| UNESCAPE[c]}
  key
end

Private Instance Methods

match_response!(response, regexp) click to toggle source
# File lib/memcache/server.rb, line 183
def match_response!(response, regexp)
  # Make sure that the response matches the protocol.
  unexpected_eof! if response.nil?
  match = response.match(regexp)
  raise ServerError, "unexpected response: #{response.inspect}" unless match

  match.to_a[1, match.size]
end
read_command(command) { |response| ... } click to toggle source
# File lib/memcache/server.rb, line 223
def read_command(command, &block)
  raise ConnectionError, "Server #{name} dead, will retry at #{retry_at}" unless alive?
  send_command(command) do |response|
    while response do
      return if response == "END\r\n"
      yield(response)
      response = socket.gets
    end
    unexpected_eof!
  end
rescue Exception => e
  puts "Memcache read error: #{e.class} #{e.to_s}"
  raise(e) if strict_reads?
end
send_command(*command) { |response| ... } click to toggle source
# File lib/memcache/server.rb, line 192
def send_command(*command)
  command = command.join("\r\n")
  socket.write("#{command}\r\n")
  response = socket.gets

  unexpected_eof! if response.nil?
  if response =~ /^(ERROR|CLIENT_ERROR|SERVER_ERROR) (.*)\r\n/
    raise ($1 == 'SERVER_ERROR' ? ServerError : ClientError), $2
  end

  block_given? ? yield(response) : response
rescue Exception => e
  close(e) # Mark dead.
  raise e if e.kind_of?(Error)
  raise ConnectionError.new(e)
end
socket() click to toggle source
# File lib/memcache/server.rb, line 238
def socket
  if @socket.nil? or @socket.closed?
    # Attempt to connect.
    @socket = timeout(CONNECT_TIMEOUT) do
      TCPSocket.new(host, port)
    end
    if Socket.constants.include? 'TCP_NODELAY'
      @socket.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
    end

    @retry_at = nil
    @status   = 'CONNECTED'
  end
  @socket
end
unexpected_eof!() click to toggle source
# File lib/memcache/server.rb, line 254
def unexpected_eof!
  raise Error, 'unexpected end of file'
end
write_command(*command, &block) click to toggle source
# File lib/memcache/server.rb, line 209
def write_command(*command, &block)
  retried = false
  begin
    send_command(*command, &block)
  rescue Exception => e
    puts "Memcache write error: #{e.class} #{e.to_s}"
    unless retried
      retried = true
      retry
    end
    raise(e)
  end
end