Affected: SP Page Builder Pro 6.7.1. The regression was introduced between 6.5.0 and 6.7.1 in components/com_sppagebuilder/builder/classes/base.php, method SppagebuilderBase::getArticleTags().
What happens
After updating from 6.5.0 to 6.7.1, every front-end page render takes over a minute. Measured with Joomla's debug console on the home page, single request, no other traffic on the server:
- Total page render: 62.27 s
- Total query time: 60.21 s across 640 queries
- One single query: 58.79 s, which is 97.79 % of the whole render time
The same page under 6.5.0 renders in a few seconds with 582 queries. So it is not the number of queries that changed, it is one statement.
The debug console lists the origin of that query as plugins/system/falangdriver/falang_database.php:334. That is only where execution passes through — this site uses the FaLang translation driver, which wraps Joomla's database driver. The statement itself is built in getArticleTags(), and running it directly against the database reproduces the same runtime.
The query
SELECT DISTINCT a.id, a.title, a.level, a.published, a.lft, a.parent_id,
parent.title AS parent_title
FROM (
SELECT id, title, level, published, parent_id, lft, rgt
FROM #tags
WHERE published = 1
) AS a
LEFT JOIN #tags AS b ON a.lft > b.lft AND a.rgt < b.rgt
LEFT JOIN #__tags AS parent ON a.parent_id = parent.id AND parent.published = 1
WHERE a.level != 0
ORDER BY a.lft ASC
Why it is slow
The join on alias b uses a range condition (a.lft > b.lft AND a.rgt < b.rgt). It cannot use an index, so it produces a cross product. This site has 11,549 published tags, which puts that join in the order of 133 million row comparisons — before the second join and the DISTINCT deduplication on top of it.
What makes this worse is that alias b is never referenced. It appears in neither the SELECT, the WHERE, nor the ORDER BY. Its only effect is to multiply rows that DISTINCT then removes again. The join is functionless.
6.5.0 had only this one join, which stayed tolerable. 6.7.1 added the parent join to fetch parent_title, and that multiplies the already quadratic intermediate result a second time.
Why this is more than a slow page
A database connection is held for the duration of the whole request, not per query. At 60 seconds per render, every page view occupies its connections for a full minute. On hosting with a per-user connection cap this exhausts the pool almost immediately:
▎ mysqli_sql_exception: User '…' has exceeded the 'max_user_connections' resource (current value: 40)
Once the pool is exhausted nothing can connect any more — not the site, not phpMyAdmin. The result looks like a database outage rather than an extension problem, which makes it very hard to trace back to the actual cause. It cost us two days and several hours of downtime before we found it.
Suggested fix
Drop the unused b join and the redundant subquery. Keep the parent join — it is needed for parent_title and resolves via the primary key, so it is index-backed. DISTINCT can go as well, because without the b join no duplicates are produced.
SELECT a.id, a.title, a.level, a.published, a.lft, a.parent_id,
parent.title AS parent_title
FROM #tags AS a
LEFT JOIN #tags AS parent ON a.parent_id = parent.id AND parent.published = 1
WHERE a.published = 1
AND a.level != 0
ORDER BY a.lft ASC
Verified on our installation: identical result set, query time in the low milliseconds, page render down from 62 seconds to 4.
Environment: Joomla 5.4.3, MySQL on shared hosting (IONOS) with max_user_connections = 40, 11,549 published tags in #__tags, FaLang translation driver active.