0
0

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.

ActiveStorage と variant と それから N+1

Last updated at Posted at 2021-09-16

概要と対策手順

  1. Active Storageを使ってポートフォリオを作成
  2. 作成後のレビューでActive StorageN+1を確認する
  3. 原因はActive Storagevariantだった
  4. 現環境では対処できないのと発覚。
  5. variantをやめてCSSでの制御に変更し解決。

※ポートフォリオの詳細に関してはこちらに投稿してあります。

実行環境

  • Ruby ( 2.7.2 )
  • Ruby on Rails ( 6.1.4.1 )
  • PostgreSQL

Active Storage と N+1

  • 対象になったのはこちら(記事の投稿者を示すユーザーアイコン)
    スクリーンショット 2021-09-16 10.56.09.png
  • 当時のコントローラーの処理は下記
.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には下記を参考に対応しているつもりだった。

参考: Active Storageの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に対応する的な事が書いてあったのはこの事かと後で合点がいく)
  • 確かに githubmain には 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 が減りました。
無事解決!!
今回の件で解決方法を正規ルートに拘りすぎず、違う形でも解決できる方法も一緒に考えることができるようになる力も必要だなと感じました。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?