Nice Keep-Alive for Sinatra Apps

Ruby

Today I've been working very hard to get this ruby app (Sinatra) to have a keep-alive with requests that take a very long time to complete. Typically, you might think this was a case for streaming the data, and in some cases, that's true. But in many of the cases I'm dealing with, the server has to marshall a lot of data, piece it together, and then format the output. It's really not ready until it's all ready. So I needed a simple keep-alive.

Back in a previous job, I used Tomcat and just had a thread that sent a space every 10 seconds to the client until I stopped it. I then put this in the rendering code, and all of a sudden, everyone got this keep-alive code, and no one had to worry about a thing. I know it's not going to be that easy this time, but it's not that far off.

First, we need to make sure that we're using the 'Thin' server in Sinatra. WEBrick doesn't support an event model, and so won't be starting an instance of EventMachine. This is thankfully pretty easy to do, just require 'thin', and backup will detect it and we're good to go:

  require 'thin'

Next, we need to put the keep-alive in the individual calls. This one, for instance, takes a while to complete:

  get "/call_list_spreadsheet/:dbname/:extag.tsv" do
    content_type('text/tab-separated-values')
    attachment("call_list-#{params[:dbname]}-#{params[:extag]}.tsv")
    stream do |out|
      timer = EventMachine.add_periodic_timer(10) do
        out.write("\0")
      end
      out.write(CallListTSV.new(params[:dbname], params[:extag]).render)
      timer.cancel
      out.flush
    end
  end

the key component here is that little bit in the middle:

      timer = EventMachine.add_periodic_timer(10) do
        out.write("\0")
      end

Simply put, that's going to send a NULL to the client every 10 sec. until the timer is cancelled. This works like a charm to do what I need. Amazingly so. I was really quite pleased.

All this is in a nice little gist.