Friday, September 17, 2010

Use Signal.trap("TERM") to support graceful shutdown

This server (http://github.com/wanli/avro_client/blob/master/test/sample_server.rb) doesn't trap any signals, so when you kill it with a TERM signal, it will shutdown even when it is in the middle of processing a request. We can make a few changes to support graceful shutdown.

First we need to setup signal traps:

  def setup_signal_traps
    Signal.trap("TERM") do
      @handler.stop if @handler
    end
  end

Then we need to add a variable @running and a method stop() to the handler:

  class SampleHandler
    def initialize(name, address, port)
      @name = name
      @ip_address = address
      @port = port
      # init running variable
      @running = true
    end
    def stop
      @running = false
    end
  end

Finally, we need to change the run() method to check the @running variable and don't wait for the select call forever:

  def run(&block)
    server = TCPServer.new(@ip_address, @port)
    sockets = [server]
    while @running
      # only wait for up to 1 sec at a time. 
      ready = select(sockets, nil, nil, 1)
      next unless ready
      readable = ready[0]
      readable.each do |socket|
        if socket == server
          client = server.accept
          sockets << client
        else
          begin
            handle(socket)
            yield
          rescue => e
            sockets.delete(socket)
            socket.close
          end
        end
      end
    end
  end

Friday, September 3, 2010

Use ActiveRecord::Base.logger.buffer in unit test

If you use ActiveRecord, sometimes you want to write a test to check the Sql statements that are actually used. This can be easily done with ActiveRecord::Base.logger.buffer:

  def test_what_sql_statements_are_used
    ActiveRecord::Base.logger.flush
    ActiveRecord::Base.logger.auto_flushing=false
    DoSomeThing
    assert_equal 2, ActiveRecord::Base.logger.buffer.size
    assert ActiveRecord::Base.logger.buffer[0] =~ /SELECT id FROM `T1` WHERE .* ORDER BY id DESC LIMIT .*/
    assert ActiveRecord::Base.logger.buffer[1] =~ /SELECT id FROM `T2` WHERE .* ORDER BY id DESC LIMIT .*/   
  end

ActiveRecord::Base.logger.flush flushes the existing entries in the buffer, and setting ActiveRecord::Base.logger.auto_flushing=false makes sure all the new entries will be kept in the buffer.