Skip to content

Variant subquery forcing table scan via USE INDEX in multi-site resave#4319

Draft
lukeholder wants to merge 2 commits into
5.xfrom
fix/4305-variant-query-use-index-table-scan
Draft

Variant subquery forcing table scan via USE INDEX in multi-site resave#4319
lukeholder wants to merge 2 commits into
5.xfrom
fix/4305-variant-query-use-index-table-scan

Conversation

@lukeholder

@lukeholder lukeholder commented Jun 16, 2026

Copy link
Copy Markdown
Member

Potential fix for #4305.

  • The USE INDEX (idx_...) hint on elements_owners in VariantQuery::beforePrepare() was being applied unconditionally (for all MySQL queries with no custom orderBy), including when resaving a specific product/variant by ID.
  • When a specific element ID or owner filter is present, MySQL's optimizer would naturally pick a targeted index (primary key / elementId), but the forced sortOrder index causes it to scan the full elements_owners table instead — reported at 1.58M+ rows / ~5s per variant in a multi-site setup.
  • The hint is now skipped when id, ownerId, or primaryOwnerId is set, leaving MySQL free to choose the right index for filtered queries while still benefiting from the hint during unfiltered listing queries.

Test plan

  • Resave a product with many variants via CLI (./craft resave/products --elementId X) in a multi-site, multi-store setup and confirm query time drops significantly
  • Verify the variant element index in the control panel still loads efficiently (the listing path, with no specific ID filter, still uses the sortOrder hint)
  • Run on PostgreSQL to confirm no syntax errors (the MySQL guard is unchanged)

@lukeholder lukeholder changed the title Fixed #4305: variant subquery forcing table scan via USE INDEX in multi-site resave Variant subquery forcing table scan via USE INDEX in multi-site resave Jun 16, 2026
@lukeholder lukeholder requested a review from Copilot June 17, 2026 07:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adjusts VariantQuery’s MySQL USE INDEX (sortOrder…) join hint so it’s only applied for unfiltered “listing” queries, avoiding full table scans when resaving/querying specific variants/products in multi-site setups (issue #4305).

Changes:

  • Skip the elements_owners sortOrder index hint when id, ownerId, or primaryOwnerId filters are present.
  • Keep the hint for unfiltered queries (and no custom orderBy) to preserve existing listing performance benefits.
  • Document the fix in the unreleased changelog.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/elements/db/VariantQuery.php Gates the MySQL USE INDEX (sortOrder…) hint behind a “no specific ID/owner filter” check to prevent forced table scans.
CHANGELOG.md Adds an unreleased entry describing the multi-site resave performance fix.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +523 to +526
// Forcing the use of the `sortOrder` index only when listing (no specific element/owner filter),
// so MySQL doesn't prefer it over targeted indexes when querying by ID.
$hasSpecificFilter = !empty($this->id) || !empty($this->ownerId) || !empty($this->primaryOwnerId);
if (Craft::$app->getDb()->getIsMysql() && $sortOrderIndex !== null && empty($this->orderBy) && !$hasSpecificFilter) {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants