Improving Partial Loading Performance

Episode #145 by Teacher's Avatar David Kimura

Summary

N+1 queries can make an application slow. Having queries called within views can also slow down an app as well as making it less extendable.
rails performance database 11:38

Resources

Download Source Code

Summary

# Gemfile
group :development do
  ...
  gem 'bullet'
end

# config/initializers/bullet.rb
if defined?(Bullet)
  Bullet.enable = true
  # Bullet.sentry = true
  # Bullet.alert = true
  # Bullet.bullet_logger = true
  # Bullet.console = true
  # Bullet.growl = true
  # Bullet.xmpp = { :account => 'bullets_account@jabber.org',
  # :password => 'bullets_password_for_jabber',
  # :receiver => 'your_account@jabber.org',
  # :show_online_status => true }
  Bullet.rails_logger = true
  # Bullet.honeybadger = true
  # Bullet.bugsnag = true
  # Bullet.airbrake = true
  # Bullet.rollbar = true
  # Bullet.add_footer = true
  # Bullet.stacktrace_includes = [ 'your_gem', 'your_middleware' ]
  # Bullet.stacktrace_excludes = [ 'their_gem', 'their_middleware', ['my_file.rb', 'my_method'], ['my_file.rb', 16..20] ]
  # Bullet.slack = { webhook_url: 'http://some.slack.url', channel: '#default', username: 'notifier' }
end

# controllers/concerns/strict_queries.rb
module StrictQueries
  class SQLWithViewError < StandardError; end
  module Concern
    extend ActiveSupport::Concern

    included do
      def render(*args, &block)
        return super if production?

        callback = lambda do |name, start, finish, id, payload|
          if !should_ignore_sql_statement?(payload[:name])
            raise SQLWithViewError.new(message(payload[:sql]))
          end
        end

        ActiveSupport::Notifications.subscribed(callback, 'sql.active_record') do
          super
        end
      end

      private

      def production?
        Rails.env.production?
      end

      def should_ignore_sql_statement?(name)
        ['SCHEMA', 'ActionRecord::SchemaMigration Load'].include?(name)
      end

      def message(sql)
        "A SQL request was issued within the view:\n#{sql}"
      end
    end
  end
end

# application_controller.rb
class ApplicationController < ActionController::Base
  include StrictQueries::Concern
end

# movies_controller.rb
class MoviesController < ApplicationController
  before_action :set_movie, only: [:edit, :update, :destroy]

  def index
    @movies = Movie.all.load
  end

  def show
    @movie = Movie.includes(quotes: :user).find(params[:id])
  end
  ...
end