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を検知できる仕組みが出来たのは便利ですね✨