3 Easy Steps to optimize Queries in Rails using ‘Bullet’

3 Easy Steps to optimize Queries in Rails using ‘Bullet’

rorimage-123

First,

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

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 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

Example

/Gemfile.rb

  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.
After completing the above mentioned steps a JavaScript alert popup would appear with the detected N+1 query. The alert would display the file containing the issue as well as what could be done to overcome the problem.

The previous N+1 query can be fixed by following below mentioned steps:

In Controller,

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.

Benefits:

  • 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.
Tags:
,
Avatar
Srikant M. Mohapatra
anurag.pattnaik@andolasoft.com