Well, it turns out that our problems were twofold, both fixed now, and we’re crackling along. I’ll detail them below, maybe someone else will have similar issues.
Due to SEO, we have a strict URL structure. Every page in our part of the site is at:
/sport, eg, /basketball/
/sport/sponsor, eg /basketball/nike/
/sport/sponsor/area, eg, /basketball/nike/california/
/sport/sponsor/type, eg, /basketball/nike/overnight/
/sport/sponsor/camp, eg, /basketball/nike/nike-girls-basketball-camp-uc-santa-cruz
(Generally, pages with trailing slashes are multi-entry; without are single.)
The problem is we’re struggling against the EE grain here, as the natural way to do the above would be:
/sports/basketball
/sponsors/nike/basketball
/types/overnight/basketball/nike
/camps/nike-overnight-basketball-camp-at-santa-cruz
with the first segments being template groups or templates in the default template group.
Instead, they’re all produced by the website/index template and its embedded templates. The drawback here is that its template caching doesn’t work; somehow website/index is overloaded and won’t cache all the pages it’s handling.
Our solution—though it won’t work for everyone—is to create a template group for every sport, and a template for every sponsor within every sport, each set to cache, and containing the same single line:
This reenables caching for each page; the basketball/index template’s cache holds the /basketball page, and presumably the basketball/nike template’s cache holds the /basketball/nike and /basketball/nike/* pages.
http://www.ussportcamps.com permits it because the number of sports and sponsors is relatively static and small, about a dozen of each. It wouldn’t work for a similarly-structured site where many more entries are going in the segment_1 and segment_2 parts of the URL.
So now we could cache pages fully—no channel:entries tags called! But unfortunately some embedded templates cannot be cached because they’re called more than once within the same pageload; if cached, the content simply repeats. Also unfortunately, it was those uncachable templates that were taking so long to load; they had huge numbers of queries. In all, template caching barely helped us.
The offenders were the two templates that display our camps grouped by area and city—and often there are more than one of these displays on a page. Camps contain a relationship field to cities, and cities in turn to areas. From our list of camps derived from programs (in turn derived from sport and sponsor), how do we display grouped by area then by city? Embarrassingly, it was a challenge.
We passed the list of camps to the entry_id parameter of a channel:entries tag, then used the related entries tag to get each camp’s city and the Tied Entries plugin to get each camp’s area. We passed all this data using PHP variables to the embedded template. Unfortunately we passed them as lists of entry_ids without intact relationships, so when looping through the areas we had to use Tied Entries again to get each particular area’s camps. Then we displayed each area’s cities, filtered by whether it was one of the cities we brought over. Then we looped through each city’s camps, filtered again by whether we brought the camp over. It worked, but all these reverse_related tags and Tied Entries instances—sometimes called repeatedly on a page—were producing huge numbers of MySQL queries. And remember, it couldn’t be cached.
Our eventual solution was to swap out these two heavily looped templates for a single query tag with a MySQL join from camps to cities then another from cities to areas, ordered by area, city and camp title. To avoid repeating the area and city on each camp line they’re only displayed if their values differ from the previous iteration through the single loop. This new method reduced to one query what was in some cases almost 2000!
With these changes we reduced page load times from up to around 20 seconds to just 2 on the first load and 0.2 on subsequent fully cached loads.
Thanks, Derek J, for proddings and encouragements, and pointing out the untenable number of query tag calls.