今回はincludesとかを使ってN+1問題を解決していきたいです。
さっさと解決しようかと思ったのですが、一筋縄じゃいかなかったので、今から頑張って解決しようと思います!
またこの方の記事を全面的に参考にして(パクって)記事を書いていこうと思います。
あとタイトルがクソすぎるので、いいタイトルあったら編集リクエスト待ってます。
今回のモデルのアソシエーション
drinkがいわゆる投稿です。
userはいろんな投稿をするので、
userとdrinkで1対多の関係
これだけだったら単純なのですが、
それらに加えて、imageがあります。
投稿画像とユーザー画像がそれぞれある感じ。
bulletが検知したN+1
user: root USE eager loading detected Drink => [:image_attachment] Add to your query: .includes([:image_attachment])
DrinkモデルがN+1問題を引き起こしてるっぽい。
解決していこう
drinks_controller
@pagy,@drinks = pagy(Drink.includes(image_attachment: :blob)
.where.not(user_id: 6)
.where("user_id IN (#{following_ids}) OR user_id = :user_id", user_id: @user.id)
.order('drinks.created_at DESC'))
んーややこしい。。。。
色々調べてみるとこーゆー感じで書き直したらいいんじゃないかと思った。
drinks_controller
@pagy,@drinks = pagy(Drink.includes(user: {image_attachment: :blob})
.where.not(user_id: 6)
.where("user_id IN (#{following_ids}) OR user_id = :user_id", user_id: @user.id)
.order('drinks.created_at DESC'))
したら今度は、
user: root USE eager loading detected Drink => [:image_attachment] Add to your query: .includes([:image_attachment])
とbulletに検知された。
userの方は解決できたっぽいけど、、、。
今度はimageの方でN+1が発生してる。。。。
この書き方でどうだ!!!(無策)
@pagy,@drinks = pagy(Drink.includes(:user ,{image_attachment: :blob})
.where.not(user_id: 6)
.where("user_id IN (#{following_ids}) OR user_id = :user_id", user_id: @user.id)
.order('drinks.created_at DESC'))
解決できた。
ログを実際に見てみる
Processing by DrinksController#index as HTML
web_1 | User Load (0.6ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 7 LIMIT 1
web_1 | ↳ app/helpers/sessions_helper.rb:52:in `current_user'
web_1 | (0.8ms) SELECT COUNT(*) FROM `drinks` WHERE `drinks`.`user_id` != 6 AND (user_id IN (SELECT followed_id FROM relationships WHERE follower_id = 7) OR user_id = 7)
web_1 | ↳ app/controllers/drinks_controller.rb:14:in `index'
web_1 | Rendering layout layouts/application.html.erb
web_1 | Rendering drinks/index.html.erb within layouts/application
web_1 | Drink Load (1.1ms) SELECT `drinks`.* FROM `drinks` WHERE `drinks`.`user_id` != 6 AND (user_id IN (SELECT followed_id FROM relationships WHERE follower_id = 7) OR user_id = 7) ORDER BY drinks.created_at DESC LIMIT 2 OFFSET 0
web_1 | ↳ app/views/drinks/_drinks_index.html.erb:12
web_1 | User Load (0.6ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 7
web_1 | ↳ app/views/drinks/_drinks_index.html.erb:12
web_1 | ActiveStorage::Attachment Load (0.7ms) SELECT `active_storage_attachments`.* FROM `active_storage_attachments` WHERE `active_storage_attachments`.`record_type` = 'Drink' AND `active_storage_attachments`.`name` = 'image' AND `active_storage_attachments`.`record_id` IN (8, 7)
web_1 | ↳ app/views/drinks/_drinks_index.html.erb:12
web_1 | ActiveStorage::Blob Load (0.8ms) SELECT `active_storage_blobs`.* FROM `active_storage_blobs` WHERE `active_storage_blobs`.`id` IN (12, 13)
web_1 | ↳ app/views/drinks/_drinks_index.html.erb:12
web_1 | ActiveStorage::Attachment Load (0.6ms) SELECT `active_storage_attachments`.* FROM `active_storage_attachments` WHERE `active_storage_attachments`.`record_id` = 7 AND `active_storage_attachments`.`record_type` = 'User' AND `active_storage_attachments`.`name` = 'image' LIMIT 1
web_1 | ↳ app/views/drinks/_drinks_index.html.erb:18
web_1 | Like Load (0.7ms) SELECT `likes`.* FROM `likes` WHERE `likes`.`user_id` = 7 AND `likes`.`drink_id` = 8 LIMIT 1
web_1 | ↳ app/views/likes/_like.html.erb:2
web_1 | (0.6ms) SELECT COUNT(*) FROM `likes` WHERE `likes`.`drink_id` = 8
web_1 | ↳ app/views/likes/_like.html.erb:6
web_1 | Rendered likes/_like.html.erb (Duration: 9.1ms | Allocations: 1639)
web_1 | Like Load (0.7ms) SELECT `likes`.* FROM `likes` WHERE `likes`.`user_id` = 7 AND `likes`.`drink_id` = 7 LIMIT 1
web_1 | ↳ app/views/likes/_like.html.erb:2
web_1 | (0.7ms) SELECT COUNT(*) FROM `likes` WHERE `likes`.`drink_id` = 7
web_1 | ↳ app/views/likes/_like.html.erb:6
web_1 | Rendered likes/_like.html.erb (Duration: 10.2ms | Allocations: 1558)
web_1 | Rendered drinks/_drinks_index.html.erb (Duration: 56.6ms | Allocations: 12908)
web_1 | Rendered drinks/index.html.erb within layouts/application (Duration: 57.9ms | Allocations: 12979)
web_1 | [Webpacker] Everything's up-to-date. Nothing to do
# なぜこの書き方で上手くいったのかの僕の予想
@drinks = pagy(Drink.includes(:user ,{image_attachment: :blob})
って書き方は、
drinkとアソシエーション組んでるuserとimageをどっちもincludesメソッドの引数に指定したから上手くいったのでしょう。
一方失敗した下のような書き方は
@drinks = pagy(Drink.includes(user: {image_attachment: :blob})
userモデルとアソシエーションを組んでるimegeをN+1問題を起こさないようにしてーー。
って意味のコードになってしまったのでしょう。。。。きっと。。。