Performance On Rails
So, you just finished reading your copy of Agile Web Development with Rails, and you’re ready to launch an Internet startup.
You get everything set up: hosting, database, subversion…you put the finishing touches on your capistrano deployment recipe. Your app is now online, ready for the whole wide world to see.
You tell all your friends about it, and they tell a few friends and before you know it you have about 50 people playing around on your new website. It’s exciting, and you think you may be on to something that a lot of people will want to use.
Then, all of a sudden, your web site slows to a crawl. Maybe there are too many hops between your users and the servers (in my case, it was Tennessee and Mississippi users with servers in California). But it’s not. It’s your code. You have a scaling problem.
My scaling problem came pretty quickly, and I thought it was a hosting problem. So, to prepare to change hosting plans, I downloaded a backup of my production database. Since I had it, I figured it wouldn’t be a bad idea to install the data into the development database for my Rails app. I launched my app, and much to my surprise it was as slow on my iMac as the production website! (even though my iMac has much more RAM and I’m hitting localhost)
Why was it so slow? Too much database activity. Some people would see it and blame Active Record. But it’s not Active Record’s fault. Active Record provides a lot of nice conveniences for developers, but in a production environment they can often turn into performance nightmares if not used cautiously.
So, here is a set of guidelines I’ve discovered to help improve the performance of your Rails app:
Lesson 1. Download YSlow and Follow Its Suggestions
YSlow is a performance report card tool for Firebug. If you don’t already have Firebug, install it, and then install YSlow and pay attention to what it tells you. For most web apps, the performance problems are on the front end and YSlow will tell you exactly what you need to do to fix those problems. The only problem I’ve encountered is that I can’t yet move my Javascript reference to the bottom because it breaks the in_place_editor helper supplied by Rails.
Lesson 2. Denormalize Your Database Schema
greenisus revolves around asking questions in an online poll form and voting on questions submitted by other people. When you’re looking at a list view at greenisus, you can see how many votes and comments have already been submitted for a question.

Figure 1. List view vote and comment counts at greenisus.
At first, I coded this in a way that was more developer-friendly than database-friendly. I have a model for my questions and model for the votes and comments. The questions model had lines like this:
has_many :votes has_many :comments
And in my view, I had code like this to show the counts:
So far... <%= pluralize question.votes.size, 'vote' %> and <%= pluralize question.comments.size, 'comment' %>
The problem with this is that question.votes does an individual SELECT for each vote for the question. This is terribly inefficient, so I changed my question model to have two new fields: vote_count and comment_count. Now, I reference situation.vote_count instead, and there are no extra SELECTs slowing the site down. The data is redundant, but it’s much faster to load, and that’s more important in the real world.
Lesson 3. Add Indexes To Your Models
If you’re showing items newest-first, don’t say :order => 'creation_date desc', say :order => 'id desc'. It means the same thing, and id is indexed, so your database doesn’t have to sort the results before returning them to you. If you’re showing items by a user_id, you can’t change your :conditions => ['user_id = ?', some_user_id], but you can add an index and get a great speed boost from your database. Just do it in a migration:
def self.up add_index :my_table, :whatever_column end def self.down remove_index :my_table, :whatever_column end

Figure 2. Smilin’ Bob (The Enzyte Guy) Used These Tips To Enhance His Ruby on Rails Performance
I’m sure I’ll talk about performance again, but the two biggest sources of performance woes that I’ve seen have always been:
- Inefficient front-end decisions
- Inefficient use of the database layer
So remember to get YSlow, and watch your development logs with large amounts of data. You won’t see how complicated your site is when you only have a few pages worth of data in your development database.
[...] Check This Out! While looking through the blogosphere we stumbled on an interesting post today. Here’s a quick excerpt: Since I had it, I figured it wouldn’t be a bad idea to install the data into the development database for my Rails app. I launched my app, and much to my surprise it was as slow on my iMac as the production website! … [...]
Tektag / tim…
My scaling problem came pretty quickly, and I thought it was a hosting problem. So, to prepare to change hosting plans, I downloaded a backup of my production database. Since I had it, I figured it wouldn’t be a bad idea to install the data into the …