© 2023 Wantedly, Inc.N+1 問題の解決とcomputed_modelOct. 5 2023 - Ryota Egusa (@gedorinku)
View Slide
Wantedly のアーキテクチャ© 2023 Wantedly, Inc.Rails のマイクロサービスGraphQL のデータソースなどに使われる、汎用的な API を持つ
加工した値を返すモデルのメソッドclass User < ApplicationRecordhas_many :work_experiences# => "ウォンテッドリー株式会社 / エンジニア"def position_descriptionwork_experience = work_experiences.max_by { _1.started_at }"#{work_experience.company_name} / #{work_experience.position}"endend© 2023 Wantedly, Inc.
Preload と N+1 問題class User < ApplicationRecordhas_many :work_experiences…endUser.where(...).map(&:position_description)User.preload(:work_experiences).where(...).map(&:position_description)© 2023 Wantedly, Inc.
computed_model による解決方法class Userdefine_primary_loader :raw_user do ... enddefine_loader :work_experiences do ... enddependency :work_experiencescomputed def position_descriptionwork_experience = work_experiences.max_by { _1.started_at }"#{work_experience.company_name} / #{work_experience.position}"endend© 2023 Wantedly, Inc.依存関係をここに書くここに書かれていないものを使うとエラーになる
Active Record をデータソースとして使う例class Userdefine_primary_loader :raw_user do |_, ids:, **|RawUser.where(id: ids).map do |raw_user|User.new(raw_user)endenddefine_loader :work_experiences do ... endend© 2023 Wantedly, Inc.
他サービスの API をデータソースとして使う例class Userdefine_primary_loader :raw_user do ... enddefine_loader :work_experiences, key: -> { id } do |user_ids, _, **|WorkExperienceApiClient.list(user_ids: user_ids).group_by(&:user_id)endend© 2023 Wantedly, Inc.
computed_model からデータを読む指定していないフィールドを使うとエラーになるusers = User.batch_get(user_ids, [:position_description])users.map(&:position_description)users.map(&:name) # => error© 2023 Wantedly, Inc.
まとめ1. 抽象化を損なわず依存関係解決○ N+1問題を防ぎ、必要なデータだけ読み込む2. データソースには Active Record に限らずHTTP API なども使える© 2023 Wantedly, Inc.https://github.com/wantedly/computed_model