While making the Trans Dimension site fast (a tag-only site anchored to the four UK country neighbourhoods, whose subtree is ~13,000 rows) a single recurring cause turned up in four different shapes. This issue records the pattern, the method to find it, what is fixed, and what still needs doing.
The pattern
Slow public pages trace to the site or user neighbourhood subtree (owned_neighbourhood_ids), which expands to thousands of rows for a nationally-anchored site. It goes wrong four ways:
- Interpolated as a literal
IN (...) list (180KB of SQL). Fix: a subquery over the ancestry path.
- Materialised as objects when only a handful are needed. Fix: load only what the result needs.
- Rebuilt per item in a render loop. Fix: memoise on the shared request-scoped object.
- Passed as a subquery the planner then hashes and seq-scans. Fix: hand it a literal id list.
How to find it
- Split
view_runtime vs db_runtime vs framework, do not trust wall time alone.
- When a per-item component is slow, render the whole collection in one pass and strip parts. The cost is often a shared-object method called per item, not an N+1.
- AppSignal reporting "N+1: No" does not clear a page; per-item CPU (rebuilding data, polymorphic url_for, unmemoised lookups) matters as much.
- Measure against a production clone, never seeds. These only appear at 13,000-neighbourhood scale.
Fixed
Still to do, roughly by production traffic x mean
PartnersController#show: 287ms mean across 11M hits on production (the largest aggregate). On the staging clone it is ~240ms of real work, 21 queries, no single landmine; needs a deeper profiling pass (events-by-organiser-or-place, map markers, containing sites).
Article.for_site / news scoping: ~550ms on the staging clone, feeding both the homepage latest-article and NewsController#index (338ms mean on production). Likely the next-biggest single win.
- The Trans Dimension theme homepage: its
events call and the article fetch dominate its ~600ms; revisit once Article.for_site is addressed.
Production AppSignal snapshot (before these fixes reach prod)
PartnersController#index 782ms mean, EventsController#index 368ms, PartnersController#show 287ms (11M hits), NewsController#index 338ms, SitesController#index 174ms. The first two are addressed by the PRs above once released.
While making the Trans Dimension site fast (a tag-only site anchored to the four UK country neighbourhoods, whose subtree is ~13,000 rows) a single recurring cause turned up in four different shapes. This issue records the pattern, the method to find it, what is fixed, and what still needs doing.
The pattern
Slow public pages trace to the site or user neighbourhood subtree (
owned_neighbourhood_ids), which expands to thousands of rows for a nationally-anchored site. It goes wrong four ways:IN (...)list (180KB of SQL). Fix: a subquery over the ancestry path.How to find it
view_runtimevsdb_runtimevs framework, do not trust wall time alone.Fixed
Site#owned_neighbourhood_ids, rebuilt per card).User#owned_neighbourhood_ids(admin policies, per partner).Still to do, roughly by production traffic x mean
PartnersController#show: 287ms mean across 11M hits on production (the largest aggregate). On the staging clone it is ~240ms of real work, 21 queries, no single landmine; needs a deeper profiling pass (events-by-organiser-or-place, map markers, containing sites).Article.for_site/ news scoping: ~550ms on the staging clone, feeding both the homepage latest-article andNewsController#index(338ms mean on production). Likely the next-biggest single win.eventscall and the article fetch dominate its ~600ms; revisit onceArticle.for_siteis addressed.Production AppSignal snapshot (before these fixes reach prod)
PartnersController#index782ms mean,EventsController#index368ms,PartnersController#show287ms (11M hits),NewsController#index338ms,SitesController#index174ms. The first two are addressed by the PRs above once released.