3 Easy Steps to optimize Queries in Rails using ‘Bullet’
May 15, 2013
let me introduce you to the ‘Bullet’ gem
‘Bullet’ is a ruby gem which facilitates the developers by alerting when an application performs an inefficient database query, such as an N+1 query. It is one of the most efficient tool to optimize a Rails application.
Traditional Method (w/o optimization):
This example illustrates the old-fashioned method of optimizing a query.
For example there are two models, one is ‘Order’ and other is ‘Product’. And an order has many products. Then the code for order listing page will be
class OrdersController < ApplicationController def index @orders = Order.all end end
<h1>Orders</h1> <% @orders.each do |order| %> <div class="order"> <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 %>
These codes would generate N+1 query issues, because here we have queried just once to get the orders and then separate queries for each order to fetch its products. These sorts of problems can be easily overlooked during development.
‘Bullet’ gem comes in handy for avoiding such problems.
Optimized Method – integrating gem ‘Bullet’:
Let me explain in just 3 easy steps, how the gem ‘Bullet’ can be integrated to optimize the query,
Step#1 – Add the gem to the Gemfile
gem 'bullet', '4.6.0', :group => “development”
Run “bundle install” to install the bullet gem in the development group.
Step#2 – Configuration setting in development.rb file
To enable Bullet change its configuration with the after_initialize block in the development.rb file. Set alert as true to get alert popup through 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 – Restart the Server
Restart the server as well as reload the page.
The previous N+1 query can be fixed by following below mentioned steps:
lass OrdersController < ApplicationController def index @orders = Order.includes(:products) end end
After changing the statement from ‘Order.all’ to ‘Order.includes’(:products). We can fetch the products through eager loading. Now, if we reload the page we wouldn’t get any alert as we are fetching the efficiently. Here the data is fetched by only two queries, one to get the orders and the other to get the products in those orders.
‘Bullet’ can also tell us when we’re doing eager loading unnecessarily. Let’s say in the order listing page only order will be displayed. So, we removed the code that was displaying the list of products. Now after reloading the page we will get an alert popup displaying that Bullet has detected unused eager loading.
- No need to search the codes in each file to figure out the inefficient database query.
- Bullet can notify us, through an alert message, by writing in the console or in the log file.
- Prevent our application from performing an inefficient database query like an N+1 query.
- It can also detect unused eager loading.