Bullet is an exciting plugin developed by Richard Huang for Ruby on Rails. It reduces the number of queries made by an application. It was first used in 2009 and since then, it has proved to be a powerful gem that helps monitor Ruby on Rails applications for performance improvements. Bullet seeks several ways to notify problems including JavaScript alerts, XMPP alerts and Growl notifications. Further, it saves its bullet.log statement to trace the exact root of the alert.
Here are some interesting tips to optimize Queries in rails using Bullet.
The traditional method (sans the optimization)
This is the old fashioned method of optimization which was initially used by bullet. This example shows two models; Order and Product, wherein an Order consists of many Products. The code script should be:
In app/controllers/orders_controller.rb
class OrdersController < ApplicationController
def index
@orders = Order.all
end
end
In app/views/orders/index.html.erb
<h1>Orders</h1>
<% @orders.each do |order| %>
<div>
<h2><%=link_to order.title, order_path(order)%></h2>
</div>
<%order.products.each do |product|%>
<ul class=”product”>
<li><%=link_to product.title, product_path(product)%></li>
</ul>
<%end%>
<% end %>
Source - http://blog.andolasoft.com/2013/05/3-easy-steps-to-optimize-queries-in-rails-using-bullet.html#
This script would however generate some N+1 query issues, the reason being that we have set the query just once in order to get the orders and then separate each query to fetch the products. These are also the kinds of problems that are quite frequently and easily overlooked by programmers. Also, this is where the “Bullet” gem helps in avoiding the issues.
The gem “Bullet” can be integrated to the query in just three easy steps.
Step 1:
Add “Bullet” get to the Gemfile
/Gemfile.rb
gem 'bullet', '4.6.0', :group => “development”
Run the “bundle install” to install the gem “bullet” in development group
Step 2:
Optimize the configuration setting in the development.rb file
For slowing the “bullet” to change its configuration using the after_initialize block contained in the development.rb file. Set the alert as “true” to get the alarms via the browser.
config.after_initialize do
Bullet.enable = true
Bullet.alert = true
Bullet.bullet_logger = true
Bullet.console = true
Bullet.rails_logger = true
end
Step 3:
Restarting the server
You need to both restart the server and reload the page. After step 2, you would see a JavaScript alert popup in the browser with the detected N+1 query. The alert would contain the file that holds the issue and the suggestions to what could be done to override the problem.
The N+1 query can be simultaneously fixed using the following steps:
In Controller,
lass OrdersController < ApplicationController
def index
@orders = Order.includes(:products)
end
end
After you have changed the statement from “Order all” to “Order.includes’ (:products), you need to call eager loading to fetch the products. The date herein would be fetched using two queries, one to obtain the orders and the other to retrieve the products in the orders.
The gem “bullet” can also point out when we are unnecessarily eager loading.
Benefits of optimization