Skip to content

Fix N+1 query on convention/signup_rounds in the My Schedule widget - #11890

Merged
nbudin merged 1 commit into
mainfrom
fix-convention-drop-convention-n-plus-one
Aug 7, 2026
Merged

Fix N+1 query on convention/signup_rounds in the My Schedule widget#11890
nbudin merged 1 commit into
mainfrom
fix-convention-drop-convention-n-plus-one

Conversation

@nbudin

@nbudin nbudin commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes INTERCODE-17E, a conventions + signup_rounds N+1 that was explicitly left open when the SignupCountPresenter N+1s were fixed in #11880 -- it was a different, unfixed code path even then.

Investigation

A scheduled Sentry check-in proposed that this was ConventionDrop#products/#available_products/#ticket_types calling .convention on each result without a preload (product.convention.timezone in ProductDrop/TicketTypeDrop). I wrote a test for that hypothesis first, per usual, and it didn't hold up: those methods all load via has_many associations off the single already-loaded convention, so Rails' automatic inverse-of association caching means .convention on each result is already free -- with or without an explicit .includes. Confirmed this and did not carry that dead-end change forward.

The actual site, found by re-checking the code that {% withdraw_user_signup_button %} runs (rendered once per signup in the same "My Schedule" widget already fixed once before for the bucket/requested_bucket N+1 in #11882):

signup_rounds = signup.event.convention.signup_rounds

UserConProfileDrop#signups already preloads event: :event_category, but never the event's convention -- so each signup's event is cached, but .convention on it isn't, and .signup_rounds on that freshly-loaded (per-signup) Convention instance re-queries too. Added convention: :signup_rounds to the existing event: preload.

Test plan

  • New regression test in test/liquid_drops/user_con_profile_drop_test.rb, mirroring exactly what the Liquid tag does (signup.event.convention.signup_rounds) -- verified to fail without the fix (10 queries for 5 signups) and pass with it (≤2)
  • Full test/liquid_drops/ suite (41 tests)
  • rubocop / stree clean

🤖 Generated with Claude Code

Sentry showed a conventions + signup_rounds N+1 (INTERCODE-17E),
explicitly left open when the SignupCountPresenter N+1s were fixed in
#11880 since it wasn't the same code path.

A scheduled check-in (see INTERCODE-17E activity) proposed that this
was an unbatched belongs_to :convention lookup in ConventionDrop's
products/available_products/ticket_types methods -- verified that
hypothesis with a test first, and it didn't hold up: those all load
via has_many associations off the single already-loaded convention, so
Rails' automatic inverse-of association caching means .convention on
each result is already free, fix or no fix.

The actual site: {% withdraw_user_signup_button %}, rendered once per
signup in the same "My Schedule" widget already fixed once for the
bucket/requested_bucket N+1 (#11882), calls
signup.event.convention.signup_rounds. UserConProfileDrop#signups
preloads event: :event_category but never the event's convention, so
each signup's event.convention -- and then .signup_rounds on that
fresh Convention instance -- re-queries once per signup.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Report: Only Changed Files listed

Package Base Coverage New Coverage Difference
app/javascript/setupI18Next.ts 🟠 65.52% 🟠 72.41% 🟢 6.89%
Overall Coverage 🟢 55.99% 🟢 56% 🟢 0.01%

Minimum allowed coverage is 0%, this run produced 56%

@nbudin
nbudin merged commit 9f96577 into main Aug 7, 2026
25 checks passed
@nbudin
nbudin deleted the fix-convention-drop-convention-n-plus-one branch August 7, 2026 16:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant