Accessing your database from Rails Metal

I was interested in using the new Rails Metal system for a project I’ve been building. What I want this code to do is take parameters out of the URL and write a record to my database to record these parameters.

This is my first application built with Rails 2.3, and the first application in my company to use Rails Metal in a production environment.

Here’s what I wrote:

1  def self.call(env)
2      if env["PATH_INFO"] =~ /^\/i\/(\d+)\/(\d+)\/(\d+)\/(\d+)/
3        Record.track($1.to_i, $2.to_i, $3.to_i, $4.to_i)
4        [ 200, { "Content-Type" => "text/html" }, [ "OK" ] ]
5      else
6        [ 404, { "Content-Type" => "text/html" }, [ "Not Found" ] ]
7      end
8  end

This code works great. Runs fast, does what I need.

Until I run it in production under some load!

Suddenly, I see requests backing up and monit is killing my Mongrel’s as the heartbeat call is timing out!

Diagnosing this problem was a bit painful. The RAILS_DEFAULT_LOGGER isn’t available, so I have to create my own logger, and then I used Benchmark.measure to writing out some timings.

The one thing I see is that every 5 calls, a call stalls for exactly 5 seconds. My superfast recording system is taking on average 1 second per request!

The number 5 rings a bell – where did I see that recent;y? I remember now, its in the database.yml file. There’s a new setting for connection pooling. I wonder how that works?

Well, it turns out that Rails Metal really doesn’t set up anything for you, nor does it tear anything down at the end of the request. For Connection Pooling the connection is setup automatically when you access your database, but outside of a Rails Controller, you’d better release this connection before you’re done.

Here’s a simplified version of how I did this:

 1  def self.call(env)
 2    if env["PATH_INFO"] =~ /^\/i\/(\d+)\/(\d+)\/(\d+)\/(\d+)/
 3      begin
 4        Record.track($1.to_i, $2.to_i, $3.to_i, $4.to_i)
 5      ensure
 6        # Release the connections back to the pool
 7        ActiveRecord::Base.clear_active_connections!
 8      end
 9      [ 200, { "Content-Type" => "text/html" }, [ "OK" ] ]
10    else
11      [ 404, { "Content-Type" => "text/html" }, [ "Not Found" ] ]
12    end
13  end