$30 off During Our Annual Pro Sale. View Details »

N+1 問題の解決と computed_model

N+1 問題の解決と computed_model

gedorinku

October 05, 2023
Tweet

More Decks by gedorinku

Other Decks in Programming

Transcript

  1. © 2023 Wantedly, Inc.
    N+1 問題の解決と
    computed_model
    Oct. 5 2023 - Ryota Egusa (@gedorinku)

    View Slide

  2. Wantedly のアーキテクチャ
    © 2023 Wantedly, Inc.
    Rails のマイクロサービス
    GraphQL のデータソースなどに使われる、汎用
    的な API を持つ

    View Slide

  3. 加工した値を返すモデルのメソッド
    class User < ApplicationRecord
    has_many :work_experiences
    # => "ウォンテッドリー株式会社 / エンジニア"
    def position_description
    work_experience = work_experiences.max_by { _1.started_at }
    "#{work_experience.company_name} / #{work_experience.position}"
    end
    end
    © 2023 Wantedly, Inc.

    View Slide

  4. Preload と N+1 問題
    class User < ApplicationRecord
    has_many :work_experiences

    end
    User.where(...).map(&:position_description)
    User.preload(:work_experiences).where(...).map(&:position_description)
    © 2023 Wantedly, Inc.

    View Slide

  5. computed_model による解決方法
    class User
    define_primary_loader :raw_user do ... end
    define_loader :work_experiences do ... end
    dependency :work_experiences
    computed def position_description
    work_experience = work_experiences.max_by { _1.started_at }
    "#{work_experience.company_name} / #{work_experience.position}"
    end
    end
    © 2023 Wantedly, Inc.
    依存関係をここに書く
    ここに書かれていないものを使うと
    エラーになる

    View Slide

  6. Active Record をデータソースとして使う例
    class User
    define_primary_loader :raw_user do |_, ids:, **|
    RawUser.where(id: ids).map do |raw_user|
    User.new(raw_user)
    end
    end
    define_loader :work_experiences do ... end
    end
    © 2023 Wantedly, Inc.

    View Slide

  7. 他サービスの API をデータソースとして使う例
    class User
    define_primary_loader :raw_user do ... end
    define_loader :work_experiences, key: -> { id } do |user_ids, _, **|
    WorkExperienceApiClient.list(user_ids: user_ids).group_by(&:user_id)
    end
    end
    © 2023 Wantedly, Inc.

    View Slide

  8. computed_model からデータを読む
    指定していないフィールドを使うとエラーになる
    users = User.batch_get(user_ids, [:position_description])
    users.map(&:position_description)
    users.map(&:name) # => error
    © 2023 Wantedly, Inc.

    View Slide

  9. まとめ
    1. 抽象化を損なわず依存関係解決
    ○ N+1問題を防ぎ、必要なデータだけ読み込む
    2. データソースには Active Record に限らず
    HTTP API なども使える
    © 2023 Wantedly, Inc.
    https://github.com/wantedly/computed_model

    View Slide