- Ruby / Rails関連
Rails 7.2: strict_loadingがn_plus_one_onlyモードで子の関連付けをeager loadingしないよう修正(翻訳)
Rails 7.2: strict_loadingがn_plus_one_onlyモードで子の関連付けをeager loadingしないよう修正(翻訳)
Railsのstrict_loading
モードは、関連付けがlazy loading(遅延読み込み)されるのを防ぐためのものです。
strict_loading
モードを利用すると、関連付けられるレコードはincludes
でeager loadingされなければならなくなります。さもないとActiveRecord::StrictLoadingViolationError
エラーが発生します。
このstrict_loading
モードは、特定の関連付けをeager loadingすることでN+1クエリ問題を特定および修正し、パフォーマンスのボトルネックを回避するうえで有用です。
class Client < ApplicationRecord
has_many :projects
end
Client.strict_loading.first.projects
# ActiveRecord::StrictLoadingViolationErrorが発生
class Client < ApplicationRecord
has_many :projects, strict_loading: true
end
Client.first.projects
# ActiveRecord::StrictLoadingViolationErrorが発生
Client.includes(:projects).first.projects
Client Load (0.7ms) SELECT "clients".* FROM "clients" ORDER BY "clients"."id" ASC LIMIT $1 [["LIMIT", 1]]
Project Load (2.4ms) SELECT "projects".* FROM "projects" WHERE "projects"."client_id" = $1 [["client_id", 1]]
# =>
[#<Project:0x00000011133aaa48
id: 1,
client_id: 1,
name: "Miru",
description: "Time tracking">,
#<Project:0x0000001111f150b0
id: 2,
client_id: 1,
name: "Azure.com",
description: "Cloud Computing">
]
🔗 改修前
:n_plus_one_only
モードを指定したstrict_loading
は、ネステッド関連付けの奥深くにアクセスしたときに発生する可能性のあるパフォーマンス低下問題に対処する目的で設計されています。
これにより、関連付けを深くトラバースすることを制限しつつ関連付けを直接読み込めるようになり、潜在的なN+1クエリ問題や順序の不一致に関連する予期せぬ自体を防げるようになります。
client = Client.find(1)
client.strict_loading!(mode: :n_plus_one_only)
client.projects.first
# SELECT "projects".* FROM "projects" WHERE "projects"."client_id" = $1 [["client_id", 1]] -- non-deterministic order
ただし、ネステッド関連付けを深くたどるとエラーになります。
client.projects.first.timesheets # ActiveRecord::StrictLoadingViolationErrorが発生
🔗 改修後
改修後は、:n_plus_one_only
モードを指定したstrict_loading
が、子の関連付けをeager loadingしないようになりました。
#48785の変更によって子の関連付けがeager loadingされなくなって意図通りに振る舞うようになり、first
やlast
などのメソッドが呼び出されたときに順序が一定しない問題も防止されます。first
やlast
自体はN+1問題を発生しないので、子の関連付けを呼び出してもエラーは発生しません。
つまり、関連付けはeager loadingされますが、子の関連付けはlazy loadingされるということです。
client = Client.find(1)
client.strict_loading!(mode: :n_plus_one_only)
client.projects.first
# SELECT "projects".* FROM "projects" WHERE "projects"."client_id" = $1 [["client_id", 1]] -- non-deterministic order
client.projects.first.timesheets # no longer raises error
🔗 まとめ
strict loadingを利用すると、関連付けられたレコードがeager loadingされていない場合はActiveRecord::StrictLoadingViolationError
が発生するので、関連付けがlazy loadingされることを防止できます。
ただしstrict_loading!(mode: :n_plus_one_only)
を利用すると、関連付けはeager loadingされ、子の関連付けはlazy loadingされます。
関連記事
Rails 7.2: ActiveRecord::Core#inspectの修正とattributes_for_inspectの便利な使い方(翻訳)
概要
元サイトの許諾を得て翻訳・公開いたします。
参考: 4.1.2.14
:strict_loading
-- Active Record の関連付け - Railsガイド日本語タイトルは内容に即したものにしました。
strict loading、eager loading、lazy loadingは原則として英ママとしました。