18
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Ateam Finergy Inc.Advent Calendar 2020

Day 15

Rails6.1の新機能 Strict Loading について

Last updated at Posted at 2020-12-14

本稿は、Ateam Finergy Inc. Advent Calendar2020の15日目の記事です。
本稿では、Rails6.1で新たに追加されたstrict_loadingについて解説します。

はじめに

strict_loadingは一言で言うと、N+1の発生を予防するためにActive Recordに導入された機能です。
以下のようなassociationのmodelがある場合、実際にどのようにして使うのか見ていきます。

app/models/user.rb
class User < ApplicationRecord
  has_many :posts
end
app/models/post.rb
class Post < ApplicationRecord
  belongs_to :user
end

メソッドとして使う

strict_loadingメソッドを使って取得したrecordでassociationを呼び出すと、以下のようにActiveRecord::StrictLoadingViolationErrorが発生します。

user = User.strict_loading.first
user.posts
 => ActiveRecord::StrictLoadingViolationError (`Post` called on `User` is marked for strict_loading and cannot be lazily loaded.)

includesなどEager loadingを行うメソッドでpostsを指定しておくと、問題なく呼び出すことができます。

user = User.includes(:posts).strict_loading.first
user.posts
=> #<ActiveRecord::Associations::CollectionProxy [#<Post id: 1, user_id: 1, title: "post_1", created_at: "2020-12-06 16:08:23.688506000 +0000", updated_at: "2020-12-06 16:08:23.688506000 +0000">, #<Post id: 2, user_id: 1, title: "post_2", created_at: "2020-12-06 16:08:23.696069000 +0000", updated_at: "2020-12-06 16:08:23.696069000 +0000">]>

上記のようにEager loadingを行うことが必須となるため、N+1の発生を予防することができます。

associationに定義する

strict_loadingは、associationを定義する際にもオプションとして指定することができます。

app/models/user.rb
class User < ApplicationRecord
  has_many :posts, strict_loading: true
end

こうしておくと、record取得の際にstrict_loadingメソッドを使わなくてもActiveRecord::StrictLoadingViolationErrorが発生します。

user = User.first
user.posts
=> ActiveRecord::StrictLoadingViolationError (`posts` called on `User` is marked for strict_loading and cannot be lazily loaded.)

model単位で指定する

association定義1つずつに指定するのが面倒な場合は、model単位で全てのassociationをまとめて指定することもできます。

app/models/user.rb
class User < ApplicationRecord
  self.strict_loading_by_default = true

  has_many :posts
end
user = User.first
user.posts
=> ActiveRecord::StrictLoadingViolationError (`Post` called on `User` is marked for strict_loading and cannot be lazily loaded.)

ログに出力する

ActiveRecord::StrictLoadingViolationErrorのエラーをraiseせずに、ログ出力することもできます。

config.active_record.action_on_strict_loading_violation = :log

デフォルトではエラーをraiseしますが、このようにconfigで各環境ごとにログ出力を指定できます。

Active Storageで使う

Active Storageのassociationであるhas_one_attachedhas_many_attachedに指定することもできます。

app/models/post.rb
class Post < ApplicationRecord
  belongs_to :user

  has_one_attached :logo, strict_loading: true
  has_many_attached :images, strict_loading: true
end

終わりに

N+1は、Railsのパフォーマンスを向上させる上で避けては通れない問題だと思います。
Rails6.1から新たに導入されたこのstrict_loadingを上手く活用することでN+1の発生を予防して、より高パフォーマンスのRailsアプリケーションが多く構築されることを願っております。

参考

18
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?