Summary
EventsController#past has a median duration of 466ms (max 17.3s) with 33 queries per render (median db_runtime 80ms, view_runtime 118ms). The action already sets an ETag via fresh_when, but non-conditional clients still pay the full render.
Code path: EventsController#past → fetch_past_events → paginated_events (a UNION ALL across Workshops, Meetings, Events) → load_events → EventPresenter.decorate_collection.
Measured
3h window on 2026-09-16/17: 127 requests, median 466ms, max 17.3s. Stale by the time you read this — re-measure before starting (see #2885's dashboard or the homelab drain archive).
Suggested directions
- Capture the 33 queries locally (Bullet or Prosopite against a production dump) — likely per-event loads missing
includes (sponsors, invitations, chapters)
- Batch the per-event loads into the UNION ALL or a single
preload
- The page already has per-event fragments in Solid Cache (
event_card_component/* keys) — check hit rate before adding more caching
Verify
Query count and median duration drop; ETag behaviour unchanged.
Related: #2885
Summary
EventsController#pasthas a median duration of 466ms (max 17.3s) with 33 queries per render (mediandb_runtime80ms,view_runtime118ms). The action already sets an ETag viafresh_when, but non-conditional clients still pay the full render.Code path:
EventsController#past→fetch_past_events→paginated_events(a UNION ALL across Workshops, Meetings, Events) →load_events→EventPresenter.decorate_collection.Measured
3h window on 2026-09-16/17: 127 requests, median 466ms, max 17.3s. Stale by the time you read this — re-measure before starting (see #2885's dashboard or the homelab drain archive).
Suggested directions
includes(sponsors, invitations, chapters)preloadevent_card_component/*keys) — check hit rate before adding more cachingVerify
Query count and median duration drop; ETag behaviour unchanged.
Related: #2885