Rails 6.1の新機能「strict_loading」でN+1を検知する

こんにちは。
GMOアドマーケティングの石丸(@thomi40)です。

昨年の12月にRuby on Rails 6.1の新機能の1つである「annotate_rendered_view_with_filenames」について紹介しました。

この記事は GMOアドマーケティング Advent Calendar 2020 8日目の記事です。はじめにこんにちは。GMOアドマーケティングの石丸(@thomi40)です。弊社では複数のプロダクトでRuby on Railsを使って開発を行っています。今回はRails 6.1で導入が予定されている「annotate_rendered_view_with_filenames」について紹介します。annotate_rendered_view_with_filenamesとは?annotate_rendered_view_with_filenames は config/environments/development.rb や config/environments/test.rb で設定できるオプションで、今後リリース予定のRuby...

今回は、同じくRuby on Rails 6.1で追加された「strict_loading」について紹介します。

strict_loadingとは?

strict_loadingはRails 6.1から実装された機能で、主に関連付け(Association)で発生する遅延読み込み(Lazy Loading)を防ぐ目的で追加されました。
この機能を活用することで、N+1問題を防ぐことが期待できます。

では実際に試してみましょう。

検証用の環境構築

検証のために簡易的なアプリケーションを作成します。
今回はDocker環境でRubyは2.7.3、Railsは6.1.3.2を指定しました。

アプリを立ち上げた後に、以下のコマンドで雛形を作成します。

モデルは以下の通りです。

N+1を発生させるためにサンプル用のデータをコンソールから作成します。

viewとcontrollerはscaffoldで作成されたものからほぼ変更はありませんが、viewに関してはN+1を発生させるために以下のように修正しています(以下のコード6行目部分)。

strict_loadingを試す

環境が整ったので、実際にstrict_loadingを試してみます。
strict_loadingを設定する方法はいくつかありますが、今回は関連付けのオプションとして設定し、N+1が発生する http://localhost:3000/users へアクセスしてみます。

※上記のキャプチャは執筆者が自らキャプチャしたものです。

eager loadingを行っていないため、 StrictLoadingViolationError が発生しました。
期待通りに動作していますね。

エラーを回避するために、controllerで @users = User.all となっていた部分を以下のように変更します。

改めて http://localhost:3000/users へアクセスすると正常にアクセスできました。

もしエラーではなくログに表示したい場合は環境に合わせてenvironmentsに以下を加えることで、ログとしてLazy Loadingを検知することも可能です。

今回の環境で検証すると以下のようなログが出力されました。

用途に合わせて検知方法をカスタマイズできるのは使いやすくていいですね。

まとめ

今回はLazy Loadingを検知する「strict_loading」について紹介しました。
N+1を検知する方法は他にもありますが、Railsに標準で実装されたのは心強いですね。
strict_loadingの使い方は他にもたくさんあるので、気になる方は以下の参考リンクにアクセスしてみて下さい。

参考リンク

Strict loading in Active Record and more | Riding Rails (参照: 2021年5月27日)

Add strict_loading mode to optionally prevent lazy loading by eileencodes · Pull Request #37400 · rails/rails (参照: 2021年5月27日)

Support strict_loading on association declarations by kddeisz · Pull Request #38541 · rails/rails (参照: 2021年5月27日)

Allow to enable/disable strict_loading mode by default for a model. by bogdanvlviv · Pull Request #39491 · rails/rails (参照: 2021年5月27日)