How do I make my Rails app faster?

I I’d like to provide an overview of my process for improving the performance of a Ruby on Rails application, leaving the specific changes to other, more detailed posts.

The timeline at the top shows a basic view of what happens when the user clicks on a link to get a page from your site. There are three main pieces, the Connection - DNS lookup, setting up a connection to the server, SSL negotiation. The next part is your Ruby on Rails code that processes the request and generates the response. The final part is when the Browser processes the HTML, applies the CSS, runs the Javascript, and downloads images and other page widgets.

The first thing to do is to measure the size of each of these pieces for the pages you care about. I usually start with the page that users are expected to never the site at, either the public landing page, or right after a Login page for private sites. Modern browsers have Developer Tools built in that include a Network chart that shows all of the information you need to have at this stage.

The very first item in the Network chart is your main web page. For a Ruby on Rails app, this is all based on your Rails code on the server. The longer it takes for this piece to start, then finish, the longer your user is waiting before anything else can start.

If your Rails code is taking more than a second to get HTML back to the browser, this is a good place to start looking to improve performance. Looking at the Rails log files will give you a good idea of what parts of the code are taking a long time to execute.

These are the first techniques I use to make my Rails code faster.

Eager loading of associated data

If you’re loading an object in the controller, and then accessing associated records later in the view code, then you should change the object loading code to include those associated records. The reduces the number of queries that Active Record makes to your database, which is typically on a different server machine.

Here’s an example of a Blog Post with multiple Comments. The first code causes a query for the post, then one query for every comment

Making a small change to the controller code reduces the number of queries to 2 - ActiveRecord does some magic to merge the query results together
~~~
# controller
def show
@post = BlogPost.includes(:comments).find(params[:id])
end
~~~

Cache models to avoid database calls

Often, it is possible to cache items using the Rails.cache object. Usually you want this to be attached to a memcache instance for best performance. You need to be able to expire these objects in some way, but I won’t cover that here, as that is a much bigger topic

Fragment Cache

The Rails Fragment Cache is a mechanism to allow you to cache the parts of your view that don’t change frequently. After a fragment is generated, it is stored in the Rails cache to be used later. A useful way of avoiding the problem of expiring specific items in the cache is to use a value that changes any time the cached object changes. Typically, Rails objects have an updated_at value that is changed every time that object is saved. This value can be used as part of the fragment cache key to use the cached fragment if the object hasn’t changed, and re-create the fragment if the object is different. For ActiveRecord objects, Rails builds this key automatically!

This technique requires a cache store like memcache that can automatically get rid of older items that are no-longer used, otherwise your cache will grow uncontrollably.

Here’s an example from our blog software - the cache key will be something like “blogposts/{id}-{updated_at}”

Rails 4 will add an exciting new dimension to this feature that I’ll be covering in a later post.

Use The HTTP ETag helpers

Rails recently added helper methods to make it easy to handle HTTP ETags in your controller code, fresh_when and stale?. These can be used to respond to requests with HTTP ETag headers - adding the ETag to the response, and allowing you to skip the response processing completely.

Look how we combined two techniques here - this action processing does a single memcache lookup and then returns the HTTP Not Modified response code to the client, for clients that have visited before.

Summary

In many real world Ruby on Rails applications, the interactions are more complicated than those shown here, but the basic principals remain. With some very simple changes, the performance and scalability of your Rails application can be improved quite a lot, and this maybe all you need to get your application to acceptable performance.

About the Author

Tom Fakes has been building applications with Ruby on Rails for seven years, and is an expert on high performance rails applications.

Mr Fakes was previously the Chief Engineer and co-founder of Offbeat Creations,
a social gaming company that used Ruby on Rails for all their games, scaling them to hundreds-of-thousands of daily
users. In other positions, Mr Fakes has built high scale production applications for Loudeye, Inc and Microsoft.

Mr Fakes is now a consultant providing his expertise to make
your Ruby on Rails applications scale. Get in touch now to
start the process to improve your customer experience, get better conversion rates and
improve your revenue.