概要と対策手順
-
Active Storage
を使ってポートフォリオを作成 - 作成後のレビューで
Active Storage
でN+1
を確認する - 原因は
Active Storage
のvariant
だった - 現環境では対処できないのと発覚。
-
variant
をやめてCSS
での制御に変更し解決。
※ポートフォリオの詳細に関してはこちらに投稿してあります。
実行環境
- Ruby ( 2.7.2 )
- Ruby on Rails ( 6.1.4.1 )
- PostgreSQL
Active Storage と N+1
.rb
@articles = Article.published.includes(:tag_maps, :tags, :keeps,
user: { avatar_attachment: :blob }).order(created_at: :desc).page(params[:page]).per(PER_PAGE)
-
avatar
取得時のN+1
には下記を参考に対応しているつもりだった。
原因は variant だった
- ローカルに接続した時のログを眺めていると
ActiveStorage::VariantRecord Load
が記事の表示件数分弱表示されていた。 - アイコンの画像を場所ごとにサイズを分けて表示したかったので
application_helper.rb
に下記の処理をしていた。
application_helper.rb
def user_icon(user, size)
case size
when "mypage"
user.avatar.variant(resize: "72x72").processed
when "profile"
user.avatar.variant(resize: "64x64").processed
when "list"
user.avatar.variant(resize: "25x25").processed
end
end
このバリアント画像をN+1
しているらしい。
現環境では対処できない
- 自分で調べてみたが座礁したので、相談をし調査継続
-
週刊RailsウォッチさんとRuby on Rails APIに辿り着き、下記を見て対処方法と
variant
が原因のN+1
だと気づく。
現在のActive Storageではvariantトラッキングで添付ファイルごとにvariantが存在するかどうかをチェックするクエリが走る。通常のRailsのN+1防止策(includes)ではこれを防止できない。
このプルリクはwith_all_variant_recordsメソッドを追加し、かつincludesがActive Storageの添付ファイルで期待どおり動作するようになる。
- 解決の糸口は見えてきたので、try&error で挑戦するも一向に解決できず。
-
scope :with_all_variant_records, -> { includes(blob: :variant_records) }
という記述をみた際に先ほど参考にしたActive StorageのN+1問題に対処する内で出てきたscope :"with_attached_#{name}", -> { includes("#{name}_attachment": :blob) }
を参考にしたがずっとundefind methods with_all_variant_records
と表示される(今思うと感の良い人なら気付きそうなログですね)
-
- どうやら自分の使用している
Rails
のverではwith_all_variant_records
が実装されていないという事を教えてもらいました。(githubの英語訳を見てた時にRails7
に対応する的な事が書いてあったのはこの事かと後で合点がいく) - 確かに
github
の main にはscope :with_all_variant_records, -> { includes(blob: :variant_records) }
が記載され -
6.1.4.1の方には記載がなかった。(通りで
undefind methods
な訳ですね)
CSS制御に変更し
- 件の根本的な原因と現状は分かりましたが、現状の改善はできていません。
- 今回は
variant
をやめてcss
で調整する方向に転換しました。
変更手順
-
application_helper.rb
で使用しているvariant(resize: "サイズ").processed
の記述を削除 -
CSS
で画像調整する記述を用意
CSSでの画像調整
- 今回は
SCSS
を利用 -
html.erb
内でvariant
を使用していた部分のクラス名を変更 - 同じ記述が増えるので
@mixin
と@include
を使用する
.scss
@mixin icon_size($size: 25px) {
border-radius: 50%;
width: $size;
height: $size;
}
/* アイコンサイズ */
.list-user-icon {
@include icon_size;
}
.profile-user-icon {
@include icon_size(64px);
}
.mypage-user-icon {
@include icon_size(72px);
}
スッキリ!!
解決
これでログを見てもActiveStorage::VariantRecord Load
が減りました。
無事解決!!
今回の件で解決方法を正規ルートに拘りすぎず、違う形でも解決できる方法も一緒に考えることができるようになる力も必要だなと感じました。