Corporate Blog


5 ways to boost performance of your Rails applications


by , July 23, 2010, Ruby on Rails

There are many ways of how you can boost performance of Ruby On Rails applications. Approaches might be different and depend on the application structure, size of the database and traffic intensity but a general recommendation can also be given.

In this article we will overview techniques and architectural solutions that will help you to improve performance of your applications.

Use Caching

Rails provides three types of caching mechanisms out of the box which you can start using immediately. These are:

  • Page Caching
    A cached page served by the webserver without going through the Rails stack. It’s super fast but can’t be applied to every situation.

    class ProductsController < ActionController
    caches_page :index
    def index
    @products = Products.all
    end
    def create
    expire_page :action => :index
    end
    end
    

    The first time user requests /products, Rails will generate a file called products.html which will be passed to the next request by the webserver without invoking Rails.

  • Action Caching
    It’s similar to Page Caching but the incoming request always goes through the Rails stack. It allows us to use authentication and other restrictions you can’t do with page caching.

    class ProductsController < ActionController
    before_filter :authenticate
    caches_action :index
    def index
    @products = Product.all
    end
    def create
    expire_action :action => :index
    end
    end
    
  • Fragment Caching
    Unfortunately, caching the whole page is seldom possible when you’re developing dynamic web applications. But Rails provides a mechanism called Fragment Caching. It allows a fragment of view logic to be wrapped in a cache block and served out of the cache store when the next request comes in.

    <% Order.find_recent.each do |o| %>
    <%= o.buyer.name %> bought <% o.product.name %>
    <% end %> 
    
    <% cache do %>
    All available products:
    <% Product.all.each do |p| %>
    <%= link_to p.name, product_url(p) %>
    <% end %>
    <% end %>
    

Rails has different stores for the cached data created by action and fragment caches. Page caches are always stored on disk. The default cache stores are MemoryStore, FileStore, DrbStore and Memcached store.

Rails uses the bundled memcached-client gem by default for Memcached store. Since Memcached supports clustering and load balancing it’s a great solution for scaling your application.

Keep in mind that caching always brings more complexity to the application and makes it harder to debug.

Database Optimization

Interacting with database is usually the slowest part of the application. Hopefully, there many things you can do to improve the performance:

  • Add all necessary indexes for primary and foreign keys and for fields used in conditions for filtering
  • Remove unused or ineffective indexes
  • Revise SQL queries and optimize them (use the EXPLAIN command)
  • Use eager loading of associations in Rails models
  • Don’t use transactions if they are not necessary (for example, in MySQL you can use MyISAM table engine which is much faster than InnoDB)
  • Use stored procedures
  • Denormalize some tables from 3-d form to 2-nd to avoid redundant joins
  • Perform partitioning for large tables

Cutting down the number of SQL queries is one of the many ways to improve the performance of your Rails application, and eager loading is probably the most effective way to do that.
Eager loading comes into play when you need to eliminate “1+N” query problem: if you load N objects from class Article (table “articles”), which has a n-1 relationship to class Author (table “authors”), accessing the author of a given article using the generated accessor methods will cause N additional queries to the database. This, of course, puts some additional load on the database, but more importantly for Rails application server performance, the SQL query statements to be issued will be reconstructed for object accessed.
You can get around this overhead by adding :include => :author to your query parameters like so:

Articles.find(:all, :conditions => ..., :include => :author)

This will avoid all of the above mentioned overhead by issuing a single SQL statement and constructing the author objects immediately. This technique can also be used with other relationship types (such as 1-1, 1-n or n-m).

Scaling Out Your Database

There are two main approaches for addressing scalability through database clustering:

  • Database Replication
    It’s used to address concurrent access to the same data. Database replication enables us to load-balance the access to the shared data elements among multiple replicated database instances. In this way we can distribute the load across database servers, and maintain performance even if the number of concurrent users increases.

    There is a plugin for Rails called Masochism, which provides an easy solution for applications to work in a replicated database environment. It works by replacing the connection object accessed by ActiveRecord models by ConnectionProxy that chooses between master and slave when executing queries. Generally all writes go to master.
  • Database partitioning/sharding

    Database shards/partitions enable the distribution of data on multiple nodes. In other words, each node holds part of the data. This is a better approach for scaling both read and write operations, as well as more efficient use of capacity, as it reduces the volume of data in each database instance.

    Use third party solutions such as Apache Lucene/Solr or Sphinx to do full-text search against your database. These are very fast search engines that index data and provide flexible ways of searching it.

Use Load Balancing

It can be good to use load balancing, a technique when incoming requests are distributed to other servers depending on their current load. There are 3 main approaches:

  • Use load balancing solutions, e.g. HAProxy which supports a very high number of simultaneous incomming connections at very high speeds.
  • Use partial processing on the main server and distribute workload to other servers after the initial processing. It can be done by 3 ways:
    1. Redirect requests to servers over HTTP by their URLs
    2. Use messaging systems such as Active MQ, RabbitMQ, MQSeries
    3. Use PgMQ client for PostgreSQL with other AMQP systems
  • Use dedicated servers for content distribution while logic is handled by the main server
  • Combine all approaches mentioned above

Front-end optimization

Users spend a lot of time on waiting browser to finish downloading all page components such as images, style sheets, scripts, etc. Reducing the amount of components will minimize the number of HTTP requests which will lead to the faster page loading.

You can achieve this by combining style sheet files and JavaScript files as well as using CSS sprites and image maps.

To combine all CSS and JavaScript files into one in Rails, you can do by using the following commands:

<%= javascript_include_tag :all, :cache => true %>
<%= stylesheet_link_tagi :all, :cache => true %>

You can go further and minimize combined files by using a gem called asset_packeger
It can also be a good idea to move images and videos to services like Amazon S3 or even use CDNs (Content Delivery Networks).

You can cache at the client side and use AJAX like Prototype and JQuery to stream in data to the browser on demand.
Yahoo developed a Firefox plug-in called YSlow which gives you tips on how to optimize your page loading.

Conclusion

We briefly looked at some of approaches you can use to improve performance of your Rails applications. These include using caching, optimizing and scaling your database, using load balancing and several techniques for front-end optimization.
A few pieces of advice we would like to add in the end:

  • Always measure your application’s performance after steps you take to improve it
  • Optimize only what’s slow
  • You need to continually test performance

Resources

Share
Add comment Share

3 Responses to “5 ways to boost performance of your Rails applications”

  1. by Chris, July 26, 2010

    This is very clear and helpful. Thank you for these tips.

  2. by Nick Johnson, July 28, 2010

    Thanks for the article it was quite useful for me.

  3. by makuchaku, October 10, 2010

    Nicely covered all the options!

Add Comment

Enter symbols from the box below to make us sure that you're not a robot: