Nov 18

Probably you already know about n+1 queries problem, read this post if you want to learn how to detect those kind of queries.

For example:

class User < ActiveRecord::Base
  has_many :items
end

class Item < ActiveRecord::Base
  belongs_to :user
end

If we have 2 users, and several items we have controller like this:

def index
  @users = User.find(:all)
end

and in the view we have something like this:

<% @users.each do |user| %>
    <%= user.items.collect(&:name) %>
<% end %>

If we watch the queries on the log, we should see something like this :

SELECT * FROM "users"
SELECT * FROM "items" WHERE ("items".user_id = 1)
SELECT * FROM "items" WHERE ("items".user_id = 2)

if we have 1000 items, that means we are doing 1000+1 queries!
Off course we can fix it like this (eager loading):

def index
  @users = User.find(:all, :include => :items)
end

And now we only have two queries.

Detecting it in our application is another matter. If we have huge project, detecting it can be hard work and time wasting. I found a cool plugin/gem that help detecting n+1 queries problem, called Bullet. You can install it as a gem:

sudo gem install flyerhzm-bullet --source http://gems.github.com

or as a plugin

script/plugin install git://github.com/flyerhzm/bullet.git

or you can build the gem right from the source from:

git clone git://github.com/flyerhzm/bullet.git
cd bullet
gem build bullet.gemspec
sudo gem install bullet --local

If you are using it as a gem, don’t forget to include this in your environment.rb:

config.gem 'flyerhzm-bullet', :lib => 'bullet', :source => 'http://gems.github.com'

include this in development.rb, and remember not to use bullet on production:

config.after_initialize do
  Bullet.enable = true  
  Bullet::Association.alert = true
  Bullet::Association.bullet_logger = true
  Bullet::Association.rails_logger = false
end

You want to make two profiles in your Firefox, one for regular browsing, and one without caching enabled since sometimes it is not working in regular profile. So disable caching on your browser if you want to see pop-up box if we are doing n+1 queries. Also Bullet detect eager loading that is not used. I attached a screenshot so you can see it in action:

Bullet in action

Leave a Reply

Security Code: