Supervisors and volunteers pages load every case contact in the org (13s requests, memory spikes)

クローズ
#7,279 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

まだ誰も着手していません。

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
58/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
rails, ruby

調査の方向性

Start with app/policies/supervisor_policy.rb and app/models/user.rb, then trace how app/views/supervisors/index.html.erb uses volunteer counts. Run the policy and system specs named in the issue; use the suggested console data and compare query counts for both pages. Done means the pages avoid loading every contact and the N+1 queries, while the existing counts and specs remain unchanged.

索引モデルが issue の本文から書いたものです。

説明

📈 Performance Improvement Help Wanted

Problem

/supervisors and /volunteers are the slowest pages in production. They also allocate the most memory, which likely contributes to the R14 memory errors in #7218.

From Scout APM, last 7 days:

Endpoint Avg response Slowest trace Allocations
supervisors/index 3.5 s 13.4 s ~800k
volunteers/index 1.2 s 13.8 s ~600k

In both slow traces, one preload query takes 12–13 s and loads ~29,600 CaseContact rows. That's every case contact written by every volunteer in the org, loaded on every page view.

Root cause

app/policies/supervisor_policy.rb:

class Scope < ApplicationPolicy::Scope
  def resolve
    scope.includes(
      :volunteers,
      volunteers: [:supervisor, :case_assignments, :case_contacts]
    )
  end
end

The preload doesn't even help:

  • /volunteers (VolunteersController#index, line ~11) only uses @supervisors for the supervisor filter dropdown and its ids. It never touches contacts.
  • /supervisors calls Supervisor#no_attempt_for_two_weeks (app/models/user.rb). That method calls volunteer.case_contacts.none? and volunteer.case_contacts.maximum(:created_at), which run fresh SQL anyway. The trace shows ~120 of each on top of the preload. It also runs SELECT * FROM case_assignments WHERE volunteer_id = ? 161 times, an N+1.

Suggested fix

  • Remove :case_contacts (and probably all the includes) from SupervisorPolicy::Scope#resolve. A policy scope should filter, not eager-load. Add the includes the page needs at the call site instead.
  • Rewrite no_attempt_for_two_weeks as one query per supervisor, or one grouped query for all supervisors. For example, count active volunteers with an active case assignment and no case_contacts created in the last 14 days. Use NOT EXISTS or MAX(created_at) grouped by creator_id, not Ruby loops.
  • Fix the case_assignments N+1 on /supervisors (volunteer.case_assignments.any?(&:active?) inside the loop).
  • Keep the existing spec/policies/supervisor_policy_spec.rb, spec/system/supervisors/index_spec.rb and spec/system/volunteers/index_spec.rb passing. The volunteer counts shown on /supervisors must not change.
  • Prosopite (N+1 detection) is already on in specs, so it will flag regressions.

How to verify locally

Seed data is small, so the slowness won't show up. Add a few thousand contacts in a console: FactoryBot.create_list(:case_contact, 3000, creator: Volunteer.first) in RAILS_ENV=development. Then compare the query count and time in the dev log before and after, for /supervisors and /volunteers as [email protected].

Difficulty: intermediate. It's mostly in two files (supervisor_policy.rb, user.rb), plus the views that read the counts (app/views/supervisors/index.html.erb).

主要言語
Ruby
スター
382
フォーク
547
平均マージ
2日 22時間
マージ済み PR(30日)
46

環境構築

Codespaces で開く

このプロジェクトの開発コンテナを、あなたの GitHub アカウントでブラウザ上に起動します。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

rubyforgood/casa のほかの issue

rubyforgood/casa の issue をすべて見る

似ている issue

Ruby の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。