Slide 1

Slide 1 text

Rails ΞϓϦͷ 
 5,000 ݅ͷ N+1 ໰୊ͱ 
 ઓ͍ͬͯΔ࿩ 2023.10.27. Kaigi on Rails 2023 @makicamel

Slide 2

Slide 2 text

͓࿳ͼ

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

ͳ͗౗ͤ 
 ·ͤΜͰͨ͠ 💧

Slide 5

Slide 5 text

͓Εͨͪͷઓ͍͸ 
 ͔͜͜Βͩ

Slide 6

Slide 6 text

Rails ΞϓϦͷ 
 5,000 ݅ͷ N+1 ໰୊ͱ 
 ઓ͍ͬͯΔ࿩

Slide 7

Slide 7 text

ࣗݾ঺հ w!NBLJDBNFM઒ݪສق w3VCZͱϏʔϧɹɹͱ͓ञ͕޷͖ w޷͖ͳ73ήʔϜ w

Slide 8

Slide 8 text

N+1 ໰୊ 
 ͋Γ·͔͢ʁ

Slide 9

Slide 9 text

N+1 ໰୊
 Կ݅͋Γ·͔͢ʁ

Slide 10

Slide 10 text

N+1 ໰୊ͱ͸ ͔ΜͨΜͳ 
 ͓͞Β͍ ͔ΜͨΜͳ 
 આ໌༻Ϟσϧ

Slide 11

Slide 11 text

plays = Play.limit(10) plays.each do |play| puts play.actors.map(&:name) end SELECT "plays".* FROM "plays" LIMIT 10 SELECT "actors".* FROM "actors" INNER JOIN "play_actors" ON "actors"."id" = "play_actors"."actor_id" WHERE "play_actors"."play_id" = 1 SELECT "actors".* FROM "actors" INNER JOIN "play_actors" ON "actors"."id" = "play_actors"."actor_id" WHERE "play_actors"."play_id" = 2 
 SELECT "actors".* FROM "actors" INNER JOIN "play_actors" ON "actors"."id" = "play_actors"."actor_id" WHERE "play_actors"."play_id" = 3 
 ... 10 + 1 ճͷΫΤϦ͕ൃߦ͞ΕΔ N+1 ໰୊ͱ͸

Slide 12

Slide 12 text

N+1 ໰୊ରࡦ •ActiveRecord::QueryMethods#preload •ActiveRecord::QueryMethods#eager_load •ActiveRecord::QueryMethods#includes Rails ฤ 🛤

Slide 13

Slide 13 text

ActiveRecord::QueryMethods#preload plays = Play.preload(:actors).limit(10) plays.each do |play| puts play.actors.map(&:name) end SELECT "plays".* FROM "plays" LIMIT 10 SELECT "play_actors".* FROM "play_actors" WHERE "play_actors"."play_id" IN (1, 2, 3, ...) SELECT "actors".* FROM "actors" WHERE "actors"."id" IN (1, 2, 3, ...) 3 ͭʹ෼͚ΒΕͨΫΤϦ͕ൃߦ͞ΕΔ

Slide 14

Slide 14 text

ActiveRecord::QueryMethods#eager_load plays = Play.eager_load(:actors).limit(10) plays.each do |play| puts play.actors.map(&:name) end SELECT DISTINCT "plays"."id" FROM "plays" LEFT OUTER JOIN "play_actors" ON "play_actors"."play_id" = "plays"."id" LEFT OUTER JOIN "actors" ON "actors"."id" = "play_actors"."actor_id" LIMIT 10 SELECT "plays"."id" AS t0_r0, ... FROM "plays" LEFT OUTER JOIN "play_actors" ON "play_actors"."play_id" = "plays"."id" LEFT OUTER JOIN "actors" ON "actors"."id" = "play_actors"."actor_id" WHERE "plays"."id" IN (1, 2, 3, ...) LEFT OUTER JOIN ͨ͠ 
 ΫΤϦ͕ൃߦ͞ΕΔ

Slide 15

Slide 15 text

ActiveRecord::QueryMethods#includes

Slide 16

Slide 16 text

ActiveRecord::QueryMethods#includes ActiveRecord::QueryMethods#includes
 https://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-includes
 ೔ຊޠ͸ൃදऀ༁ Specify associations args to be eager loaded to prevent N + 1 queries. 
 A separate query is performed for each association, 
 unless a join is required by conditions. N + 1 ΫΤϦΛ๷͙ͨΊʹ args associations Λ eager_load ͢ΔΑ͏ࢦఆ͠·͢ɻ 
 ৚݅ʹΑͬͯ join ͕ඞཁͰͳ͚Ε͹ɺ 
 ֤ association ͝ͱʹݸผͷΫΤϦ͕࣮ߦ͞Ε·͢ɻ

Slide 17

Slide 17 text

ActiveRecord::QueryMethods#includes ActiveRecordͷjoinsͱpreloadͱincludesͱeager_loadͷҧ͍ / @k0kubun
 https://qiita.com/k0kubun/items/80c5a5494f53bb88dc58 •includes ͨ͠ςʔϒϧͰ where ʹΑΔߜΓࠐΈΛߦ͍ͬͯΔ •includes ͨ͠ association ʹରͯ͠ joins ͔ references ΋ݺΜͰ͍Δ •೚ҙͷ association ʹରͯ͠ eager_load ΋ݺΜͰ͍Δ ͷ͏͍ͪͣΕ͔Λຬͨ͢৔߹ɺeager_load ͱಉ͡ڍಈ(LEFT JOIN)Λߦ͍ɺ 
 ͦ͏Ͱͳ͚Ε͹ preload ͱಉ͡ڍಈ(ΫΤϦΛ෼͚࣮ͯߦ)Λ͢Δɻ 
 ߜΓࠐΈ͕ඞཁͳ࣌ʹྫ֎Λ౤͛ͣ eager_load ʹfallback͢Δ preloadɻ

Slide 18

Slide 18 text

N+1 ໰୊ରࡦ gem ฤ 💎

Slide 19

Slide 19 text

Bullet 🔫

Slide 20

Slide 20 text

fl yerhzm / bullet
 https://github.com/ fl yerhzm/bullet Bullet 🔫 ɹN+1 ΍ෆཁͳ eager_loadingͳͲΛ 
 ɹϩά΍ը໘ʹදࣔͯ͠ڭ͑ͯ͘ΕΔ

Slide 21

Slide 21 text

# config/environments/test.rb Rails.application.configure do config.after_initialize do Bullet.enable = true Bullet.raise = true end end Bullet 🔫 ɹraise Λࢦఆ͢ΔͱΤϥʔΛى͜͢

Slide 22

Slide 22 text

N+1 ໰୊
 Կ݅͋Γ·͔͢ʁ ʢ࠶ܝʣ

Slide 23

Slide 23 text

※ unused eager_loading ͱ counter cache ͷ਺΋ؚΈ·͢

Slide 24

Slide 24 text

4722 tests failed

Slide 25

Slide 25 text

ߕຊҰ / Ϛογϡϧ-MASHLE-
 https://mashle.pw

Slide 26

Slide 26 text

ͭͬͨ͘ 🩹 makicamel / bulletmark_repairer
 https://github.com/makicamel/bulletmark_repairer

Slide 27

Slide 27 text

ͭͬͨ͘ 🩹 makicamel / bulletmark_repairer
 https://github.com/makicamel/bulletmark_repairer

Slide 28

Slide 28 text

BulletmarkRepairer 🩹 class PlaysController < ApplicationController def index @plays = Play.all end end <% @plays.each do |play| %> <% play.actors.each do |actor| %> <%= actor.name %> <% end %> <% end %> N+1 ൃੜ

Slide 29

Slide 29 text

BulletmarkRepairer 🩹 class PlaysController < ApplicationController def index @plays = Play.all end end <% @plays.each do |play| %> <% play.actors.each do |actor| %> <%= actor.name %> <% end %> <% end %> N+1 ൃੜ $ REPAIR=1 rspec

Slide 30

Slide 30 text

BulletmarkRepairer 🩹 class PlaysController < ApplicationController def index @plays = Play.all.includes([:actors]) end end <% @plays.each do |play| %> <% play.actors.each do |actor| %> <%= actor.name %> <% end %> <% end %> ɹ࣮ߦ࣌ʹ autocorrect Ͱ 
 ɹincludes Λ௥Ճ͢Δ $ REPAIR=1 rspec

Slide 31

Slide 31 text

BulletmarkRepairer ͷ 
 ͭ͘Γํ 🛠

Slide 32

Slide 32 text

ྩ࿨ͷ࣌୅ʹ 
 खͰ includes ॻ͘ͷ 
 ໘౗͍͘͞

Slide 33

Slide 33 text

Bullet ͕ N+1 Λݕ஌ͨ͠Β 
 includes Λఴ෇ͨ͠Β 
 ͍͍Μ͡Όͳ͍ʁ

Slide 34

Slide 34 text

•Ͳ͜Ͱ includes ͢Δ΂͖͔ •ͳʹΛ includes ͢Δ΂͖͔ ໰୊

Slide 35

Slide 35 text

•Ͳ͜Ͱ includes ͢Δ΂͖͔ •ͳʹΛ includes ͢Δ΂͖͔ ໰୊

Slide 36

Slide 36 text

Case 1. class PlaysController < ApplicationController def index @plays = Play.all end end <% @plays.each do |play| %> <% play.actors.each do |actor| %> <%= actor.name %> <% end %> <% end %> ɹϏϡʔͰ N+1 ͕ 
 ɹൃੜ͢Δέʔε

Slide 37

Slide 37 text

Case 1. class PlaysController < ApplicationController def index @plays = Play.all end end <% @plays.includes(:actors).each do |play| %> <% play.actors.each do |actor| %> <%= actor.name %> <% end %> <% end %> ɹϏϡʔͰ 
 ɹincludes ͨ͘͠ͳ͍ 🙅

Slide 38

Slide 38 text

Case 1. class PlaysController < ApplicationController def index @plays = Play.all.includes(:actors) end end <% @plays.each do |play| %> <% play.actors.each do |actor| %> <%= actor.name %> <% end %> <% end %> ɹίϯτϩʔϥͰ 
 ɹincludes ͍ͨ͠ 🙆

Slide 39

Slide 39 text

Case 2. class PlaysController < ApplicationController def index plays = Play.all.map do |play| { title: play.name, actors: play.actors.map(&:name) } end render json: { data: plays } end end ɹίϯτϩʔϥͰ 
 ɹൃੜ͢Δέʔε

Slide 40

Slide 40 text

Case 2. class PlaysController < ApplicationController def index plays = Play.all.includes(:actors).map do |play| { title: play.name, actors: play.actors.map(&:name) } end render json: { data: plays } end end ɹίϯτϩʔϥͰ 
 ɹ௚͢ͱΑͦ͞͏ 🙆

Slide 41

Slide 41 text

Case 3. class PlaysController < ApplicationController before_action :set_plays def index render json: { data: @plays.as_json } end private def set_plays @plays = Play.all end end ɹίʔϧόοΫͰ 
 ɹActiveRecord Λ 
 ɹ૊ΈཱͯΔέʔε

Slide 42

Slide 42 text

Case 3. class PlaysController < ApplicationController before_action :set_plays def index render json: { data: @plays.as_json } end private def set_plays @plays = Play.all.includes(:actors) end end ɹ͜ͷ৔߹͸ 
 ɹίʔϧόοΫͰ௚ͯ͠ 
 ɹΑͦ͞͏ 🙆

Slide 43

Slide 43 text

Case 4. class PlaysController < ApplicationController def index render json: { data: scope.as_json } end def show render json: { data: scope.find(params[:id]).as_json } end private def scope Play.where('name like ?', params[:name]) end end ɹڞ௨ͷείʔϓΛ 
 ɹ࢖͍ͬͯΔ৔߹

Slide 44

Slide 44 text

Case 4. class PlaysController < ApplicationController def index render json: { data: scope.as_json } end def show render json: { data: scope.find(params[:id]).as_json } end private def scope Play.where('name like ?', params[:name]) end end ɹΞΫγϣϯͰ 
 ɹ௚͢΂͖…ʁ🤔 ɹ͜ͷ͘Β͍ͳΒ 
 ɹείʔϓͰ௚ͯ͠΋ 
 ɹΑͦ͞͏͚ͩͲ…🤔 ɹshow Ͱ includes ͢Δ 
 ɹඞཁͳ͘ͳ͍ʁ🤔

Slide 45

Slide 45 text

Case 5. ɹίϯτϩʔϥ͔Β 
 ɹݺͼग़ͨ͠ 
 ɹϞσϧͷϝιουͰ 
 ɹN+1 ͕ൃੜ͢Δέʔε class PlaysController < ApplicationController def index plays = Play.all render json: { data: plays.map(&:as_json_with_actors) } end end class Play < ApplicationRecord def as_json_with_actors as_json.merge(actors: actors.map(&:name)) end end

Slide 46

Slide 46 text

Case 5. class PlaysController < ApplicationController def index plays = Play.all.includes(:actors) render json: { data: plays.map(&:as_json_with_actors) } end end class Play < ApplicationRecord def as_json_with_actors as_json.merge(actors: actors.map(&:name)) end end ɹίϯτϩʔϥͰ 
 ɹ௚͢ͱΑͦ͞͏ 🙆

Slide 47

Slide 47 text

Case 6. ɹϞσϧͷίʔϧόοΫͰ 
 ɹΫΤϦ͠௚্ͨ͠Ͱ 
 ɹൃੜ͍ͯ͠Δέʔε class Play < ApplicationRecord after_save :notify_actors, if: :notification_required? private def notify_actors actors.where(...).each(&:notify!) end end

Slide 48

Slide 48 text

Case 6. class Play < ApplicationRecord after_save :notify_actors, if: :notification_required? private def notify_actors actors.where(...).includes(:actors).each(&:notify!) end end ɹϞσϧͰ 
 ɹ௚͢ͱΑͦ͞͏ʁ

Slide 49

Slide 49 text

Case 7. class TransitionPastPlaysService def initialize(border_date) @border_date = border_date end def execute plays = Play.where(created_at: ..@border_date) play_actors = plays.map do |play| { play_id: play.id, actor_id: play.actors.map(&:id) } end PastPlay.insert_all plays.map(&:as_json) PastPlayActor.insert_all play_actors plays.destroy_all end end ɹαʔϏε಺Ͱ 
 ɹPlay.where ্ͨ͠Ͱ 
 ɹൃੜ͢Δέʔε ※ ຊདྷ map(&:id) Ͱ͸ͳ͘ pluck(:id) ΍ ids Λ࢖͑͹ N+1 ͷӨڹ͸খ͘͞ͳΓ·͢

Slide 50

Slide 50 text

Case 7. class TransitionPastPlaysService def initialize(border_date) @border_date = border_date end def execute plays = Play.where(created_at: ..@border_date).includes(:actors) play_actors = plays.map do |play| { play_id: play.id, actor_id: play.actors.map(&:id) } end PastPlay.insert_all plays.map(&:as_json) PastPlayActor.insert_all play_actors plays.destroy_all end end ※ ຊདྷ map(&:id) Ͱ͸ͳ͘ pluck(:id) ΍ ids Λ࢖͑͹ N+1 ͷӨڹ͸খ͘͞ͳΓ·͢ ɹαʔϏε಺Ͱ 
 ɹ௚͔͢͠ͳ͍

Slide 51

Slide 51 text

ۚాҰগ೥ͷࣄ݅฽֎఻ ൜ਓͨͪͷࣄ݅฽
 ఱथ੐ؙݪҊ ધ௡ਈฏݪҊ ͞ͱ͏;Έ΍ݪҊ ۚ੒ཅࡾ࿠ອը ߟ 
 ͑ 
 Δ ߟ 
 ͑ 
 Δ

Slide 52

Slide 52 text

•ίϯτϩʔϥͰ includes ͢Δͷ͸ਖ਼ͦ͠͏ •ෳ਺ͷΞΫγϣϯͰڞ௨είʔϓΛར༻͍ͯ͠Δ৔߹͸ʁ •ϏϡʔͰ includes ͢Δͷ͸޷·͘͠ͳͦ͞͏ •ϞσϧͷΠϯελϯεϝιουΑΓ΋ίϯτϩʔϥͰ includes͍ͨ͠ •ϝιου಺ͰΫΤϦ͠௚͍ͯ͠Δ৔߹͸ʁ •αʔϏεΫϥε etc … Ͳ͜Ͱ includes ͢Δ΂͖͔

Slide 53

Slide 53 text

Ϝζ͍ 😇

Slide 54

Slide 54 text

;ͩΜ 
 Ͳ͏ͯ͠Δ͚ͬʁ

Slide 55

Slide 55 text

•جຊతʹίϯτϩʔϥͰ includes ͢Δ •ϓϥΠϕʔτϝιουΑΓΞΫγϣϯͰ includes ͢Δ͜ͱ͕ଟ͍ •ActiveRecord ͷ૊ΈཱͯॳظͰ includes ͢Δ͜ͱ͕ଟ͍ •΍ΉΛಘͳ͚Ε͹ϞσϧɾαʔϏεΫϥε etc Ͱ includes ͢Δ ;ͩΜͷࢥߟճ࿏

Slide 56

Slide 56 text

;ͩΜͷࢥߟճ࿏

Slide 57

Slide 57 text

;ͩΜͷࢥߟճ࿏

Slide 58

Slide 58 text

Ϗϡʔฤ <% @plays.each do |play| %> <% play.actors.each do |actor| %> <%= actor.name %> <% end %> <% end %> class PlaysController < ApplicationController def index @plays = Play.all end end

Slide 59

Slide 59 text

Ϗϡʔฤ <% @plays.each do |play| %> <% play.actors.each do |actor| %> <%= actor.name %> <% end %> <% end %> class PlaysController < ApplicationController def index @plays = Play.all end end ɹBullet ͷϩά 2023-10-23 07:21:04[WARN] user: makicamel GET /plays USE eager loading detected Play => [:actors] Add to your query: .includes([:actors]) Call stack /Users/makicamel/awesome_app/app/views/plays/ 
 index.html.erb:2:in `block in _app_views_plays 
 _index_html_erb__3206924200111145907_11500' /Users/makicamel/awesome_app/app/views/plays/ 
 index.html.erb:1:in `_app_views_plays 
 _index_html_erb__3206924200111145907_11500’

Slide 60

Slide 60 text

Ϗϡʔฤ <% @plays.each do |play| %> <% play.actors.each do |actor| %> <%= actor.name %> <% end %> <% end %> class PlaysController < ApplicationController def index @plays = Play.all end end 2023-10-23 07:21:04[WARN] user: makicamel GET /plays USE eager loading detected Play => [:actors] Add to your query: .includes([:actors]) Call stack /Users/makicamel/awesome_app/app/views/plays/ 
 index.html.erb:2:in `block in _app_views_plays 
 _index_html_erb__3206924200111145907_11500' /Users/makicamel/awesome_app/app/views/plays/ 
 index.html.erb:1:in `_app_views_plays 
 _index_html_erb__3206924200111145907_11500’ ɹελοΫτϨʔεʹ 
 ɹίϯτϩʔϥ͕ͳ͍

Slide 61

Slide 61 text

Ϗϡʔฤ <% @plays.each do |play| %> <% play.actors.each do |actor| %> <%= actor.name %> <% end %> <% end %> class PlaysController < ApplicationController def index @plays = Play.all end end 2023-10-23 07:21:04[WARN] user: makicamel GET /plays USE eager loading detected Play => [:actors] Add to your query: .includes([:actors]) Call stack /Users/makicamel/awesome_app/app/views/plays/ 
 index.html.erb:2:in `block in _app_views_plays 
 _index_html_erb__3206924200111145907_11500' /Users/makicamel/awesome_app/app/views/plays/ 
 index.html.erb:1:in `_app_views_plays 
 _index_html_erb__3206924200111145907_11500’ ɹमਖ਼͍ͨ͠ϑΝΠϧ͸ 
 ɹͬͪ͜ ɹελοΫτϨʔεʹ 
 ɹίϯτϩʔϥ͕ͳ͍

Slide 62

Slide 62 text

Ϗϡʔฤ <% @plays.each do |play| %> <% play.actors.each do |actor| %> <%= actor.name %> <% end %> <% end %> class PlaysController < ApplicationController def index @plays = Play.all end end 2023-10-23 07:21:04[WARN] user: makicamel GET /plays USE eager loading detected Play => [:actors] Add to your query: .includes([:actors]) Call stack /Users/makicamel/awesome_app/app/views/plays/ 
 index.html.erb:2:in `block in _app_views_plays 
 _index_html_erb__3206924200111145907_11500' /Users/makicamel/awesome_app/app/views/plays/ 
 index.html.erb:1:in `_app_views_plays 
 _index_html_erb__3206924200111145907_11500’ ɹϏϡʔͰ N+1 Λൃੜͤ͞Δ஋͸ 
 ɹଟ͘ͷ৔߹Πϯελϯεม਺ʹೖ͍ͬͯΔ

Slide 63

Slide 63 text

Ϗϡʔฤ <% @plays.each do |play| %> <% play.actors.each do |actor| %> <%= actor.name %> <% end %> <% end %> class PlaysController < ApplicationController def index @plays = Play.all end end 2023-10-23 07:21:04[WARN] user: makicamel GET /plays USE eager loading detected Play => [:actors] Add to your query: .includes([:actors]) Call stack /Users/makicamel/awesome_app/app/views/plays/ 
 index.html.erb:2:in `block in _app_views_plays 
 _index_html_erb__3206924200111145907_11500' /Users/makicamel/awesome_app/app/views/plays/ 
 index.html.erb:1:in `_app_views_plays 
 _index_html_erb__3206924200111145907_11500’ ɹN+1 ൃੜߦ͸Θ͔Δ

Slide 64

Slide 64 text

Ϗϡʔฤ <% @plays.each do |play| %> <% play.actors.each do |actor| %> <%= actor.name %> <% end %> <% end %> class PlaysController < ApplicationController def index @plays = Play.all end end 2023-10-23 07:21:04[WARN] user: makicamel GET /plays USE eager loading detected Play => [:actors] Add to your query: .includes([:actors]) Call stack /Users/makicamel/awesome_app/app/views/plays/ 
 index.html.erb:2:in `block in _app_views_plays 
 _index_html_erb__3206924200111145907_11500' /Users/makicamel/awesome_app/app/views/plays/ 
 index.html.erb:1:in `_app_views_plays 
 _index_html_erb__3206924200111145907_11500’ ɹର৅ͬΆ͍ 
 ɹΠϯελϯεม਺͕Θ͔Δʢצʣ

Slide 65

Slide 65 text

Ϗϡʔฤ <% @plays.each do |play| %> <% play.actors.each do |actor| %> <%= actor.name %> <% end %> <% end %> class PlaysController < ApplicationController def index @plays = Play.all end end 2023-10-23 07:21:04[WARN] user: makicamel GET /plays USE eager loading detected Play => [:actors] Add to your query: .includes([:actors]) Call stack /Users/makicamel/awesome_app/app/views/plays/ 
 index.html.erb:2:in `block in _app_views_plays 
 _index_html_erb__3206924200111145907_11500' /Users/makicamel/awesome_app/app/views/plays/ 
 index.html.erb:1:in `_app_views_plays 
 _index_html_erb__3206924200111145907_11500’ ɹURL ͕Θ͔Δ

Slide 66

Slide 66 text

Ϗϡʔฤ <% @plays.each do |play| %> <% play.actors.each do |actor| %> <%= actor.name %> <% end %> <% end %> class PlaysController < ApplicationController def index @plays = Play.all end end 2023-10-23 07:21:04[WARN] user: makicamel GET /plays USE eager loading detected Play => [:actors] Add to your query: .includes([:actors]) Call stack /Users/makicamel/awesome_app/app/views/plays/ 
 index.html.erb:2:in `block in _app_views_plays 
 _index_html_erb__3206924200111145907_11500' /Users/makicamel/awesome_app/app/views/plays/ 
 index.html.erb:1:in `_app_views_plays 
 _index_html_erb__3206924200111145907_11500’ ɹίϯτϩʔϥͱ 
 ɹΞΫγϣϯ͕Θ͔Δ

Slide 67

Slide 67 text

Ϗϡʔฤ <% @plays.each do |play| %> <% play.actors.each do |actor| %> <%= actor.name %> <% end %> <% end %> class PlaysController < ApplicationController def index @plays = Play.all end end 2023-10-23 07:21:04[WARN] user: makicamel GET /plays USE eager loading detected Play => [:actors] Add to your query: .includes([:actors]) Call stack /Users/makicamel/awesome_app/app/views/plays/ 
 index.html.erb:2:in `block in _app_views_plays 
 _index_html_erb__3206924200111145907_11500' /Users/makicamel/awesome_app/app/views/plays/ 
 index.html.erb:1:in `_app_views_plays 
 _index_html_erb__3206924200111145907_11500’ ɹΠϯελϯεม਺ʹ 
 ɹ୅ೖ͢Δͱ͜Ζʹ

Slide 68

Slide 68 text

Ϗϡʔฤ <% @plays.each do |play| %> <% play.actors.each do |actor| %> <%= actor.name %> <% end %> <% end %> class PlaysController < ApplicationController def index @plays = Play.all.includes(:actors) end end 2023-10-23 07:21:04[WARN] user: makicamel GET /plays USE eager loading detected Play => [:actors] Add to your query: .includes([:actors]) Call stack /Users/makicamel/awesome_app/app/views/plays/ 
 index.html.erb:2:in `block in _app_views_plays 
 _index_html_erb__3206924200111145907_11500' /Users/makicamel/awesome_app/app/views/plays/ 
 index.html.erb:1:in `_app_views_plays 
 _index_html_erb__3206924200111145907_11500’ ɹincludes Λ଍͢✌

Slide 69

Slide 69 text

Ϗϡʔฤ <% @plays.each do |play| %> <% play.actors.each do |actor| %> <% if @favorite %> <%= actor.name %> <% end %> <% end %> <% end %> ର৅ͷΠϯελϯεม਺͸ 
 צͰ୳͍ͯ͠ΔͷͰ 
 ͜Μͳίʔυͩͱಈ͔ͳ͍ N+1 ൃੜՕॴ Πϯελϯεม਺Ά͍ 
 จࣈྻͦͷ 1ʢμ΢τʣ Πϯελϯεม਺Ά͍ 
 จࣈྻͦͷ 2ʢຊ෺ʣ

Slide 70

Slide 70 text

Ϗϡʔฤ <% Play.all.each do |play| %> <% play.actors.each do |actor| %> <%= actor.name %> <% end %> <% end %> Πϯελϯεม਺Λ 
 લఏͱ͍ͯ͠ΔͷͰ 
 ͜Μͳίʔυ΋ಈ͔ͳ͍

Slide 71

Slide 71 text

Ϗϡʔฤ

Slide 72

Slide 72 text

;ͩΜͷࢥߟճ࿏

Slide 73

Slide 73 text

ίϯτϩʔϥฤ •͍͔ͭ͘ύλʔϯ͕͋Γͦ͏

Slide 74

Slide 74 text

ίϯτϩʔϥฤ class PlaysController < ApplicationController def index plays = Play.all.map do |play| { title: play.name, actors: play.actors.map(&:name) } end render json: { data: plays } end end ɹ͜͜Ͱ includes ͢Δͱ 
 ɹΑͦ͞͏

Slide 75

Slide 75 text

ίϯτϩʔϥฤ class PlaysController < ApplicationController def index @plays = scope if params[:future] @plays = @plays.where(performed_at: Date.today..) end gon.as_json = @plays.as_json end def show @play = scope.find(params[:id]) end private def scope Actor.find(params[:actor_id]).plays end end ɹ͜͜Ͱ includes ͢Δͱ 
 ɹΑͦ͞͏

Slide 76

Slide 76 text

ίϯτϩʔϥฤ class PlaysController < ApplicationController before_action :set_plays def index render json: { data: @plays.as_json } end private def set_plays @plays = Play.all end end ɹ͜͜Ͱ includes ͢Δͱ 
 ɹΑͦ͞͏ʁ

Slide 77

Slide 77 text

ίϯτϩʔϥฤ

Slide 78

Slide 78 text

ίϯτϩʔϥฤ ɹ͍ͬͨΜ 
 ɹޙճ͠

Slide 79

Slide 79 text

ίϯτϩʔϥฤ ɹϩά͔ΒURL ͕Θ͔ΔͷͰ 
 ɹίϯτϩʔϥͱ 
 ɹΞΫγϣϯ͕Θ͔Δ

Slide 80

Slide 80 text

ίϯτϩʔϥฤ class PlaysController < ApplicationController def index @plays = scope if params[:future] @plays = @plays.where(performed_at: Date.today..) end gon.as_json = @plays.as_json end def show @play = scope.find(params[:id]) end private def scope Actor.find(params[:actor_id]).plays end end ɹͻͱͭͷϝιου಺Ͱ 
 ɹෳ਺ճ୅ೖ͞Ε͍ͯΔ৔߹ 
 ɹͲͪΒ͕ਖ਼͍͔͠ 
 ɹػձతͳ൑ఆ͸ແཧ

Slide 81

Slide 81 text

ίϯτϩʔϥฤ class PlaysController < ApplicationController def index @plays = scope if params[:future] @plays = @plays.where(performed_at: Date.today..) end gon.as_json = @plays.as_json end def show @play = scope.find(params[:id]) end private def scope Actor.find(params[:actor_id]).plays end end ɹ࠷ॳʹ୅ೖ͢Δ΄͏͕ 
 ɹϢʔεέʔεͱͯ͠ଟͦ͏

Slide 82

Slide 82 text

ίϯτϩʔϥฤ ɹ࠶ؼతʹϝιουΛνΣοΫ 
 ɹݟ͔ͭΒͳ͚Ε͹ 
 ɹίʔϧόοΫΛνΣοΫ

Slide 83

Slide 83 text

ର৅ͷΠϯελϯεม਺͸ 
 Ϗϡʔಉ༷ 
 צͰ୳ͯ͠·͢ ίϯτϩʔϥฤ

Slide 84

Slide 84 text

;ͩΜͷࢥߟճ࿏

Slide 85

Slide 85 text

ͦͷଞฤ ɹBullet ͷϩά͔Β 
 ɹൃੜߦ͕Θ͔ΔͷͰ 
 ɹͦ͜Ͱमਖ਼

Slide 86

Slide 86 text

ίϯτϩʔϥฤ ɹ͜ͷέʔε΋ 
 ɹճऩ

Slide 87

Slide 87 text

•Ͳ͜Ͱ includes ͢Δ΂͖͔ ✓ •ͳʹΛ includes ͢Δ΂͖͔ ໰୊

Slide 88

Slide 88 text

•Ͳ͜Ͱ includes ͢Δ΂͖͔ ✓ •ͳʹΛ includes ͢Δ΂͖͔ ໰୊

Slide 89

Slide 89 text

<% @plays.each do |play| %> <% play.actors.each do |actor| %> <%= actor.name %> <% end %> <% end %> <% @plays.each do |play| %> <% play.actors.each do |actor| %> <%= actor.company.name %> <% end %> <% end %> ɹ͜Μͳײ͡ͷϏϡʔ͕ 
 ɹ͋Δͱ͢Δ

Slide 90

Slide 90 text

Case 1. 2023-10-23 07:21:04[WARN] user: makicamel GET /plays USE eager loading detected Play => [:actors] Add to your query: .includes([:actors]) Call stack /Users/makicamel/awesome_app/app/views/plays/index.html.erb:2:in `block in _app_views_plays_index_html_erb__3206924200111145907_11500' /Users/makicamel/awesome_app/app/views/plays/index.html.erb:1:in `_app_views_plays_index_html_erb__3206924200111145907_11500' class PlaysController < ApplicationController def index @plays = Play.all end end ɹجຊέʔε

Slide 91

Slide 91 text

2023-10-23 07:21:04[WARN] user: makicamel GET /plays USE eager loading detected Play => [:actors] Add to your query: .includes([:actors]) Call stack /Users/makicamel/awesome_app/app/views/plays/index.html.erb:2:in `block in _app_views_plays_index_html_erb__3206924200111145907_11500' /Users/makicamel/awesome_app/app/views/plays/index.html.erb:1:in `_app_views_plays_index_html_erb__3206924200111145907_11500' class PlaysController < ApplicationController def index @plays = Play.all.includes(:actors) end end Case 1. ɹجຊέʔε

Slide 92

Slide 92 text

Case 2. class PlaysController < ApplicationController def index @plays = Play.all end end ɹassociations ͕ 
 ɹෳ਺͋Δέʔε 2023-10-23 07:21:04[WARN] user: makicamel GET /plays USE eager loading detected Play => [:actors] Add to your query: .includes([:actors]) 2023-10-23 07:21:04[WARN] user: makicamel GET /plays USE eager loading detected Play => [:theaters] Add to your query: .includes([:theaters])

Slide 93

Slide 93 text

class PlaysController < ApplicationController def index @plays = Play.all.includes([:actors, :theaters]) end end Case 2. ɹassociations ͕ 
 ɹෳ਺͋Δέʔε 2023-10-23 07:21:04[WARN] user: makicamel GET /plays USE eager loading detected Play => [:actors] Add to your query: .includes([:actors]) 2023-10-23 07:21:04[WARN] user: makicamel GET /plays USE eager loading detected Play => [:theaters] Add to your query: .includes([:theaters])

Slide 94

Slide 94 text

Case 3. 2023-10-26 14:37:36[WARN] user: makicamel GET /plays USE eager loading detected Play => [{:actors=>[:company]}] Add to your query: .includes([:"{:actors=>[:company]}"]) Call stack /Users/makicamel/awesome_app/app/views/plays/index.html.erb:2:in `block in _app_views_plays_index_html_erb__1665884924136242781_11760' /Users/makicamel/awesome_app/app/views/plays/index.html.erb:1:in `_app_plays_index_html_erb__1665884924136242781_11760' class PlaysController < ApplicationController def index @plays = Play.all end end ɹωετ͍ͯ͠Δέʔε

Slide 95

Slide 95 text

Case 3. 2023-10-26 14:37:36[WARN] user: makicamel GET /plays USE eager loading detected Play => [{:actors=>[:company]}] Add to your query: .includes([:"{:actors=>[:company]}"]) Call stack /Users/makicamel/awesome_app/app/views/plays/index.html.erb:2:in `block in _app_views_plays_index_html_erb__1665884924136242781_11760' /Users/makicamel/awesome_app/app/views/plays/index.html.erb:1:in `_app_plays_index_html_erb__1665884924136242781_11760' class PlaysController < ApplicationController def index @plays = Play.all.includes([{:actors=>[:company]}]) end end ɹωετ͍ͯ͠Δέʔε

Slide 96

Slide 96 text

Case 4. 2023-10-26 14:57:19[WARN] user: makicamel GET /plays USE eager loading detected Play => [:awesome_actors] Add to your query: .includes([:awesome_actors]) Call stack /Users/makicamel/awesome_app/app/views/plays/index.html.erb:2:in `block in _app_views_plays_html_erb___4265071383862059390_11760’ /Users/makicamel/awesome_app/app/views/plays/index.html.erb:1:in `_app_views_plays_index_html_erb___4265071383862059390_11760' class PlaysController < ApplicationController def index @plays = Play.all end end ɹωετ͍ͯ͠Δ͔ͭ 
 ɹassociation ʹผ໊͕͋Δέʔε class Play < ApplicationRecord has_many :play_actors has_many :awesome_actors, through: :play_actors, source: :actor end

Slide 97

Slide 97 text

class PlaysController < ApplicationController def index @plays = Play.all.includes({:awesome_actors=>[:company]}) end end Case 4. 2023-10-26 14:57:19[WARN] user: makicamel GET /plays USE eager loading detected Play => [:awesome_actors] Add to your query: .includes([:awesome_actors]) Call stack /Users/makicamel/awesome_app/app/views/plays/index.html.erb:2:in `block in _app_views_plays_html_erb___4265071383862059390_11760’ /Users/makicamel/awesome_app/app/views/plays/index.html.erb:1:in `_app_views_plays_index_html_erb___4265071383862059390_11760' ɹωετ͍ͯ͠Δ͔ͭ 
 ɹassociation ʹผ໊͕͋Δέʔε class Play < ApplicationRecord has_many :play_actors has_many :awesome_actors, through: :play_actors, source: :actor end

Slide 98

Slide 98 text

Case 5. 2023-10-26 14:46:46[WARN] user: makicamel GET /plays USE eager loading detected Actor => [:company] Add to your query: .includes([:company]) Call stack /Users/makicamel/awesome_app/app/views/plays/index.html.erb:3:in `block (2 levels) in _app_views_plays_index_html_erb__2119670124553133993_11760' /Users/makicamel/awesome_app/app/views/plays/index.html.erb:2:in `block in _app_views_plays_index_html_erb__2119670124553133993_11760' /Users/makicamel/awesome_app/app/views/plays/index.html.erb:1:in `_app_views_plays_index_html_erb__2119670124553133993_11760' class PlaysController < ApplicationController def index @plays = Play.includes(:actors) end end ɹҰ෦ includes ࡁͷέʔε

Slide 99

Slide 99 text

Case 5. 2023-10-26 14:46:46[WARN] user: makicamel GET /plays USE eager loading detected Actor => [:company] Add to your query: .includes([:company]) Call stack /Users/makicamel/awesome_app/app/views/plays/index.html.erb:3:in `block (2 levels) in _app_views_plays_index_html_erb__2119670124553133993_11760' /Users/makicamel/awesome_app/app/views/plays/index.html.erb:2:in `block in _app_views_plays_index_html_erb__2119670124553133993_11760' /Users/makicamel/awesome_app/app/views/plays/index.html.erb:1:in `_app_views_plays_index_html_erb__2119670124553133993_11760' class PlaysController < ApplicationController def index @plays = Play.includes(:actors).includes({:actors=>[:company]}) end end ɹҰ෦ includes ࡁͷέʔε

Slide 100

Slide 100 text

AssociationsBuilder

Slide 101

Slide 101 text

AssociationsBuilder module BulletmarkRepairer class AssociationsBuilder def build(marker) return if marker.skip? if associations[marker.index] associations[marker.index].add(marker) else associations[marker.index] = Associations.new( marker, @application_associations, @loaded_associations ) end end end end ɹBullet ͷܯࠂΛந৅Խͨ͠ 
 ɹΠϯελϯε

Slide 102

Slide 102 text

AssociationsBuilder module BulletmarkRepairer class AssociationsBuilder def build(marker) return if marker.skip? if associations[marker.index] associations[marker.index].add(marker) else associations[marker.index] = Associations.new( marker, @application_associations, @loaded_associations ) end end end end ɹN+1 ൃੜՕॴΛΩʔʹͯ͠ 
 ɹ ɹ

Slide 103

Slide 103 text

AssociationsBuilder module BulletmarkRepairer class AssociationsBuilder def build(marker) return if marker.skip? if associations[marker.index] associations[marker.index].add(marker) else associations[marker.index] = Associations.new( marker, @application_associations, @loaded_associations ) end end end end ɹN+1 ൃੜՕॴΛΩʔʹͯ͠ 
 ɹassociation Λ૊ΈཱͯΔ

Slide 104

Slide 104 text

AssociationsBuilder module BulletmarkRepairer class AssociationsBuilder def build(marker) return if marker.skip? if associations[marker.index] associations[marker.index].add(marker) else associations[marker.index] = Associations.new( marker, @application_associations, @loaded_associations ) end end end end 2023-10-23 07:21:04[WARN] user: makicamel GET /plays USE eager loading detected Play => [:actors] Add to your query: .includes([:actors]) 2023-10-23 07:21:04[WARN] user: makicamel GET /plays USE eager loading detected Play => [:theaters] Add to your query: .includes([:theaters]) ɹෳ਺ͷܯࠂΛ 
 ɹͻͱͭʹ·ͱΊ͍ͨ 
 ɹέʔε͕͋Δ

Slide 105

Slide 105 text

AssociationsBuilder •γϯϘϧΛϋογϡʹͨ͠Γ 
 [:actor]ɹˠɹ[{ actor: [:company] }] •ϋογϡͷόϦϡʔΛ഑ྻʹม׵ͯ͠஋Λ௥Ճͨ͠Γ 
 [{ actors: :company }, :staffs] 
 ɹˠɹ[{ actors: [:company, :works] }, :staffs] •഑ྻͷ஋ͷͻͱͭͱ௥Ճ͍ͨ͠஋Λϋογϡʹม׵ͨ͠Γ 
 [{ actors: :works }]ɹˠɹ[{ actors: { works: [:staffs] } }] ͳΜ͔΋͍ͯ͠Δ ɹෳ਺ͷܯࠂΛ 
 ɹͻͱͭʹ·ͱΊ͍ͨ 
 ɹέʔε͕͋Δ

Slide 106

Slide 106 text

LoadedAssociations

Slide 107

Slide 107 text

2023-10-26 14:46:46[WARN] user: makicamel GET /plays USE eager loading detected Actor => [:company] Add to your query: .includes([:company]) Call stack /Users/makicamel/awesome_app/app/views/plays/index.html.erb:3:in `block (2 levels) in _app_views_plays_index_html_erb__2119670124553133993_11760' /Users/makicamel/awesome_app/app/views/plays/index.html.erb:2:in `block in _app_views_plays_index_html_erb__2119670124553133993_11760' /Users/makicamel/awesome_app/app/views/plays/index.html.erb:1:in `_app_views_plays_index_html_erb__2119670124553133993_11760' class PlaysController < ApplicationController def index @plays = Play.includes(:actors) end end ɹҰ෦ includes ࡁͷέʔε LoadedAssociations

Slide 108

Slide 108 text

2023-10-26 14:46:46[WARN] user: makicamel GET /plays USE eager loading detected Actor => [:company] Add to your query: .includes([:company]) Call stack /Users/makicamel/awesome_app/app/views/plays/index.html.erb:3:in `block (2 levels) in _app_views_plays_index_html_erb__2119670124553133993_11760' /Users/makicamel/awesome_app/app/views/plays/index.html.erb:2:in `block in _app_views_plays_index_html_erb__2119670124553133993_11760' /Users/makicamel/awesome_app/app/views/plays/index.html.erb:1:in `_app_views_plays_index_html_erb__2119670124553133993_11760' class PlaysController < ApplicationController def index @plays = Play.includes(:actors).includes({:actors=>[:company]}) end end ɹҰ෦ includes ࡁͷέʔε LoadedAssociations

Slide 109

Slide 109 text

2023-10-26 14:46:46[WARN] user: makicamel GET /plays USE eager loading detected Actor => [:company] Add to your query: .includes([:company]) Call stack /Users/makicamel/awesome_app/app/views/plays/index.html.erb:3:in `block (2 levels) in _app_views_plays_index_html_erb__2119670124553133993_11760' /Users/makicamel/awesome_app/app/views/plays/index.html.erb:2:in `block in _app_views_plays_index_html_erb__2119670124553133993_11760' /Users/makicamel/awesome_app/app/views/plays/index.html.erb:1:in `_app_views_plays_index_html_erb__2119670124553133993_11760' class PlaysController < ApplicationController def index @plays = Play.includes(:actors).includes({:actors=>[:company]}) end end ɹBullet ͷܯࠂ͔Β͸ 
 ɹ@plays ͷϕʔεͷΫϥε͕ 
 ɹԿ͔Θ͔Βͳ͍ LoadedAssociations

Slide 110

Slide 110 text

2023-10-26 14:46:46[WARN] user: makicamel GET /plays USE eager loading detected Actor => [:company] Add to your query: .includes([:company]) Call stack /Users/makicamel/awesome_app/app/views/plays/index.html.erb:3:in `block (2 levels) in _app_views_plays_index_html_erb__2119670124553133993_11760' /Users/makicamel/awesome_app/app/views/plays/index.html.erb:2:in `block in _app_views_plays_index_html_erb__2119670124553133993_11760' /Users/makicamel/awesome_app/app/views/plays/index.html.erb:1:in `_app_views_plays_index_html_erb__2119670124553133993_11760' ɹBullet ͷܯࠂ͔Β͸ͲͪΒ͕ 
 ɹਖ਼͍͔͠Θ͔Βͳ͍ LoadedAssociations class PlaysController < ApplicationController def index @plays = Play.includes(:actors).includes({:actors=>[:company]}) end end .includes([:company])

Slide 111

Slide 111 text

LoadedAssociations module BulletmarkRepairer module ActiveRecord module QueryMethod def includes(*args) Thread.current[:bulletmark_repaier_loaded_associations][model.name][:includes].add(args) super(args) end def eager_load(*args) Thread.current[:bulletmark_repaier_loaded_associations][model.name][:eager_load].add(args) super(args) end def preload(*args) Thread.current[:bulletmark_repaier_loaded_associations][model.name][:preload].add(args) super(args) end end end end ɹಡΈࠐΈࡁͷ associations Λ 
 ɹϝϞ͓ͯ͘͠

Slide 112

Slide 112 text

LoadedAssociations ɹassociation Λ૊ΈཱͯΔ࣌ʹ 
 ɹࢀর͢Δ class Associations def initialize(marker, application_associations, loaded_associations) @marker = marker @application_associations = application_associations @loaded_associations = loaded_associations key = @loaded_associations.key(marker.base_class) @associations = { base: key ? { key => marker.associations } : marker.associations } end end

Slide 113

Slide 113 text

2023-10-26 14:46:46[WARN] user: makicamel GET /plays USE eager loading detected Actor => [:company] Add to your query: .includes([:company]) Call stack /Users/makicamel/awesome_app/app/views/plays/index.html.erb:3:in `block (2 levels) in _app_views_plays_index_html_erb__2119670124553133993_11760' /Users/makicamel/awesome_app/app/views/plays/index.html.erb:2:in `block in _app_views_plays_index_html_erb__2119670124553133993_11760' /Users/makicamel/awesome_app/app/views/plays/index.html.erb:1:in `_app_views_plays_index_html_erb__2119670124553133993_11760' ɹactors ͸ includes ࡁͳͷͰ 
 ɹωετ͢Δͷ͕ਖ਼͍͠ 💡 LoadedAssociations class PlaysController < ApplicationController def index @plays = Play.includes(:actors).includes({:actors=>[:company]}) end end .includes([:company])

Slide 114

Slide 114 text

•Ͳ͜Ͱ includes ͢Δ΂͖͔ ✓ •ͳʹΛ includes ͢Δ΂͖͔ ✓ ͋ͱ͸ Parer ʹ͓೚ͤ

Slide 115

Slide 115 text

Slide 116

Slide 116 text

ͨͿΜΈͳ͞Μ͕ؾʹͳ͍ͬͯΔ͜ͱ

Slide 117

Slide 117 text

includes vs. preload or eager_load

Slide 118

Slide 118 text

includes vs. preload or eager_load

Slide 119

Slide 119 text

ൃߦ͞ΕΔΫΤϦ͕มΘΔ৚݅͸ࣗ໌ͳͷʹ 
 Ͳ͏ͯ͠࢖͍෼͚͍ͨΜ͚ͩͬʁ includes vs. preload or eager_load

Slide 120

Slide 120 text

•preload ͕޷·͍͠έʔε͕ଟ͍ •eager_load ͸ LEFT OUTER JOIN ͳͷͰϝϞϦΛ৯͍΍͍͢ •preload ͷ৔߹ IN ۟ʹೖΔ id ͷ਺͕ଟ͘ʢe.g. ਺ສ݅ʣͳΔͱ 
 ύϑΥʔϚϯε͕ஶ͘͠௿Լ͢Δ͜ͱ͕͋Δ •preload or eager_load Ҏ֎ͷํ๏΋͋Δ preload vs. eager_load ઈରղ͸ͳ͍

Slide 121

Slide 121 text

•includes ͩͱύϥϝʔλ΍ঢ়ଶʹΑΓ preload or eager_load ͕ 
 ࢖͍෼͚ΒΕΔ͜ͱ͕͋Δ •ϦΫΤετʹΑΓൃߦ SQL ͕มΘΔͱ෼ੳͮ͠Β͍ includes vs. preload or eager_load ෼ੳɾӡ༻໘Ͱ includes ΑΓ preload or eager_load ͕޷·͍͠

Slide 122

Slide 122 text

ϝϞϦѹഭ͕৺഑ͳΜͰ͚͢Ͳ... N+1 → LEFT JOIN ͰϝϞϦ͕໰୊ʹͳΔͳΒ 
 ଟ͘ͷ৔߹ N+1 ͷ࣌఺Ͱ 
 ໰୊ʹͳ͍ͬͯΔͱࢥ͍·͢Α includes vs. preload or eager_load @tricknotes

Slide 123

Slide 123 text

N+1 < includes < preload or eager_load (or ͦͷଞ) ͷ࢖͍෼͚ includes vs. preload or eager_load

Slide 124

Slide 124 text

ݱ࣮ੈքͷ 
 N+1 ໰୊ͱͷ޲͖߹͍ํ

Slide 125

Slide 125 text

•๩͍͠

Slide 126

Slide 126 text

•ΘΕΘΕ͸๩͍͠ •΍Γ͍ͨ͜ͱΛ͢΂ͯͰ͖ΔΘ͚Ͱ͸ͳ͍ N+1 ໰୊ͱͷ޲͖߹͍ํ

Slide 127

Slide 127 text

•޲͖߹͑Δ࣌ʹ͸޲͖߹͑Δ •৽͘͠ίʔυΛॻ࣌͘ •طଘͷίʔυΛ֦ு͢Δ࣌ •N+1 ͕໰୊ʹͳ͍ͬͯΔ࣌ N+1 ໰୊ͱͷ޲͖߹͍ํ

Slide 128

Slide 128 text

•޲͖߹͍ʹ͍࣌͘ •ࣗ෼ͷαʔϏεͰݩʑ͋ͬͨ N+1 ໰୊ •௨Γ͕͢Γʹݟ͚ͭͨ N+1 ໰୊ N+1 ໰୊ͱͷ޲͖߹͍ํ

Slide 129

Slide 129 text

•ͨ·ͨ·ݟ͚ͭͨطଘͷ N+1 ໰୊ •SQL ͷมߋ͸ϦεΩʔ •౔஍צ͕ͳ͍ͱεϧʔ͕ͪ͠ •େ͖ͳαʔϏεʹͳΔͱྡͷ౔஍ͷ͜ͱΛ஌Βͳ͔ͬͨΓ͢Δ N+1 ໰୊ͱͷ޲͖߹͍ํ

Slide 130

Slide 130 text

໨ͷલʹ͝Έ͕མͪͯͨΒ 
 र͏͡Όͳ͍Ͱ͔͢ @a_matsuda

Slide 131

Slide 131 text

ݱঢ়ͷANDPADͷվળ͍ͨ͠ͱ͜ΖΛ ざ ͬ͘ ば ΒΜʹʢANDPAD ࣾ಺։࠵ʣ 2020 ೥ 5 ݄ 22 ೔ @a_matsuda

Slide 132

Slide 132 text

•ͨ·ͨ·ݟ͚ͭͨ N+1 ΛͱΓ͋͑ͣ௚ͯ͠ PR Λ࡞Δਓ •PR ΛϤγ 👈 ͯ͠औΓࠐΉਓ N+1 ୀ࣏൪௕ BulletmarkRepairer ͸ 
 ͜ͷ෦෼ͷ 
 αϙʔτπʔϧ 🩹

Slide 133

Slide 133 text

•ͨ·ͨ·ݟ͚ͭͨ N+1 ΛͱΓ͋͑ͣ௚ͯ͠ PR Λ࡞Δਓ •PR ΛϤγ 👈 ͯ͠औΓࠐΉਓ •ൃߦ͞ΕΔ SQL ͷมԽͷ֬ೝ •࣮σʔλ্໰୊ͳͦ͞͏͔ͷ֬ೝ ͜ͷϓϩηεվળ΋ 
 ͔͚ͨͬͨ͠Ͳ 
 ؒʹ߹Θͳ͔ͬͨ ͜ͷϓϩηε΋ 
 վળ͍ͨ͠ 
 ʢؒʹ߹Θͳ͔ͬͨʣ N+1 ୀ࣏൪௕

Slide 134

Slide 134 text

N+1 ໰୊ͱͷ޲͖߹͍ํ

Slide 135

Slide 135 text

•શ෦ includes (preload or eager_load) ͢Ε͹ղܾ •Ͱ͸ͳ͍ N+1 ໰୊ͷղ๏

Slide 136

Slide 136 text

•AR#present? → AR#exists? / AR#map → AR#pluck N+1 ໰୊ͷղ๏ ద੾ͳϝιουΛ 
 ࢖͏

Slide 137

Slide 137 text

•e.g. Ұ෦ͷ৚݅࣌ͷΈಡΈࠐΜͩϨίʔυ͕ར༻͞ΕΔέʔε 
 → ෆཁͳΒಡΈࠐ·ͳ͍ •e.g. ϙϦϞʔϑΟοΫؔ࿈ͷ 
 ಛఆͷؔ࿈ͰͷΈϨίʔυ͕ར༻͞ΕΔέʔε 
 → ෆཁͳΒಡΈࠐ·ͳ͍ N+1 ໰୊ͷղ๏ ෆཁͳΒ 
 ಡΈࠐ·ͳ͍

Slide 138

Slide 138 text

•ղ๏͸༷ʑ •୯७Ͱୀ۶ͳ΋ͷ •೉ͯ͘͠಄ʹ׼ͯ͠ؤுΔ΋ͷ N+1 ໰୊ͱͷ޲͖߹͍ํ

Slide 139

Slide 139 text

N+1 ໰୊ͱͷ޲͖߹͍ํ Ruby ୀ۶ͳ͜ͱ͸Pythonʹ΍ΒͤΑ͏ɹϊϯϓϩάϥϚʔʹ΋Ͱ͖ΔࣗಈԽॲཧϓϩάϥϛϯά
 Al Sweigart ஶ ૬઒ Ѫࡾ ༁

Slide 140

Slide 140 text

•୯७Ͱୀ۶ͳ͜ͱ͸ Ruby ʹ΍ΒͤΑ͏ •೉ͯ͘͠໘ന͍໰୊͸ਓ͕ؒղ͜͏ N+1 ໰୊ͱͷ޲͖߹͍ํ

Slide 141

Slide 141 text

໘ന͍͜ͱΛ΍͍͖ͬͯ·͠ΐ͏

Slide 142

Slide 142 text

͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ Special Thanks @tricknotes @ydah @l15n @ken3ypa @pupupopo88