You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When saving a Promotion with many required relationships (e.g. 499
PromotionRegisters), validateRelationships() called getRelatedContent()
for every child contentlet in the isParent loop — including MANY_TO_MANY
cardinalities where the result was never used. The limit=1 parameter was
silently ignored by the RelationshipCache path, causing
filterRelatedContentByLiveAndLanguage() to fire one DB query per
identifier per configured language. With 528 related identifiers and 44
languages this produced ~23K queries per save, accounting for 436 of
~641 seconds observed in Glowroot traces.
Fix: guard the query with the ONE_TO_MANY cardinality check (skipping it
entirely for other cardinalities) and replace getRelatedContent() with
FactoryLocator.getRelationshipFactory().dbRelatedContent(..., 1, 0),
which queries the tree + contentlet_version_info tables directly with
LIMIT 1, bypassing the cache path.
dbRelatedContentByChild selects cvi.working_inode and then calls contentletAPI.find(inode, ...) to hydrate a full Contentlet per row. In validateRelationships, only Contentlet.getIdentifier() is used from the result — which maps directly to t.parent, already present in the tree table.
A dedicated method returning List<String> identifiers instead of List<Contentlet> would eliminate the secondary find() call entirely:
-- current: fetches working_inode, then find(inode) per rowSELECTcvi.working_inodeAS inode
FROM tree t, contentlet_version_info cvi
WHEREt.child= ? ANDt.relation_type= ?
ANDcvi.working_inodeIS NOT NULLANDt.parent=cvi.identifier-- proposed: t.parent IS the identifier, no secondary find() neededSELECTt.parentFROM tree t
WHEREt.child= ? ANDt.relation_type= ?
2. ESContentletAPIImpl.getRelatedContent — root cause of the query explosion is still present (lines 4545–4556)
The fix bypasses the cache for this specific call, but the underlying bug remains in getRelatedContent: when the relationship cache is warm the limit parameter is silently dropped before entering getCachedRelatedContentlets:
if (relatedIds.containsKey(variableName)) {
// limit is NOT forwarded here ← root causerelatedContentlet = getCachedRelatedContentlets(relatedIds, variableName, language, live);
} else {
// limit is passed correctly hererelatedContentlet = getNonCachedRelatedContentlets(..., limit, offset, ...);
}
getCachedRelatedContentlets then expands every cached identifier across all configured languages with no upper bound:
Any other caller of getRelatedContent() that passes a limit > 0 and has a warm cache is still exposed to the same N × languages query explosion. This PR patches the symptom for validateRelationships but leaves the root cause intact.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When saving a Promotion with many required relationships (e.g. 499
PromotionRegisters), validateRelationships() called getRelatedContent()
for every child contentlet in the isParent loop — including MANY_TO_MANY
cardinalities where the result was never used. The limit=1 parameter was
silently ignored by the RelationshipCache path, causing
filterRelatedContentByLiveAndLanguage() to fire one DB query per
identifier per configured language. With 528 related identifiers and 44
languages this produced ~23K queries per save, accounting for 436 of
~641 seconds observed in Glowroot traces.
Fix: guard the query with the ONE_TO_MANY cardinality check (skipping it
entirely for other cardinalities) and replace getRelatedContent() with
FactoryLocator.getRelationshipFactory().dbRelatedContent(..., 1, 0),
which queries the tree + contentlet_version_info tables directly with
LIMIT 1, bypassing the cache path.
Screen.Recording.2026-04-21.at.4.49.32.PM.mov