Madogiwa Blog

主に技術系の学習メモに使っていきます。

Ruby on Rails: 6.1.0の新機能strict_loadingを使ってN+1を防ぐMEMO

Rails 6.1.0で導入されたstrict_loadingを使うと手軽にN+1のチェックが出来て便利そうなのでMEMO📝

基本的には以下のような形でActiveRecord::Releationを作るメソッドチェイン内でstrict_loadingを呼び出すようにすると、

  def index
    @feeds = Feed.strict_loading
                 .search(params.dig(:query, :keyword)).recent
                 .pager(page: params[:page], per: PER_PAGE)
    response = @feeds.map { |feed| ::Api::Feed.new(feed: feed).attributes }
    render json: response
  end

eager_loadingされてないと以下のようなエラーが発生します🚨

ActiveRecord::StrictLoadingViolationError (`Entry` called on `Feed` is marked for strict_loading and cannot be lazily loaded.):
  
app/models/api/feed.rb:5:in `initialize'
app/controllers/api/feeds_controller.rb:8:in `new'
app/controllers/api/feeds_controller.rb:8:in `block in index'
app/controllers/api/feeds_controller.rb:8:in `map'
app/controllers/api/feeds_controller.rb:8:in `index'

以下のようにpreloadを入れるとエラーが発生しなくなります✨

  def index
    @feeds = Feed.strict_loading.preload(:last_entry, :tags)
                 .search(params.dig(:query, :keyword)).recent
                 .pager(page: params[:page], per: PER_PAGE)
    response = @feeds.map { |feed| ::Api::Feed.new(feed: feed).attributes }
    render json: response
  end

Model単位でデフォルトを指定することもできるので、ApplicationRecordに設定を入れておいて、N+1を許容するときだけFeed.strict_loading(false)みたいな形で明示的にわかるようにするといい感じな気もしました👍

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
  self.strict_loading_by_default = true
end

今まではbulletでこういうのを検知していましたが、Rails標準でN+1を検知できる仕組みが出来たのは便利ですね✨

参考

qiita.com

Rails6.1で新しく入る機能について - Speaker Deck

github.com