Boost Your Ruby on Rails Application Performance with Bullet Gem

railsMarch 13, 2024Dotby Alkesh Ghorpade

Introduction

In the fast-paced world of web development, optimizing application performance is crucial for delivering exceptional user experiences. As your Ruby on Rails project grows in complexity, identifying and rectifying potential performance bottlenecks becomes increasingly challenging. Fortunately, there's a powerful tool at your disposal – the Bullet gem.

What is the Bullet Gem?

The Bullet gem is a sophisticated tool designed to help Ruby on Rails developers optimize their application's database queries. Bullet analyzes your application's queries in real time and provides valuable feedback to identify N + 1 query issues, unused eager loading, and other performance-related issues. By detecting and resolving these inefficiencies, Bullet empowers developers to build faster, more efficient Rails applications.

Getting Started with Bullet

Let's explore how you can integrate Bullet into your Ruby on Rails project.

Installation

Begin by adding the Bullet gem to your Gemfile and running bundle install to install the gem.

gem 'bullet', group: 'development'

You need to enable the bullet gem by executing:

bundle exec rails g bullet:install

Using the generate command will automatically generate the default configuration file. After generation, you may be prompted to confirm that this configuration is included in your test environment.

demo git:(main) ✗ bundle exec rails g bullet:install

 (called from <main> at /Users/apple/opensource/demo/config/environment.rb:5)
Enabled bullet in config/environments/development.rb
Would you like to enable bullet in test environment? (y/n) n

Note: You need to ensure bullet gem is added after activerecord (rails) and mongoid.

Configuration

After installing the gem, configure Bullet in your Rails application's development environment. Open the config/environments/development.rb file and add the following configuration:

# config/environments/development.rb

Rails.application.configure do
  # Other configurations...

  config.after_initialize do
    Bullet.enable = true
    Bullet.alert = true
    # Customize other Bullet configurations as needed
  end
end

The Bullet gem offers a range of notification options to help you stay informed about potential performance issues in your Rails application. When addressed effectively, these notifications provide valuable insights into inefficient database queries and can significantly improve your application's performance. Let's explore the various types of Bullet gem notifications:

  • Console Notifications:

    Bullet gem notifications are displayed by default in the Rails console during application runtime. These notifications include alerts about N + 1 query problems, unused eager loading, and other potential performance bottlenecks. Console notifications provide real-time feedback to developers, allowing them to address issues promptly during the development and testing phases.

  • Browser Popup Notifications:

    In addition to console notifications, Bullet gem can also trigger browser popup notifications when performance issues are detected. This feature is handy for developers who prefer visual feedback or need to address performance problems immediately while working on their applications.

    Bullet.alert will pop up a JavaScript alert in your browser.

  • Logging Notifications:

    Bullet gem can log performance-related notifications to the Rails log file, providing a comprehensive record of detected issues for future reference. Logging notifications enable developers to review performance trends over time, track the effectiveness of optimization efforts, and identify recurring problems in their applications.

    Bullet.bullet_logger will log the data to the bullet log file: Rails.root/log/bullet.log. Bullet.console will log warnings to your browser's console.log. Bullet.rails_logger will add warnings directly to the Rails log.

  • Third-Party Notifications:

    In the configuration settings, you can configure if Bullet can notify the third-party services you use by setting the keys to true or false. For example, if you want to send a notification to your Slack channel, you need to configure the Bullet.slack as below:

    # config/environments/development.rb
    
    Rails.application.configure do
      # Other configurations...
    
      config.after_initialize do
        Bullet.enable = true
        Bullet.alert = true
        Bullet.slack = {
          webhook_url: 'http://your.slack.url',
          channel: '#bullet_alerts',
          username: 'admin'
        }
      end
    end

    You need to set the keys to true or false to send notifications to Bugsnag, AirBrake, AppSignal, HoneyBadger, and Sentry.

    # config/environments/development.rb
    
    Rails.application.configure do
      config.after_initialize do
        Bullet.enable = true
        Bullet.alert = true
        Bullet.airbrake = true
        Bullet.honeybadger = true
        Bullet.bugsnag = false
      end
    end

In the Bullet gem, you can turn off specific detectors, allowing you to tailor the performance analysis according to your application's requirements. Disabling detectors can be helpful if certain types of notifications are irrelevant to your application or if you prefer to focus on specific areas of optimization.

# Each of these settings defaults to true

Bullet.n_plus_one_query_enable     = false

Bullet.unused_eager_loading_enable = false

Bullet.counter_cache_enable        = false

The safe list feature allows you to whitelist specific associations or classes, ignoring them when Bullet checks for potential N + 1 query issues or unused eager loading. This can be useful when you have associations that you know are intentionally loaded separately and don't indicate performance problems.

Bullet.add_safelist type: :n_plus_one_query, class_name: "User", association: :posts
Bullet.add_safelist type: :unused_eager_loading, class_name: "Category", association: :articles

If you want to skip bullet for a controller actions, you can add :skip_bullet callback around the actions.

class ApplicationsController < ActionController::Base
  around_action :skip_bullet, if: -> { defined?(Bullet) }

  def skip_bullet
    previous_value = Bullet.enable?
    Bullet.enable = false
    yield
  ensure
    Bullet.enable = previous_value
  end
end

By setting up a safe list in Bullet gem, you can customize the performance analysis to exclude associations or classes you know are intentionally loaded separately or not indicative of performance problems.

Key Features and Benefits

  • N + 1 Query Detection:

    One of the most common performance pitfalls in Rails applications is the N + 1 query problem, where multiple individual queries are executed to retrieve associated records instead of utilizing eager loading. Bullet automatically detects N + 1 query occurrences and alerts developers, enabling them to optimize database queries and improve performance.

  • Eager Loading Suggestions:

    Bullet provides actionable suggestions for optimizing eager loading in your Rails application. By identifying unused eager loading associations, developers can fine-tune their queries to load only the necessary data, reducing database load and enhancing application responsiveness.

  • ActiveRecord Optimization:

    Bullet offers insights into ActiveRecord usage patterns and suggests optimizations to minimize database interactions. Bullet helps developers streamline their ActiveRecord queries for optimal performance, from inefficient scopes to unnecessary database calls.

  • Integration with ORM and ODM:

    While initially designed for ActiveRecord, Bullet has expanded its support to include other object-relational mapping (ORM) and object-document mapping (ODM) libraries such as Mongoid. Whether you're working with traditional SQL databases or NoSQL solutions, Bullet adapts to your ORM/ODM of choice to provide comprehensive performance analysis.

  • Customizable Configuration:

    Bullet offers various configuration options to suit your application's specific needs. From enabling/disabling alerts to customizing verbosity levels, developers can tailor Bullet's behaviour to align with their performance optimization objectives.

Conclusion

The Bullet gem empowers Ruby on Rails developers to optimize their applications for maximum performance and scalability. Bullet streamlines the performance optimisation process by detecting N + 1 query issues, suggesting eager loading optimizations, and providing actionable insights into ActiveRecord usage, enabling developers to deliver lightning-fast web experiences. Integrating Bullet into your Rails project is a game-changer for achieving optimal performance and scalability, whether you're building a small-scale web application or a large-scale enterprise solution. Start harnessing the power of Bullet today and unlock the full potential of your Ruby on Rails applications.

Closing Remark

Could your team use some help with topics like this and others covered by ShakaCode's blog and open source? We specialize in optimizing Rails applications, especially those with advanced JavaScript frontends, like React. We can also help you optimize your CI processes with lower costs and faster, more reliable tests. Scraping web data and lowering infrastructure costs are two other areas of specialization. Feel free to reach out to ShakaCode's CEO, Justin Gordon, at justin@shakacode.com or schedule an appointment to discuss how ShakaCode can help your project!
Are you looking for a software development partner who can
develop modern, high-performance web apps and sites?
See what we've doneArrow right