Skip to content

perf: restore UNION ALL pagination for /events/past#2682

Merged
mroderick merged 1 commit into
masterfrom
feature/restore-union-all-pagination
Jul 2, 2026
Merged

perf: restore UNION ALL pagination for /events/past#2682
mroderick merged 1 commit into
masterfrom
feature/restore-union-all-pagination

Conversation

@mroderick

Copy link
Copy Markdown
Collaborator

Problem

The /events/past page times out on Heroku (503) because it loads all past workshops (~2,500), events (~250), and meetings (~50) into ActiveRecord objects, sorts them in Ruby, then paginates. This is the same issue described in the original #2666.

What happened to the original fix

PR #2666 built a database-level UNION ALL + LIMIT/OFFSET pagination approach, which reduced the page from 11.3s to 1.3s. However, it was accidentally lost during branch cleanup — it was merged into bullet-n-plus-one-fixes via #2666, but then that branch was force-pushed before merging to master, dropping the UNION ALL commit. The bullet-n-plus-one-fixes branch that eventually merged only contained the smaller organisers eager-load fix.

This fix

Cherry-picks the original UNION ALL commit (f775e3bd) from the surviving arel-union-pagination branch, then applies the production fix that was learned later:

  • Removed :organisers from Event.includes and Meeting.includes in load_events, because those models' organisers scope generates SQL without properly joining permissions, causing PG::UndefinedTable on production data (fixed in 39da6926 on master).
  • Preserved :organisers on Workshop.includes, because Workshop's complex associations force eager_load (LEFT JOINs), which correctly includes the permissions table.
  • Added :venue to Meeting.includes and :host to Workshop.includes to match the current master's eager loading pattern.

How it works

  • paginated_events(upcoming:) — builds an Arel UNION ALL query across Workshops, Meetings, and Events with LIMIT/OFFSET at the database level. Only 20 (id, event_type) pairs leave the database.
  • load_events(rows) — fetches full ActiveRecord objects for just those 20 rows with eager loading, preserving the UNION sort order.

Verification

  • spec/controllers/events_controller_spec.rb — 4 examples, all passing
  • spec/features/listing_events_spec.rb — 4 examples, all passing
  • Pagination at 20 per page works for past workshops, meetings, and events

…memory sort

Extract fetch_upcoming_events and fetch_past_events into two new methods:
- paginated_events(upcoming:) — builds a UNION ALL across workshops,
  meetings, and events with LIMIT/OFFSET at the database level
- load_events(rows) — fetches only the 20 visible rows with full
  eager loading, preserving UNION sort order

Previously, all past workshops (~1100) were loaded into AR objects and
sorted/paginated in Ruby. Now only the current page's 20 rows ever
leave the database.

Benchmark (first load / cached):
  Past events page: 11.3s → 1.3s
  DB queries count: ~119 → 118 (same — view N+1s unchanged)
  Allocations: 25M → 2.9M (cached)
@mroderick mroderick marked this pull request as ready for review July 2, 2026 08:01
@mroderick mroderick merged commit d0b9824 into master Jul 2, 2026
8 checks passed
@mroderick mroderick deleted the feature/restore-union-all-pagination branch July 2, 2026 08:02
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