magento2: Randomly getting flooded with queries from related / upsell / crosssell blocks and price indexing
Preconditions and environment
- Magento 2.4.1
- Multiple websites at least 5
- At least 50 price rules
- 150k products
- 800+ categories
- At least 15 customer groups
Steps to reproduce
I don’t have the exact steps, other than the fact that reindexing the price rules takes too long. However in my case I see hundreds of queries like this
Which take too long to finish and drop our website
Expected result
The site is still working and queries are much faster and won’t bring the site down.
Actual result
The website is down with a lot of queries in queue. Over 2k siilar to this
SELECT `e`.*, `price_index`.`price`, `price_index`.`tax_class_id`, `price_index`.`final_price`, IF(price_index.tier_price IS NOT NULL, LEAST(price_index.min_price, price_index.tier_price), price_index.min_price) AS `minimal_price`, `price_index`.`min_price`, `price_index`.`max_price`, `price_index`.`tier_price`, `cat_index`.`position` AS `cat_index_position`, `stock_status_index`.`is_salable`, `links`.`link_id`, `links`.`product_id` AS `_linked_to_product_id`, `link_attribute_position_int`.`value` AS `position` FROM `catalog_product_entity` AS `e` INNER JOIN `inventory_stock_5` AS `inventory_in_stock` ON e.sku = inventory_in_stock.sku INNER JOIN `catalog_product_index_price` AS `price_index` ON price_index.entity_id = e.entity_id AND price_index.customer_group_id = 0 AND price_index.website_id = '2' INNER JOIN `catalog_category_product_index_store5` AS `cat_index` ON cat_index.product_id=e.entity_id AND cat_index.store_id=5 AND cat_index.visibility IN(2, 4) AND cat_index.category_id=2 INNER JOIN `catalog_product_entity` AS `product` ON product.entity_id = e.entity_id INNER JOIN `inventory_stock_5` AS `stock_status_index` ON product.sku = stock_status_index.sku INNER JOIN `catalog_product_link` AS `links` ON links.linked_product_id = e.entity_id AND links.link_type_id = 4 LEFT JOIN `catalog_product_link_attribute_int` AS `link_attribute_position_int` ON link_attribute_position_int.link_id = links.link_id AND link_attribute_position_int.product_link_attribute_id = '3' INNER JOIN `catalog_product_entity` AS `product_entity_table` ON links.product_id = product_entity_table.entity_id WHERE (inventory_in_stock.is_salable = 1) AND (stock_status_index.is_salable = 1) AND (links.product_id in ('80468')) AND (`e`.`entity_id` != '80468') ORDER BY `position` ASC
Additional information
No response
Release note
No response
Triage and priority
- Severity: S0 - Affects critical data or functionality and leaves users without workaround.
- Severity: S1 - Affects critical data or functionality and forces users to employ a workaround.
- Severity: S2 - Affects non-critical data or functionality and forces users to employ a workaround.
- Severity: S3 - Affects non-critical data or functionality and does not force users to employ a workaround.
- Severity: S4 - Affects aesthetics, professional look and feel, “quality” or “usability”.
About this issue
- Original URL
- State: open
- Created 2 years ago
- Reactions: 2
- Comments: 76 (3 by maintainers)
Second day the query didn’t appear in the slow log. I think it’s safe to say that the issue is in two areas
Performance of Upsell / Related / Cross-sells queries
inventory_stock_#
tables, causing the query to do full table scansaddInStockFilterToCollection
not checking whether the is_salable condition is already added and the tableinventory_stock_#
is joined alreadyvendor/magento/module-catalog-inventory/Model/Plugin/ProductLinks.php
causing the double join on theinventory_stock_#
tablesArchitectural flaw
The root cause of the downtime is the architectural flaw of renaming the index tables without calculating that long running queries will prevent the
rename table
action from taking place while it still tries to get an exclusive lock on the table.@engcom-Dash since you were initially assigned to the task, could you re-examine it now after all information is here?
Steps to simulate the issue
For actual replication, you’d need a high traffic live environment with cross-sells / upsells and related products spamming queries all over the database and reindex to hit while those queries don’t run optimally on any mariadb version 10.4 -> 10.6
Running steady for 24 hours now and no occurrences of the slow query, this looks like it might be the culprit.
2 weeks later, 0 downtimes after applying the patch. 0 records of the query in the slow log.
No noticeable issues.
I think I was spot on with this one. Can the engcom team please acknowledge this issue on both accounts?
RENAME_TABLE
procedure of index tables, blocks all other queriesIsInStockFilter
can make the MariaDB optimizer to cause infinite blocking on theRENAME TABLE
queries coming from Crosssell / Upsell and Related products blocks depending on the plan to execute the joinsPlease this would be vital, so that we can brainstorm and get suggestions for possible solutions to this if possible.
cc @engcom-Dash @engcom-Alfa @engcom-Bravo @engcom-Charlie @engcom-Delta
I can also confirm that this patch worked for me on Open Source 2.4.5-p4 with ~100k products with some catalog price rules.
e.g. this message
It points to a flaw in the design of the indexing process in Magento 2 as it relies on the core of renaming index tables from the
replicas
to the normal name and thenormal name
to thenormal name _old
. This one seems to be spot on.There is an opreation for a RENAME table almost 90% of the time I check the issue occuring. On top of it there are some price indexing queries which slow down and below the RENAME table there are other random queries either for price indexer or stock indexer or different other things.
I’m thinking that different indexers are trying to rename the tables while other processes are reading data from the index tables, thus causing this deadlock situation.
@engcom-Dash could you check with some core developer about this use case? It seems like a valid architectural flaw as described in MariaDB jira bug tracker because the RENAME table action can cause a deadlock when used in this context. If we could get some input from both ends we could fix this issue
It seems that those two functions are written in the following helper
\Magento\CatalogInventory\Helper\Stock
The second is modified by a plugin
\Magento\InventoryCatalog\Plugin\CatalogInventory\Model\ResourceModel\Stock\Status\AdaptAddIsInStockFilterToCollectionPlugin::aroundAddIsInStockFilterToCollection
\Magento\InventoryCatalog\Model\ResourceModel\AddIsInStockFilterToCollection::execute
To give pretty much the same result, but doesn’t prevent double joining the table due to the missing flag.
If they’re doing the same thing, why aren’t they unified?
Broken down further it’s these two culprits for the double join
\Magento\CatalogInventory\Model\Plugin\ProductLinks::afterGetProductCollection leads to
\Magento\CatalogInventory\Helper\Stock::addIsInStockFilterToCollection leads to
Because the first one doesn’t set the
has_stock_status_filter
flag. There’s no need for the double joinI’ve managed to get to the double join for inventory stock status
Here’s the execution path that leads to the double join first of all
STEP 1: \Magento\Catalog\Block\Product\ProductList\Upsell::_prepareData
null collection
STEP 2: \Magento\Catalog\Model\Product::getUpSellProductCollection
STEP 3: \Magento\Catalog\Model\ResourceModel\Product\Link\Product\Collection::setPositionOrder
STEP 4: \Magento\Catalog\Model\ResourceModel\Product\Collection::addStoreFilter
STEP 5: \Magento\Catalog\Block\Product\AbstractProduct::_addProductAttributesAndPrices
STEP 6: \Magento\CatalogInventory\Helper\Stock::addIsInStockFilterToCollection
On step 2 the flag is not set for the stock status filter so it’s joined twice.
I’m beginning to think it’s the optimizer switching the execution plan order of tables.
What I notice is on the slow log explain looks like this examining 199043934 rows
When I execute it the order differs
I guess it’s possible to replicate this issue with the new information
catalog_product_index_price
table to export a product feed for examplecatalog_product_price
catalog_product_index_price
table to rename it.What do you think @engcom-Dash ?
Could you guys take a look at this thread as well?
https://jira.mariadb.org/browse/MDEV-32033
I’ve opened it as a bug, and any input in the internal process of reindexing and why this might be happening is welcome.
A user hinted that the rename action on the indexing tables may be creating the metadata lock on the whole table, while other queries are pending leading to this.
Hi @engcom-Dash,
I’m not sure why I’m pinged here. However, that makes me think of an issue of mine : https://github.com/magento/magento2/issues/35685
As you can see in
mview.xml
:Price index gets reindexed each time a product is updated. So if we update the color, the manufacturer or any int/decimal/date/static attribute, the price gets reindexed. On website with high amounts of product data writing, that could produce a huge quantity of useless reindex sql requests.
Hope it’ll help.