perf: preload organiser roles to eliminate N+1 queries in event_organiser?#2692
Merged
Conversation
…iser? Replaces per-event has_role?(:organiser) calls with a single query that loads all organiser roles upfront, then does in-memory hash lookups. Reduces permission queries for a page with 30 events from ~88 to ~2 (98% reduction). Measured against dev data with an organiser user. - Adds private organising_events method (cached) that plucks all organiser role (resource_type, resource_id) pairs - Adds private admin? method (cached) for the admin fallback check - Handles nil chapter for events/meetings that use HABTM chapters
jonodrew
approved these changes
Jul 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This optimisation originally came from @jonodrew's work in #2305. That PR had deeper merge conflicts with a rewritten EventsController, but the hash-lookup approach for
event_organiser?was independently useful and is extracted here.What changed
event_organiser?inMemberPresenterwas callinghas_role?(:organiser, event)per event — one permission query (plus join) for each event on the page. For an organiser, this fires for every event/meeting rendered in the listing.The replacement preloads all organiser roles into a
{ resource_type => [resource_id] }hash with a single query, then does in-memory lookups.When this applies
Only for organiser users viewing event listing pages. The call site in
_event.html.hamlis guarded by@user.organiser?, so non-organisers never hit this path. The pages that benefit:/,/dashboard) — rendering upcoming workshops/:slug) — upcoming and past workshops/events/upcoming,/events/past) — paginated event listsBenchmarks
Against production data (29k members, 2.5k workshops, user with 258 organiser roles, 50 workshops):
The 2 remaining queries are: one
SELECTto bulk-load all organiser role rows, and oneSELECTfor the memoised admin check.