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

已关闭
#7,279 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

还没有人认领这个 Issue。

评估

难度
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 小时
30 天内合并 PR
46

环境准备

在 Codespaces 中打开

在浏览器里用你自己的 GitHub 账号启动这个项目的开发容器。

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

rubyforgood/casa 的其他 Issue

查看 rubyforgood/casa 的全部 Issue

相似的 Issue

更多 Ruby Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。