module EM::Voldemort::Connection::Handler

EventMachine handler for a Voldemort node connection

Attributes

connection[R]

The EM::Voldemort::Connection object for which we’re handling the connection

in_flight[R]

If a request is currently in flight, this is a deferrable that will succeed or fail when the request completes. The protocol requires that only one request can be in flight at once.

last_request[R]

The time at which the request currently in flight was sent

request_queue[R]

Array of [request_data, deferrable] pairs, containing requests that have not yet been sent

state[R]

State machine. One of :connecting, :protocol_proposal, :idle, :request, :disconnected

Public Class Methods

new(connection) click to toggle source
# File lib/em-voldemort/connection.rb, line 112
def initialize(connection)
  @connection = connection
  @state = :connecting
  @in_flight = EM::DefaultDeferrable.new
  @last_request = Time.now
  @request_queue = []
end

Public Instance Methods

close_gracefully() click to toggle source

Connection is asking us to shut down. Wait for the currently in-flight request to complete, but fail any unsent requests in the queue.

# File lib/em-voldemort/connection.rb, line 190
def close_gracefully
  @request_queue.each {|request, deferrable| deferrable.fail(ServerError.new('shutdown requested')) }
  @request_queue = []
  if in_flight
    in_flight.callback { close_connection }
    in_flight.errback  { close_connection }
  else
    close_connection
  end
end
enqueue_request(request) click to toggle source
# File lib/em-voldemort/connection.rb, line 120
def enqueue_request(request)
  EM::DefaultDeferrable.new.tap do |deferrable|
    request_queue << [request, deferrable]
    send_next_request unless in_flight
  end
end
post_init() click to toggle source

Connection established (called by EventMachine)

# File lib/em-voldemort/connection.rb, line 148
def post_init
  connection.logger.info "Connected to Voldemort node at #{connection.host}:#{connection.port}"
  send_protocol_proposal(connection.protocol)
  in_flight.errback do |response|
    connection.logger.warn "Voldemort protocol #{connection.protocol} not accepted: #{response.inspect}"
  end
end
receive_data(data) click to toggle source

The Voldemort node is talking to us (called by EventMachine)

# File lib/em-voldemort/connection.rb, line 157
def receive_data(data)
  case @state
  when :protocol_proposal
    deferrable = @in_flight
    @state = :idle
    @in_flight = nil
    if data == 'ok'
      send_next_request
      deferrable.succeed
    else
      close_connection
      deferrable.fail("server response: #{data.inspect}")
    end

  when :request
    @recv_buf << data
    response_size = @recv_buf.unpack('N').first
    if response_size && @recv_buf.bytesize >= response_size + 4
      response = @recv_buf[4, response_size]
      deferrable = @in_flight
      @state = :idle
      @in_flight = @recv_buf = nil
      send_next_request
      deferrable.succeed(response)
    end

  else
    raise "Received data in unexpected state: #{@state.inspect}"
  end
end
send_next_request() click to toggle source

Takes the request at the front of the queue and sends it to the Voldemort node

# File lib/em-voldemort/connection.rb, line 137
def send_next_request
  return if request_queue.empty?
  raise "cannot make a request while in #{@state.inspect} state" unless @state == :idle
  request, @in_flight = request_queue.shift
  send_data([request.size, request].pack('NA*'))
  @recv_buf = ''.force_encoding('BINARY')
  @last_request = Time.now
  @state = :request
end
send_protocol_proposal(protocol) click to toggle source

First action when the connection is established: client tells the server which version of the Voldemort protocol it wants to use

# File lib/em-voldemort/connection.rb, line 129
def send_protocol_proposal(protocol)
  raise ArgumentError, 'protocol must be 3 bytes long' if protocol.bytesize != 3
  raise "unexpected state before protocol proposal: #{@state.inspect}" unless @state == :connecting
  send_data(protocol)
  @state = :protocol_proposal
end
unbind(reason=nil) click to toggle source

Connection closed (called by EventMachine)

# File lib/em-voldemort/connection.rb, line 202
def unbind(reason=nil)
  @state = :disconnected
  deferrable = @in_flight
  @in_flight = nil
  deferrable.fail(ServerError.new('connection closed')) if deferrable
  @request_queue.each {|request, deferrable| deferrable.fail(ServerError.new('connection closed')) }
  @request_queue = []
  connection.connection_closed(self, reason)
end