Rails Metal has been available on Rails since version 2.3 – it’s old news. But if you haven’t used it or heard about it, you can find out more about Rails Metal on the RoR weblog and on Jesse Newland’s blog.
So anyway, I am one of those laggards and only wrote a Rails Metal piece not too long ago for a Rails app on Wego.com in an effort to optimize some high volume requests. It looked something like this:
class Adamantine
def self.call(env)
if env['PATH_INFO'] =~ ROUTE_REGEX
location = Location.find(1) # Use ActiveRecord.
[200, { 'Content-Type' => 'text/html' }, [location.to_json]]
else
# Leave it to Rails to deal with the request.
[404, { 'Content-Type' => 'text/html' }, ['Not Found']]
end
end
end
Notice the use of an ActiveRecord model. It ran for quite awhile, almost 30 minutes, in production until I started getting notification emails about “Too many open database connections” to the MySQL server! The change was promptly rolled back and there was no cheezburger for me.
As it turns out, Rails doesn’t take care of certain things in Rails Metal pieces, including the releasing of connections back to the database connection pool. A bit of googling turned up this bug in Rails’ bug tracker (see Josh Peek’s comment about ActiveRecord::Base.clear_active_connections!
).
I rewrote the Rails Metal piece to always ensure it clears any active database connections with ActiveRecord::Base.clear_active_connections!
:
class Adamantine
def self.call(env)
if env['PATH_INFO'] =~ ROUTE_REGEX
location = Location.find(1)
[200, { 'Content-Type' => 'text/html' }, [location.to_json]]
else
# Leave it to Rails to deal with the request.
[404, { 'Content-Type' => 'text/html' }, ['Not Found']]
end
ensure
# Release the connections back to the pool.
ActiveRecord::Base.clear_active_connections!
end
end
Moral of the story: Don’t forget to release your database connections if you’re using ActiveRecord in your Rails Metal. Or even better, don’t use ActiveRecord in Rails Metal – you’re aiming for raw speed anyway right?