Home arrow Ruby-on-Rails arrow Page 2 - Common Performance Problems in Rails
RUBY-ON-RAILS

Common Performance Problems in Rails


In this conclusion to a six-part article series on optimizing the performance of a Ruby on Rails program, you'll learn about the most common performance problems you might encounter in Rails, and how to avoid them. This article is excerpted from chapter 13 of the book Practical Rails Projects, written by Eldon Alameda (Apress; ISBN: 1590597818).

Author Info:
By: Apress Publishing
Rating: 5 stars5 stars5 stars5 stars5 stars / 1
December 29, 2010
TABLE OF CONTENTS:
  1. · Common Performance Problems in Rails
  2. · Database Access

print this article
SEARCH DEVARTICLES

Common Performance Problems in Rails - Database Access
(Page 2 of 2 )

We already mentioned in Chapter 4 how fetching a list of books and then referencing their authors can lead to 2n+1 queries, but itís worth a reminder. Use the :includeparameter in finders if you know you are going to use the associated objects, like authors, on the page:

def index
  @book_pages, @books = paginate :books,
                          :per_page => 10,
                         
:include => [:authors, :publisher],
                      
:order => "books.id desc"
end

Another common database performance problem arises when you forget to add needed indices to the database. This is not a Rails-specific problem per se, but it is easy to forget when building an application with small sample data. When the amount of data then grows, you might start wondering why queries that were so snappy when testing are now taking a minute or an hour (no kidding!).

If you track the root of a slow action to a certain database query (or find one by following the slow query log with MySQL, as described in Chapter 10), the next step is to analyze that query. Most database vendors have their own tools for that. In MySQL and PostgreSQL, you can useEXPLAIN [SELECT query]for this. As an example, letís runEXPLAIN on a query that is run whenever the index page of the catalog controller is loaded (weíve truncated the output lines to make them more readable):

mysql> EXPLAIN SELECT COUNT(DISTINCT books.id)
   
-> FROM books LEFT OUTER JOIN authors_books
   
-> ON authors_books.book_id = books.id
   
-> LEFT OUTER JOIN authors
   
-> ON authors.id = authors_books.author_id
   
-> LEFT OUTER JOIN publishers
   
-> ON publishers.id = books.publisher_id;

--------------------------------------------

 

 

 

 

 

 

 

 

 id  select_type  table

 type

 possible_keys  key

 

 

1 SIMPLE

 books

 ALL

 NULL

 NULL

 

1  SIMPLE

 authors_books  ALL

 NULL

 NULL

 

1 SIMPLE

 authors

 eq_ref  PRIMARY

 PRIMARY

 

1 SIMPLE

 publishers

 eq_ref PRIMARY

 PRIMARY

 

 

 

 

 

 

 

4 rows in set (0.00 sec)

--------------------------------------------

Weíre mostly interested in thetypecolumn of the output. For the two first tables,books andauthors_books,ALLmeans that a full-table scan is done, which should always be at least a bit alarming. If we wanted this query to stay fast when more data is imported in the database, we should make the query use an index for theauthors_bookstable. We can do that easily with a migration:

$ script/generate migration add_indices
      exists  db/migrate
      create  db/migrate/014_add_indices.rb

Then edit the migration file to add needed indices:

class AddIndices < ActiveRecord::Migration
 
def self.up
    add_index :authors_books, :author_id
    add_index :authors_books, :book_id
 
end

  def self.down
    remove_index :authors_books, :author_id
    remove_index :authors_books, :book_id

  end
end

We added indices for both foreign key fields. Now letís run the migration:

$ rake db:migrate

If we now run theEXPLAIN query again, we can see that the type of the join is nowrefinstead ofALL, meaning that an index is being used to perform the join:

mysql> EXPLAIN SELECT COUNT(DISTINCT books.id)
    -> FROM books LEFT OUTER JOIN authors_books
    -> ON authors_books.book_id = books.id
    -> LEFT OUTER JOIN authors
    -> ON authors.id = authors_books.author_id
    -> LEFT OUTER JOIN publishers
    -> ON publishers.id = books.publisher_id;

--------------------------------------------

 

 

 

 

 

 

 

 

id  select_type table type  possible_keys  key

 

1  SIMPLE  books ALL NULL NULL

 1  SIMPLE authors_books  ref  authors_books_book_id_index  ➥

authors_books_book_id_index

 1  SIMPLE authors eq_ref PRIMARY ➥

PRIMARY

1 SIMPLE publishers eq_ref PRIMARY | ➥

PRIMARY

 


4 rows in set (0.01 sec)

 

 

 

 

 

 

--------------------------------------------

In thekeyfield, you can see that the index used isauthors_books_book_id_index, the index we just created with the migration.


Tip  For more information about usingEXPLAINin MySQL, seehttp://dev.mysql.com/doc/refman/ 5.0/en/explain.html. For details on the PostgreSQL version, seewww.postgresql.org/docs/8.1/ interactive/sql-explain.html.


As a rule of thumb, you should create indices for all foreign key fields, as well as for all columns you are using inWHEREclauses. A good example of such a column istags.name, which is used extensively by theacts_as_taggablecode to find tags in the database table.

Summary

In this chapter, we took a look at how to measure and optimize the performance of a Rails application. We introduced the Rails Analyzer set of profiling tools. Then we added different caching mechanisms to our application, for entire pages and content chunks that are either needed very often or that take expensive operations to be produced. Finally, we took a look at common performance problems in Rails applications and how to solve them.

Measuring and optimizing a web application are constant processes. It is not enough to make the application fast once and then lean back and enjoy the profits. User behavior changes over time, and the hit rates go up (hopefully), so it pays to follow closely how the site behaves.

Optimizing a Rails application is a topic that merits a book or three just for itself. We could only scratch the surface in this chapter. For a lot more information on this topic, follow Stefan Kaesís blog athttp://railsexpress.de/blog/, and look for his upcoming book on the topic, Performance Rails: Building Rails Applications with Sustainable Performance (ISBN: 0-32147-741-3). 


DISCLAIMER: The content provided in this article is not warranted or guaranteed by Developer Shed, Inc. The content provided is intended for entertainment and/or educational purposes in order to introduce to the reader key ideas, concepts, and/or product reviews. As such it is incumbent upon the reader to employ real-world tactics for security and implementation of best practices. We are not liable for any negative consequences that may result from implementing any information covered in our articles or tutorials. If this is a hardware review, it is not recommended to open and/or modify your hardware.

blog comments powered by Disqus
RUBY-ON-RAILS ARTICLES

- Ruby-on-Rails Faces Second Security Flaw in ...
- Ruby 2.0 Prepped for February 2013 Release
- Why LinkedIn Switched from Ruby on Rails
- Adding Style with Action Pack
- Handling HTML in Templates with Action Pack
- Filters, Controllers and Helpers in Action P...
- Action Pack and Controller Filters
- Action Pack Categories and Events
- Logging Out, Events and Templates with Actio...
- Action Pack Sessions and Architecture
- More on Action Pack Partial Templates
- Action Pack Partial Templates
- Displaying Error Messages with the Action Pa...
- Action Pack Request Parameters
- Creating an Action Pack Registration Form

Watch our Tech Videos 
Dev Articles Forums 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Contact Us 
Site Map 
Privacy Policy 
Support 

Developer Shed Affiliates

 




© 2003-2018 by Developer Shed. All rights reserved. DS Cluster - Follow our Sitemap
Popular Web Development Topics
All Web Development Tutorials