Closed ioweb-gr closed 4 months ago
Rows_examined: 1139192931
This is actually an absurd amount. I've added temporarily a sleep(10) to make the query get logged in mariadb slow log and executed it myself, it said rows examined 53k
There's something that's causing the rows to expand to millions for thousands but I can't quite get a grasp of what causes it.
READ COMMITTED made our website drop 10 times more daily than normally.
What we know so far after I've discussed this in mariadb bug tracker:
When RENAME table kicks in, if there's any active select, it has to wait for the select to finish in order to acquire it's exclusive lock. While the exclusive lock required is pending, no further queries will execute on this table.
An active SELECT query would hold a metadata lock for the duration of the query, preventing the RENAME TABLE operation from acquiring the lock it needs until the SELECT query is complete.
So, even if you change the isolation level to READ COMMITTED, you'll still have to wait for ongoing SELECT queries to finish before you can successfully rename the table.
Finally it seems that MariaDB is picking a different query execution plan every time this issue occurs than when running it standalone and for some reason the amount of rows examined is totally different
I'm trying to eliminate the duplicate joins as I think it's the main cause for high rows to be examined as the multiplication in such cases to create a tmp table can be high. Any ideas where to look for those joins?
hello
i dont about MariaDb i work with SQL Server i see it problem on freelancer platform
if you have result sets with 1139192931 rows you have optimize your queries
so i belive the problem could be that in "AND (e
.entity_id
!= '80468')". With that instruction Instead of use seek index it force to use index scan.
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
@ioweb-gr could it be a bug in MySQL? Maybe ask the Maria community? what could cause the ROW examed to be so high.
I've already done @Adel-Magebinary on the mariadb jira ticket. I've even talked to their company's support to see what is happening. Unfortunately they're too high budget for us to pursue that avenue but they hinted that we should upgrade to 10.6 and see if we can get better traces. So I did yesterday in an effort to see if we can get to the bottom of this.
Just waiting on the next occurrence of the issue now.
I'm sincerely hoping this will turn out to be an actual bug in mariadb that was solved in 10.6 because nothing makes sense on that.
But to summarize and answer to your questions guys
When the problem first appeared this is the explain for example
Indeed it was doing a full scan which is why I added the indexers in is_salable and sku columns of inventorystock* tables
Which led to this explain
Which solved the full scan issue, but still got a huge rows examined issue
When exexcuting normally without the choked situation I get a normal explain
But because the query is by no means slow, 70-100ms, it's not logged in the slow log with full info about the rows examined.
The entity_id !=
clause is needed to filter out the same product from the linked products and it's only coming after the in(entity_id)
clause
I'm seriously buffled about this because everything looks normally in place but an absurd amount of rows examined.
I wonder if this is a fault in the mariadb optimizer.
I'll ask in Jira about this @fuhye to see if it could be a possible cause but the deal here is that whenever I run it myself, it ends fast. So I cannot replicate the slowness to see if something would fix it but only through observation.
E.g.
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
# explain: id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
# explain: 1 SIMPLE inventory_in_stock ref PRIMARY,index_sku_qty,index_is_salable,index_sku index_is_salable 1 const 1 5822.00 100.00 100.00 Using index; Using temporary; Using filesort
# explain: 1 SIMPLE stock_status_index ref PRIMARY,index_sku_qty,index_is_salable,index_sku index_is_salable 1 const 1 17093.62 100.00 100.00 Using index
# explain: 1 SIMPLE e ref PRIMARY,CATALOG_PRODUCT_ENTITY_SKU CATALOG_PRODUCT_ENTITY_SKU 195 db_edomainnlive.inventory_in_stock.sku 1 1.00 100.00 100.00 Using index condition
# explain: 1 SIMPLE links eq_ref CATALOG_PRODUCT_LINK_LINK_TYPE_ID_PRODUCT_ID_LINKED_PRODUCT_ID,CATALOG_PRODUCT_LINK_PRODUCT_ID,CATALOG_PRODUCT_LINK_LINKED_PRODUCT_ID CATALOG_PRODUCT_LINK_LINK_TYPE_ID_PRODUCT_ID_LINKED_PRODUCT_ID 10 const,const,db_edomainnlive.e.entity_id 1 0.00 100.00 100.00 Using index
# explain: 1 SIMPLE cat_index eq_ref PRIMARY,IDX_4B965DC45C352D6E4C9DC0FF50B1FCF5,IDX_47AB760CD6A893ACEA69A9C2E0112C60 PRIMARY 10 const,db_edomainnlive.e.entity_id,const 1 NULL 100.00 NULL Using where
# explain: 1 SIMPLE price_index eq_ref PRIMARY,CATALOG_PRODUCT_INDEX_PRICE_CUSTOMER_GROUP_ID,CAT_PRD_IDX_PRICE_WS_ID_CSTR_GROUP_ID_MIN_PRICE PRIMARY 10 db_edomainnlive.e.entity_id,const,const 1NULL 100.00 NULL
# explain: 1 SIMPLE product eq_ref PRIMARY,CATALOG_PRODUCT_ENTITY_SKU PRIMARY 4 db_edomainnlive.e.entity_id 1 NULL 100.00 NULL Using where
# explain: 1 SIMPLE product_entity_table eq_ref PRIMARY PRIMARY 4 db_edomainnlive.links.product_id 1 NULL 100.00 NULL Using index
# explain: 1 SIMPLE link_attribute_position_int eq_ref CAT_PRD_LNK_ATTR_INT_PRD_LNK_ATTR_ID_LNK_ID,CATALOG_PRODUCT_LINK_ATTRIBUTE_INT_LINK_ID CAT_PRD_LNK_ATTR_INT_PRD_LNK_ATTR_ID_LNK_ID 7 const,db_edomainnlive.links.link_id 1 NULL 100.00 NULL
# explain: 1 SIMPLE link_attribute_qty_decimal eq_ref CAT_PRD_LNK_ATTR_DEC_PRD_LNK_ATTR_ID_LNK_ID,CATALOG_PRODUCT_LINK_ATTRIBUTE_DECIMAL_LINK_ID CAT_PRD_LNK_ATTR_DEC_PRD_LNK_ATTR_ID_LNK_ID 7const,db_edomainnlive.links.link_id 1 NULL 100.00 NULL
When I execute it the order differs
+--+-----------+---------------------------+------+-------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------+-------+---------------------------------------------------+----+--------+--------------------------------------------+
|id|select_type|table |type |possible_keys |key |key_len|ref |rows|filtered|Extra |
+--+-----------+---------------------------+------+-------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------+-------+---------------------------------------------------+----+--------+--------------------------------------------+
|1 |SIMPLE |links |ref |CATALOG_PRODUCT_LINK_LINK_TYPE_ID_PRODUCT_ID_LINKED_PRODUCT_ID,CATALOG_PRODUCT_LINK_PRODUCT_ID,CATALOG_PRODUCT_LINK_LINKED_PRODUCT_ID|CATALOG_PRODUCT_LINK_LINK_TYPE_ID_PRODUCT_ID_LINKED_PRODUCT_ID|6 |const,const |2 |100 |Using index; Using temporary; Using filesort|
|1 |SIMPLE |cat_index |eq_ref|PRIMARY,IDX_4B965DC45C352D6E4C9DC0FF50B1FCF5,IDX_47AB760CD6A893ACEA69A9C2E0112C60 |PRIMARY |10 |const,db_edomainnlive.links.linked_product_id,const|1 |100 |Using where |
|1 |SIMPLE |price_index |eq_ref|PRIMARY,CATALOG_PRODUCT_INDEX_PRICE_CUSTOMER_GROUP_ID,CAT_PRD_IDX_PRICE_WS_ID_CSTR_GROUP_ID_MIN_PRICE |PRIMARY |10 |db_edomainnlive.links.linked_product_id,const,const|1 |100 | |
|1 |SIMPLE |e |eq_ref|PRIMARY,CATALOG_PRODUCT_ENTITY_SKU |PRIMARY |4 |db_edomainnlive.links.linked_product_id |1 |100 |Using where |
|1 |SIMPLE |inventory_in_stock |eq_ref|PRIMARY,index_sku_qty,index_is_salable,index_sku |PRIMARY |194 |db_edomainnlive.e.sku |1 |100 |Using where |
|1 |SIMPLE |product |eq_ref|PRIMARY,CATALOG_PRODUCT_ENTITY_SKU |PRIMARY |4 |db_edomainnlive.links.linked_product_id |1 |100 |Using where |
|1 |SIMPLE |stock_status_index |eq_ref|PRIMARY,index_sku_qty,index_is_salable,index_sku |PRIMARY |194 |db_edomainnlive.product.sku |1 |100 |Using where |
|1 |SIMPLE |product_entity_table |eq_ref|PRIMARY |PRIMARY |4 |db_edomainnlive.links.product_id |1 |100 |Using index |
|1 |SIMPLE |link_attribute_position_int|eq_ref|CAT_PRD_LNK_ATTR_INT_PRD_LNK_ATTR_ID_LNK_ID,CATALOG_PRODUCT_LINK_ATTRIBUTE_INT_LINK_ID |CAT_PRD_LNK_ATTR_INT_PRD_LNK_ATTR_ID_LNK_ID |7 |const,db_edomainnlive.links.link_id |1 |100 | |
|1 |SIMPLE |link_attribute_qty_decimal |eq_ref|CAT_PRD_LNK_ATTR_DEC_PRD_LNK_ATTR_ID_LNK_ID,CATALOG_PRODUCT_LINK_ATTRIBUTE_DECIMAL_LINK_ID |CAT_PRD_LNK_ATTR_DEC_PRD_LNK_ATTR_ID_LNK_ID |7 |const,db_edomainnlive.links.link_id |1 |100 | |
+--+-----------+---------------------------+------+-------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------+-------+---------------------------------------------------+----+--------+--------------------------------------------+
The more I think about it the problem must be the optimizer, the query plans are too different and the only real limitation on columns is the entity_id in the links table and the product_entity_table.
I think the optimizer may just need a hint to use those tables always as the order that magento joins the tables hints to a bad query plan.
So I was thinking about how I can modify this
\Magento\Catalog\Model\ResourceModel\Product\Link\Product\Collection::_joinLinks
So that
$joinCondition = [
'links.linked_product_id = e.entity_id',
$connection->quoteInto('links.link_type_id = ?', $this->_linkTypeId),
];
Will actually create something like this
INNER JOIN `catalog_product_link` AS `links` FORCE INDEX (CATALOG_PRODUCT_LINK_LINK_TYPE_ID_PRODUCT_ID_LINKED_PRODUCT_ID, CATALOG_PRODUCT_LINK_LINKED_PRODUCT_ID)
ON links.linked_product_id = e.entity_id AND links.link_type_id = 1
So that maybe the optimizer can consistently use the index which is used as a filter in where
clause.
@Adel-Magebinary Do you happen to know for how to force the index using the abstraction layer? I think it's worth a shot
I think this is the relevant function
/**
* Join linked products and their attributes
*
* @return $this
*/
protected function _joinLinks()
{
$select = $this->getSelect();
$connection = $select->getConnection();
$joinCondition = [
'links.linked_product_id = e.entity_id',
$connection->quoteInto('links.link_type_id = ?', $this->_linkTypeId),
];
$joinType = 'join';
$linkField = $this->getLinkField();
if ($this->productIds) {
if ($this->_isStrongMode) {
$this->getSelect()->where('links.product_id in (?)', $this->productIds);
} else {
$joinType = 'joinLeft';
$joinCondition[] = $connection->quoteInto('links.product_id in (?)', $this->productIds);
}
if (count($this->productIds) === 1) {
$this->addFieldToFilter(
$linkField,
['neq' => array_values($this->productIds)[0]]
);
}
} elseif ($this->_isStrongMode) {
$this->addFieldToFilter(
$linkField,
['eq' => -1]
);
}
if ($this->_hasLinkFilter) {
$select->{$joinType}(
['links' => $this->getTable('catalog_product_link')],
implode(' AND ', $joinCondition),
['link_id' => 'link_id', '_linked_to_product_id' => 'product_id']
);
$this->joinAttributes();
}
return $this;
}
I think a better option would be something like this
CREATE TABLE myTable ( id MEDIUMINT NOT NULL AUTO_INCREMENT, identity_id int NOT NULL, PRIMARY KEY (id) );
-- add entity_ids
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
,
link_attribute_qty_decimal
.value
AS qty
FROM
catalog_product_entity
AS e
INNER JOIN inventory_stock_7
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 = '1'
INNER JOIN catalog_category_product_index_store1
AS cat_index
ON
cat_index.product_id=e.entity_id AND cat_index.store_id=1 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_7
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 = 1
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 = '2'
LEFT JOIN catalog_product_link_attribute_decimal
AS
link_attribute_qty_decimal
ON link_attribute_qty_decimal.link_id =
links.link_id AND link_attribute_qty_decimal.product_link_attribute_id
= '6'
INNER JOIN catalog_product_entity
AS product_entity_table
ON
links.product_id = product_entity_table.entity_id
LEFT JOIN myTable as t on t.Identity_Id = e.entity_id
WHERE (inventory_in_stock.is_salable = 1) AND
(stock_status_index.is_salable = 1) AND (links.product_id in
('179427')) AND t.identity_id is null ORDER BY position
ASC;
El mar, 19 sept 2023 a las 8:40, IOWEB TECHNOLOGIES (< @.***>) escribió:
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
explain: id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
explain: 1 SIMPLE inventory_in_stock ref PRIMARY,index_sku_qty,index_is_salable,index_sku index_is_salable 1 const 1 5822.00 100.00 100.00 Using index; Using temporary; Using filesort
explain: 1 SIMPLE stock_status_index ref PRIMARY,index_sku_qty,index_is_salable,index_sku index_is_salable 1 const 1 17093.62 100.00 100.00 Using index
explain: 1 SIMPLE e ref PRIMARY,CATALOG_PRODUCT_ENTITY_SKU CATALOG_PRODUCT_ENTITY_SKU 195 db_edomainnlive.inventory_in_stock.sku 1 1.00 100.00 100.00 Using index condition
explain: 1 SIMPLE links eq_ref CATALOG_PRODUCT_LINK_LINK_TYPE_ID_PRODUCT_ID_LINKED_PRODUCT_ID,CATALOG_PRODUCT_LINK_PRODUCT_ID,CATALOG_PRODUCT_LINK_LINKED_PRODUCT_ID CATALOG_PRODUCT_LINK_LINK_TYPE_ID_PRODUCT_ID_LINKED_PRODUCT_ID 10 const,const,db_edomainnlive.e.entity_id 1 0.00 100.00 100.00 Using index
explain: 1 SIMPLE cat_index eq_ref PRIMARY,IDX_4B965DC45C352D6E4C9DC0FF50B1FCF5,IDX_47AB760CD6A893ACEA69A9C2E0112C60 PRIMARY 10 const,db_edomainnlive.e.entity_id,const 1 NULL 100.00 NULL Using where
explain: 1 SIMPLE price_index eq_ref PRIMARY,CATALOG_PRODUCT_INDEX_PRICE_CUSTOMER_GROUP_ID,CAT_PRD_IDX_PRICE_WS_ID_CSTR_GROUP_ID_MIN_PRICE PRIMARY 10 db_edomainnlive.e.entity_id,const,const 1NULL 100.00 NULL
explain: 1 SIMPLE product eq_ref PRIMARY,CATALOG_PRODUCT_ENTITY_SKU PRIMARY 4 db_edomainnlive.e.entity_id 1 NULL 100.00 NULL Using where
explain: 1 SIMPLE product_entity_table eq_ref PRIMARY PRIMARY 4 db_edomainnlive.links.product_id 1 NULL 100.00 NULL Using index
explain: 1 SIMPLE link_attribute_position_int eq_ref CAT_PRD_LNK_ATTR_INT_PRD_LNK_ATTR_ID_LNK_ID,CATALOG_PRODUCT_LINK_ATTRIBUTE_INT_LINK_ID CAT_PRD_LNK_ATTR_INT_PRD_LNK_ATTR_ID_LNK_ID 7 const,db_edomainnlive.links.link_id 1 NULL 100.00 NULL
explain: 1 SIMPLE link_attribute_qty_decimal eq_ref CAT_PRD_LNK_ATTR_DEC_PRD_LNK_ATTR_ID_LNK_ID,CATALOG_PRODUCT_LINK_ATTRIBUTE_DECIMAL_LINK_ID CAT_PRD_LNK_ATTR_DEC_PRD_LNK_ATTR_ID_LNK_ID 7const,db_edomainnlive.links.link_id 1 NULL 100.00 NULL
When I execute it the order differs
+--+-----------+---------------------------+------+-------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------+-------+---------------------------------------------------+----+--------+--------------------------------------------+ |id|select_type|table |type |possible_keys |key |key_len|ref |rows|filtered|Extra | +--+-----------+---------------------------+------+-------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------+-------+---------------------------------------------------+----+--------+--------------------------------------------+ |1 |SIMPLE |links |ref |CATALOG_PRODUCT_LINK_LINK_TYPE_ID_PRODUCT_ID_LINKED_PRODUCT_ID,CATALOG_PRODUCT_LINK_PRODUCT_ID,CATALOG_PRODUCT_LINK_LINKED_PRODUCT_ID|CATALOG_PRODUCT_LINK_LINK_TYPE_ID_PRODUCT_ID_LINKED_PRODUCT_ID|6 |const,const |2 |100 |Using index; Using temporary; Using filesort| |1 |SIMPLE |cat_index |eq_ref|PRIMARY,IDX_4B965DC45C352D6E4C9DC0FF50B1FCF5,IDX_47AB760CD6A893ACEA69A9C2E0112C60 |PRIMARY |10 |const,db_edomainnlive.links.linked_product_id,const|1 |100 |Using where | |1 |SIMPLE |price_index |eq_ref|PRIMARY,CATALOG_PRODUCT_INDEX_PRICE_CUSTOMER_GROUP_ID,CAT_PRD_IDX_PRICE_WS_ID_CSTR_GROUP_ID_MIN_PRICE |PRIMARY |10 |db_edomainnlive.links.linked_product_id,const,const|1 |100 | | |1 |SIMPLE |e |eq_ref|PRIMARY,CATALOG_PRODUCT_ENTITY_SKU |PRIMARY |4 |db_edomainnlive.links.linked_product_id |1 |100 |Using where | |1 |SIMPLE |inventory_in_stock |eq_ref|PRIMARY,index_sku_qty,index_is_salable,index_sku |PRIMARY |194 |db_edomainnlive.e.sku |1 |100 |Using where | |1 |SIMPLE |product |eq_ref|PRIMARY,CATALOG_PRODUCT_ENTITY_SKU |PRIMARY |4 |db_edomainnlive.links.linked_product_id |1 |100 |Using where | |1 |SIMPLE |stock_status_index |eq_ref|PRIMARY,index_sku_qty,index_is_salable,index_sku |PRIMARY |194 |db_edomainnlive.product.sku |1 |100 |Using where | |1 |SIMPLE |product_entity_table |eq_ref|PRIMARY |PRIMARY |4 |db_edomainnlive.links.product_id |1 |100 |Using index | |1 |SIMPLE |link_attribute_position_int|eq_ref|CAT_PRD_LNK_ATTR_INT_PRD_LNK_ATTR_ID_LNK_ID,CATALOG_PRODUCT_LINK_ATTRIBUTE_INT_LINK_ID |CAT_PRD_LNK_ATTR_INT_PRD_LNK_ATTR_ID_LNK_ID |7 |const,db_edomainnlive.links.link_id |1 |100 | | |1 |SIMPLE |link_attribute_qty_decimal |eq_ref|CAT_PRD_LNK_ATTR_DEC_PRD_LNK_ATTR_ID_LNK_ID,CATALOG_PRODUCT_LINK_ATTRIBUTE_DECIMAL_LINK_ID |CAT_PRD_LNK_ATTR_DEC_PRD_LNK_ATTR_ID_LNK_ID |7 |const,db_edomainnlive.links.link_id |1 |100 | | +--+-----------+---------------------------+------+-------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------+-------+---------------------------------------------------+----+--------+--------------------------------------------+
— Reply to this email directly, view it on GitHub https://github.com/magento/magento2/issues/36667#issuecomment-1725549906, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACP3V56UPXUQE5U4YMQGOKLX3GOGHANCNFSM6AAAAAATHYQTKQ . You are receiving this because you were mentioned.Message ID: @.***>
--
Jorge Carlos Cocom Pech Desarrollador .net (52)99 81 03 48 42 | @.*** Cancún, Q.Roo México https://maps.google.com/?q=Canc%C3%BAn,%20Q.Roo%20M%C3%A9xico
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
# explain: id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra # explain: 1 SIMPLE inventory_in_stock ref PRIMARY,index_sku_qty,index_is_salable,index_sku index_is_salable 1 const 1 5822.00 100.00 100.00 Using index; Using temporary; Using filesort # explain: 1 SIMPLE stock_status_index ref PRIMARY,index_sku_qty,index_is_salable,index_sku index_is_salable 1 const 1 17093.62 100.00 100.00 Using index # explain: 1 SIMPLE e ref PRIMARY,CATALOG_PRODUCT_ENTITY_SKU CATALOG_PRODUCT_ENTITY_SKU 195 db_edomainnlive.inventory_in_stock.sku 1 1.00 100.00 100.00 Using index condition # explain: 1 SIMPLE links eq_ref CATALOG_PRODUCT_LINK_LINK_TYPE_ID_PRODUCT_ID_LINKED_PRODUCT_ID,CATALOG_PRODUCT_LINK_PRODUCT_ID,CATALOG_PRODUCT_LINK_LINKED_PRODUCT_ID CATALOG_PRODUCT_LINK_LINK_TYPE_ID_PRODUCT_ID_LINKED_PRODUCT_ID 10 const,const,db_edomainnlive.e.entity_id 1 0.00 100.00 100.00 Using index # explain: 1 SIMPLE cat_index eq_ref PRIMARY,IDX_4B965DC45C352D6E4C9DC0FF50B1FCF5,IDX_47AB760CD6A893ACEA69A9C2E0112C60 PRIMARY 10 const,db_edomainnlive.e.entity_id,const 1 NULL 100.00 NULL Using where # explain: 1 SIMPLE price_index eq_ref PRIMARY,CATALOG_PRODUCT_INDEX_PRICE_CUSTOMER_GROUP_ID,CAT_PRD_IDX_PRICE_WS_ID_CSTR_GROUP_ID_MIN_PRICE PRIMARY 10 db_edomainnlive.e.entity_id,const,const 1NULL 100.00 NULL # explain: 1 SIMPLE product eq_ref PRIMARY,CATALOG_PRODUCT_ENTITY_SKU PRIMARY 4 db_edomainnlive.e.entity_id 1 NULL 100.00 NULL Using where # explain: 1 SIMPLE product_entity_table eq_ref PRIMARY PRIMARY 4 db_edomainnlive.links.product_id 1 NULL 100.00 NULL Using index # explain: 1 SIMPLE link_attribute_position_int eq_ref CAT_PRD_LNK_ATTR_INT_PRD_LNK_ATTR_ID_LNK_ID,CATALOG_PRODUCT_LINK_ATTRIBUTE_INT_LINK_ID CAT_PRD_LNK_ATTR_INT_PRD_LNK_ATTR_ID_LNK_ID 7 const,db_edomainnlive.links.link_id 1 NULL 100.00 NULL # explain: 1 SIMPLE link_attribute_qty_decimal eq_ref CAT_PRD_LNK_ATTR_DEC_PRD_LNK_ATTR_ID_LNK_ID,CATALOG_PRODUCT_LINK_ATTRIBUTE_DECIMAL_LINK_ID CAT_PRD_LNK_ATTR_DEC_PRD_LNK_ATTR_ID_LNK_ID 7const,db_edomainnlive.links.link_id 1 NULL 100.00 NULL
When I execute it the order differs
+--+-----------+---------------------------+------+-------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------+-------+---------------------------------------------------+----+--------+--------------------------------------------+ |id|select_type|table |type |possible_keys |key |key_len|ref |rows|filtered|Extra | +--+-----------+---------------------------+------+-------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------+-------+---------------------------------------------------+----+--------+--------------------------------------------+ |1 |SIMPLE |links |ref |CATALOG_PRODUCT_LINK_LINK_TYPE_ID_PRODUCT_ID_LINKED_PRODUCT_ID,CATALOG_PRODUCT_LINK_PRODUCT_ID,CATALOG_PRODUCT_LINK_LINKED_PRODUCT_ID|CATALOG_PRODUCT_LINK_LINK_TYPE_ID_PRODUCT_ID_LINKED_PRODUCT_ID|6 |const,const |2 |100 |Using index; Using temporary; Using filesort| |1 |SIMPLE |cat_index |eq_ref|PRIMARY,IDX_4B965DC45C352D6E4C9DC0FF50B1FCF5,IDX_47AB760CD6A893ACEA69A9C2E0112C60 |PRIMARY |10 |const,db_edomainnlive.links.linked_product_id,const|1 |100 |Using where | |1 |SIMPLE |price_index |eq_ref|PRIMARY,CATALOG_PRODUCT_INDEX_PRICE_CUSTOMER_GROUP_ID,CAT_PRD_IDX_PRICE_WS_ID_CSTR_GROUP_ID_MIN_PRICE |PRIMARY |10 |db_edomainnlive.links.linked_product_id,const,const|1 |100 | | |1 |SIMPLE |e |eq_ref|PRIMARY,CATALOG_PRODUCT_ENTITY_SKU |PRIMARY |4 |db_edomainnlive.links.linked_product_id |1 |100 |Using where | |1 |SIMPLE |inventory_in_stock |eq_ref|PRIMARY,index_sku_qty,index_is_salable,index_sku |PRIMARY |194 |db_edomainnlive.e.sku |1 |100 |Using where | |1 |SIMPLE |product |eq_ref|PRIMARY,CATALOG_PRODUCT_ENTITY_SKU |PRIMARY |4 |db_edomainnlive.links.linked_product_id |1 |100 |Using where | |1 |SIMPLE |stock_status_index |eq_ref|PRIMARY,index_sku_qty,index_is_salable,index_sku |PRIMARY |194 |db_edomainnlive.product.sku |1 |100 |Using where | |1 |SIMPLE |product_entity_table |eq_ref|PRIMARY |PRIMARY |4 |db_edomainnlive.links.product_id |1 |100 |Using index | |1 |SIMPLE |link_attribute_position_int|eq_ref|CAT_PRD_LNK_ATTR_INT_PRD_LNK_ATTR_ID_LNK_ID,CATALOG_PRODUCT_LINK_ATTRIBUTE_INT_LINK_ID |CAT_PRD_LNK_ATTR_INT_PRD_LNK_ATTR_ID_LNK_ID |7 |const,db_edomainnlive.links.link_id |1 |100 | | |1 |SIMPLE |link_attribute_qty_decimal |eq_ref|CAT_PRD_LNK_ATTR_DEC_PRD_LNK_ATTR_ID_LNK_ID,CATALOG_PRODUCT_LINK_ATTRIBUTE_DECIMAL_LINK_ID |CAT_PRD_LNK_ATTR_DEC_PRD_LNK_ATTR_ID_LNK_ID |7 |const,db_edomainnlive.links.link_id |1 |100 | | +--+-----------+---------------------------+------+-------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------------+-------+---------------------------------------------------+----+--------+--------------------------------------------+
This is the root causes of the issue. Will check it again tonight and reply.
Also my initial estimate about price indexer is wrong, these queries are coming from the Related / Upsell blocks.
I added a logger in Collection afterLoad() function and with a debug backtrace ended up to this
I'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
null collection
SELECT `e`.*
FROM `catalog_product_entity` AS `e`
INNER JOIN `inventory_stock_5` AS `inventory_in_stock` ON e.sku = inventory_in_stock.sku
WHERE (inventory_in_stock.is_salable = 1);
SELECT `e`.*
FROM `catalog_product_entity` AS `e`
INNER JOIN `inventory_stock_5` AS `inventory_in_stock` ON e.sku = inventory_in_stock.sku
WHERE (inventory_in_stock.is_salable = 1)
ORDER BY `position` ASC
SELECT `e`.*
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_website` AS `product_website`
ON product_website.product_id = e.entity_id AND product_website.website_id = 2
WHERE (inventory_in_stock.is_salable = 1)
ORDER BY `position` ASC
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`
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_website` AS `product_website`
ON product_website.product_id = e.entity_id AND product_website.website_id = 2
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'
WHERE (inventory_in_stock.is_salable = 1)
ORDER BY `position` ASC
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`
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
WHERE (inventory_in_stock.is_salable = 1)
AND (stock_status_index.is_salable = 1)
ORDER BY `position` ASC
On step 2 the flag is not set for the stock status filter so it's joined twice.
Broken down further it's these two culprits for the double join
SELECT `e`.*
FROM `catalog_product_entity` AS `e`
INNER JOIN `inventory_stock_5` AS `inventory_in_stock` ON e.sku = inventory_in_stock.sku
WHERE (inventory_in_stock.is_salable = 1)
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`
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
WHERE (inventory_in_stock.is_salable = 1)
AND (stock_status_index.is_salable = 1)
ORDER BY `position` ASC
Because the first one doesn't set the has_stock_status_filter
flag. There's no need for the double join
It seems that those two functions are written in the following helper
\Magento\CatalogInventory\Helper\Stock
/**
* Adds filtering for collection to return only in stock products
*
* @param \Magento\Catalog\Model\ResourceModel\Product\Link\Product\Collection $collection
* @return void
*/
public function addInStockFilterToCollection($collection)
{
$manageStock = $this->scopeConfig->getValue(
\Magento\CatalogInventory\Model\Configuration::XML_PATH_MANAGE_STOCK,
\Magento\Store\Model\ScopeInterface::SCOPE_STORE
);
$cond = [
'{{table}}.use_config_manage_stock = 0 AND {{table}}.manage_stock=1 AND {{table}}.is_in_stock=1',
'{{table}}.use_config_manage_stock = 0 AND {{table}}.manage_stock=0'
];
if ($manageStock) {
$cond[] = '{{table}}.use_config_manage_stock = 1 AND {{table}}.is_in_stock=1';
} else {
$cond[] = '{{table}}.use_config_manage_stock = 1';
}
$collection->joinField(
'inventory_in_stock',
'cataloginventory_stock_item',
'is_in_stock',
'product_id=entity_id',
'(' . join(') OR (', $cond) . ')'
);
}
/**
* Add only is in stock products filter to product collection
*
* @param \Magento\Catalog\Model\ResourceModel\Product\Collection $collection
* @return void
*/
public function addIsInStockFilterToCollection($collection)
{
$stockFlag = 'has_stock_status_filter';
if (!$collection->hasFlag($stockFlag)) {
$isShowOutOfStock = $this->scopeConfig->getValue(
\Magento\CatalogInventory\Model\Configuration::XML_PATH_SHOW_OUT_OF_STOCK,
\Magento\Store\Model\ScopeInterface::SCOPE_STORE
);
$resource = $this->getStockStatusResource();
$resource->addStockDataToCollection(
$collection,
!$isShowOutOfStock
);
$collection->setFlag($stockFlag, true);
}
}
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?
I've added this optimization to the code, and will check again if the situation improves
Index: vendor/magento/module-catalog-inventory/Model/Plugin/ProductLinks.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/vendor/magento/module-catalog-inventory/Model/Plugin/ProductLinks.php b/vendor/magento/module-catalog-inventory/Model/Plugin/ProductLinks.php
--- a/vendor/magento/module-catalog-inventory/Model/Plugin/ProductLinks.php
+++ b/vendor/magento/module-catalog-inventory/Model/Plugin/ProductLinks.php (date 1695246381224)
@@ -44,7 +44,7 @@
public function afterGetProductCollection(Link $subject, Collection $collection)
{
if ($this->configuration->isShowOutOfStock() != 1) {
- $this->stockHelper->addInStockFilterToCollection($collection);
+ $this->stockHelper->addIsInStockFilterToCollection($collection);
}
return $collection;
}
It will utilize the same function for filtering in stock products, thus add the flag and remove the second useless join on inventorystock# table
I've added this optimization to the code, and will check again if the situation improves
Index: vendor/magento/module-catalog-inventory/Model/Plugin/ProductLinks.php IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/vendor/magento/module-catalog-inventory/Model/Plugin/ProductLinks.php b/vendor/magento/module-catalog-inventory/Model/Plugin/ProductLinks.php --- a/vendor/magento/module-catalog-inventory/Model/Plugin/ProductLinks.php +++ b/vendor/magento/module-catalog-inventory/Model/Plugin/ProductLinks.php (date 1695246381224) @@ -44,7 +44,7 @@ public function afterGetProductCollection(Link $subject, Collection $collection) { if ($this->configuration->isShowOutOfStock() != 1) { - $this->stockHelper->addInStockFilterToCollection($collection); + $this->stockHelper->addIsInStockFilterToCollection($collection); } return $collection; }
It will utilize the same function for filtering in stock products, thus add the flag and remove the second useless join on inventorystock# table
Running steady for 24 hours now and no occurrences of the slow query, this looks like it might be the culprit.
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
inventory_stock_#
tables, causing the query to do full table scansaddInStockFilterToCollection
not checking whether the is_salable condition is already added and the table inventory_stock_#
is joined alreadyvendor/magento/module-catalog-inventory/Model/Plugin/ProductLinks.php
causing the double join on the inventory_stock_#
tables 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
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 the RENAME 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
Hello @ioweb-gr,
Thanks for the detailed explanation here in this comment https://github.com/magento/magento2/issues/36667#issuecomment-1731315222.
We are trying to reproduce this issue meanwhile can you please elaborate on the below point:
- Run a long running select query on the price index table (make it run for a long period).
Thanks
Hello @ioweb-gr,
Thanks for the detailed explanation here in this comment https://github.com/magento/magento2/issues/36667#issuecomment-1731315222.
We are trying to reproduce this issue meanwhile can you please elaborate on the below point:
- Run a long running select query on the price index table (make it run for a long period).
Thanks
As explained this main issue is coming when a select query is executing that uses the price index table that is normally renamed by the indexer. If the rename action occurs while the select query is running, Magento will drop
In my case the queries from related, crosssells and upsells took ages to finish due to improper joins so they were the culprits but also other queries like product export queries can block it as well as they run for a prolonged period
@engcom-Hotel Try inserting 2m products with 10 MSI sources with upsell and related products as dummy data. Then, run an export from the command line. Then go to catalogue price rule and create a rule with a 20% discount. You should see the freeze. Make sure cron is changed to schedule.
This is a significant performance and stability issue for large sites. It should be looked into urgently. I'm sure most of the large sites are experiencing this issue and getting random downtime. They might not be able to look as deep as @ioweb-gr did. But this is definitely should be on the top of the top of the backlog P0.
I've added this optimization to the code, and will check again if the situation improves
Index: vendor/magento/module-catalog-inventory/Model/Plugin/ProductLinks.php IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/vendor/magento/module-catalog-inventory/Model/Plugin/ProductLinks.php b/vendor/magento/module-catalog-inventory/Model/Plugin/ProductLinks.php --- a/vendor/magento/module-catalog-inventory/Model/Plugin/ProductLinks.php +++ b/vendor/magento/module-catalog-inventory/Model/Plugin/ProductLinks.php (date 1695246381224) @@ -44,7 +44,7 @@ public function afterGetProductCollection(Link $subject, Collection $collection) { if ($this->configuration->isShowOutOfStock() != 1) { - $this->stockHelper->addInStockFilterToCollection($collection); + $this->stockHelper->addIsInStockFilterToCollection($collection); } return $collection; }
It will utilize the same function for filtering in stock products, thus add the flag and remove the second useless join on inventorystock# table
Running steady for 24 hours now and no occurrences of the slow query, this looks like it might be the culprit.
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.
Hello @Adel-Magebinary,
Thanks for the steps!
We have tried to reproduce the issue with the vanilla 2.4-develop instance with the MSI package installed. But still the issue is not reproducible for us.
Please have a look at the below screenshot for reference:
Product Page
Cron setup on schedule mode
Catalog Price Rule
The only difference is, that we have started the export from the admin panel but not from CLI, can you please let us know which module you have used to run the export from CLI?
Thanks
@engcom-Hotel it's not so easy to reproduce the issue. Keep in mind we were seeing this happen 4-5 times / day on a moderate traffic website where the mariadb optimizer would be confused by the query and choosing randomly a different plan.
The underlying issue that the RENAME table queries break the website is reproducible by any type of select that needs to run for a long time
e.g. here's a sample query,
__SELECT__ `e`.*,
IF(at_status.value_id > 0, at_status.value, at_status_default.value) AS `status`,
IF(at_visibility.value_id > 0, at_visibility.value,
at_visibility_default.value) AS `visibility`,
`at_brand_logo`.`value` AS `brand_logo`,
IF(at_image.value_id > 0, at_image.value, at_image_default.value) AS `image`,
`at_manufacturer`.`value` AS `manufacturer`,
IF(at_name.value_id > 0, at_name.value, at_name_default.value) AS `name`,
IF(at_season.value_id > 0, at_season.value, at_season_default.value) AS `season`,
`at_sex`.`value` AS `sex`,
IF(at_short_description.value_id > 0, at_short_description.value,
at_short_description_default.value) AS `short_description`,
IF(at_small_image.value_id > 0, at_small_image.value,
at_small_image_default.value) AS `small_image`,
IF(at_thumbnail.value_id > 0, at_thumbnail.value,
at_thumbnail_default.value) AS `thumbnail`,
IF(at_url_key.value_id > 0, at_url_key.value,
at_url_key_default.value) AS `url_key`,
IF(at_special_price.value_id > 0, at_special_price.value,
at_special_price_default.value) AS `special_price`,
IF(at_special_from_date.value_id > 0, at_special_from_date.value,
at_special_from_date_default.value) AS `special_from_date`,
IF(at_special_to_date.value_id > 0, at_special_to_date.value,
at_special_to_date_default.value) AS `special_to_date`,
`at_price_type`.`value` AS `price_type`,
IF(at_price.value_id > 0, at_price.value, at_price_default.value) AS `price`,
`at_tax_class_id`.`value` AS `tax_class_id`,
`stock`.`qty`,
`stock`.`is_in_stock`,
`stock`.`manage_stock`,
`stock`.`use_config_manage_stock`,
`stock`.`backorders`,
`stock`.`use_config_backorders`,
MAX(DISTINCT request_path) AS `request_path`,
`curpc`.*,
`cpsl`.`parent_id`,
GROUP_CONCAT(DISTINCT categories.category_id) AS `categories_ids`,
`price_index`.`min_price`,
`price_index`.`max_price`,
`price_index`.`final_price`,
`price_index`.`price` AS `base_price`,
COUNT(DISTINCT e.entity_id) AS `total`
FROM `catalog_product_entity` AS `e`
INNER JOIN `catalog_product_website` AS `product_website`
ON product_website.product_id = e.entity_id AND product_website.website_id = 2
INNER JOIN `catalog_product_entity_int` AS `at_status_default`
ON (`at_status_default`.`entity_id` = `e`.`entity_id`) AND
(`at_status_default`.`attribute_id` = 84) AND `at_status_default`.`store_id` = 0
LEFT JOIN `catalog_product_entity_int` AS `at_status`
ON (`at_status`.`entity_id` = `e`.`entity_id`) AND (`at_status`.`attribute_id` = 84) AND
(`at_status`.`store_id` = 5)
INNER JOIN `catalog_product_entity_int` AS `at_visibility_default`
ON (`at_visibility_default`.`entity_id` = `e`.`entity_id`) AND
(`at_visibility_default`.`attribute_id` = 91) AND `at_visibility_default`.`store_id` = 0
LEFT JOIN `catalog_product_entity_int` AS `at_visibility`
ON (`at_visibility`.`entity_id` = `e`.`entity_id`) AND (`at_visibility`.`attribute_id` = 91) AND
(`at_visibility`.`store_id` = 5)
LEFT JOIN `catalog_product_entity_int` AS `at_brand_logo`
ON (`at_brand_logo`.`entity_id` = `e`.`entity_id`) AND (`at_brand_logo`.`attribute_id` = 273) AND
(`at_brand_logo`.`store_id` = 0)
LEFT JOIN `catalog_product_entity_varchar` AS `at_image_default`
ON (`at_image_default`.`entity_id` = `e`.`entity_id`) AND
(`at_image_default`.`attribute_id` = 74) AND `at_image_default`.`store_id` = 0
LEFT JOIN `catalog_product_entity_varchar` AS `at_image`
ON (`at_image`.`entity_id` = `e`.`entity_id`) AND (`at_image`.`attribute_id` = 74) AND
(`at_image`.`store_id` = 5)
LEFT JOIN `catalog_product_entity_int` AS `at_manufacturer`
ON (`at_manufacturer`.`entity_id` = `e`.`entity_id`) AND (`at_manufacturer`.`attribute_id` = 70) AND
(`at_manufacturer`.`store_id` = 0)
LEFT JOIN `catalog_product_entity_varchar` AS `at_name_default`
ON (`at_name_default`.`entity_id` = `e`.`entity_id`) AND (`at_name_default`.`attribute_id` = 60) AND
`at_name_default`.`store_id` = 0
LEFT JOIN `catalog_product_entity_varchar` AS `at_name`
ON (`at_name`.`entity_id` = `e`.`entity_id`) AND (`at_name`.`attribute_id` = 60) AND
(`at_name`.`store_id` = 5)
LEFT JOIN `catalog_product_entity_varchar` AS `at_season_default`
ON (`at_season_default`.`entity_id` = `e`.`entity_id`) AND
(`at_season_default`.`attribute_id` = 319) AND `at_season_default`.`store_id` = 0
LEFT JOIN `catalog_product_entity_varchar` AS `at_season`
ON (`at_season`.`entity_id` = `e`.`entity_id`) AND (`at_season`.`attribute_id` = 319) AND
(`at_season`.`store_id` = 5)
LEFT JOIN `catalog_product_entity_int` AS `at_sex`
ON (`at_sex`.`entity_id` = `e`.`entity_id`) AND (`at_sex`.`attribute_id` = 343) AND
(`at_sex`.`store_id` = 0)
LEFT JOIN `catalog_product_entity_text` AS `at_short_description_default`
ON (`at_short_description_default`.`entity_id` = `e`.`entity_id`) AND
(`at_short_description_default`.`attribute_id` = 62) AND
`at_short_description_default`.`store_id` = 0
LEFT JOIN `catalog_product_entity_text` AS `at_short_description`
ON (`at_short_description`.`entity_id` = `e`.`entity_id`) AND
(`at_short_description`.`attribute_id` = 62) AND (`at_short_description`.`store_id` = 5)
LEFT JOIN `catalog_product_entity_varchar` AS `at_small_image_default`
ON (`at_small_image_default`.`entity_id` = `e`.`entity_id`) AND
(`at_small_image_default`.`attribute_id` = 75) AND `at_small_image_default`.`store_id` = 0
LEFT JOIN `catalog_product_entity_varchar` AS `at_small_image`
ON (`at_small_image`.`entity_id` = `e`.`entity_id`) AND (`at_small_image`.`attribute_id` = 75) AND
(`at_small_image`.`store_id` = 5)
LEFT JOIN `catalog_product_entity_varchar` AS `at_thumbnail_default`
ON (`at_thumbnail_default`.`entity_id` = `e`.`entity_id`) AND
(`at_thumbnail_default`.`attribute_id` = 76) AND `at_thumbnail_default`.`store_id` = 0
LEFT JOIN `catalog_product_entity_varchar` AS `at_thumbnail`
ON (`at_thumbnail`.`entity_id` = `e`.`entity_id`) AND (`at_thumbnail`.`attribute_id` = 76) AND
(`at_thumbnail`.`store_id` = 5)
LEFT JOIN `catalog_product_entity_varchar` AS `at_url_key_default`
ON (`at_url_key_default`.`entity_id` = `e`.`entity_id`) AND
(`at_url_key_default`.`attribute_id` = 86) AND `at_url_key_default`.`store_id` = 0
LEFT JOIN `catalog_product_entity_varchar` AS `at_url_key`
ON (`at_url_key`.`entity_id` = `e`.`entity_id`) AND (`at_url_key`.`attribute_id` = 86) AND
(`at_url_key`.`store_id` = 5)
LEFT JOIN `catalog_product_entity_decimal` AS `at_special_price_default`
ON (`at_special_price_default`.`entity_id` = `e`.`entity_id`) AND
(`at_special_price_default`.`attribute_id` = 65) AND `at_special_price_default`.`store_id` = 0
LEFT JOIN `catalog_product_entity_decimal` AS `at_special_price`
ON (`at_special_price`.`entity_id` = `e`.`entity_id`) AND
(`at_special_price`.`attribute_id` = 65) AND (`at_special_price`.`store_id` = 5)
LEFT JOIN `catalog_product_entity_datetime` AS `at_special_from_date_default`
ON (`at_special_from_date_default`.`entity_id` = `e`.`entity_id`) AND
(`at_special_from_date_default`.`attribute_id` = 66) AND
`at_special_from_date_default`.`store_id` = 0
LEFT JOIN `catalog_product_entity_datetime` AS `at_special_from_date`
ON (`at_special_from_date`.`entity_id` = `e`.`entity_id`) AND
(`at_special_from_date`.`attribute_id` = 66) AND (`at_special_from_date`.`store_id` = 5)
LEFT JOIN `catalog_product_entity_datetime` AS `at_special_to_date_default`
ON (`at_special_to_date_default`.`entity_id` = `e`.`entity_id`) AND
(`at_special_to_date_default`.`attribute_id` = 67) AND `at_special_to_date_default`.`store_id` = 0
LEFT JOIN `catalog_product_entity_datetime` AS `at_special_to_date`
ON (`at_special_to_date`.`entity_id` = `e`.`entity_id`) AND
(`at_special_to_date`.`attribute_id` = 67) AND (`at_special_to_date`.`store_id` = 5)
LEFT JOIN `catalog_product_entity_int` AS `at_price_type`
ON (`at_price_type`.`entity_id` = `e`.`entity_id`) AND (`at_price_type`.`attribute_id` = 109) AND
(`at_price_type`.`store_id` = 0)
LEFT JOIN `catalog_product_entity_decimal` AS `at_price_default`
ON (`at_price_default`.`entity_id` = `e`.`entity_id`) AND
(`at_price_default`.`attribute_id` = 64) AND `at_price_default`.`store_id` = 0
LEFT JOIN `catalog_product_entity_decimal` AS `at_price`
ON (`at_price`.`entity_id` = `e`.`entity_id`) AND (`at_price`.`attribute_id` = 64) AND
(`at_price`.`store_id` = 5)
LEFT JOIN `catalog_product_entity_int` AS `at_tax_class_id`
ON (`at_tax_class_id`.`entity_id` = `e`.`entity_id`) AND (`at_tax_class_id`.`attribute_id` = 85) AND
(`at_tax_class_id`.`store_id` = 0)
LEFT JOIN `cataloginventory_stock_item` AS `stock` ON stock.product_id = e.entity_id
LEFT JOIN `url_rewrite` AS `url` ON url.entity_id = e.entity_id AND url.target_path NOT LIKE '%category%' and
is_autogenerated = '1' AND url.entity_type = 'product' AND url.store_id = 5
LEFT JOIN `catalog_url_rewrite_product_category` AS `curpc` ON url.url_rewrite_id = curpc.url_rewrite_id
LEFT JOIN `catalog_product_super_link` AS `cpsl` ON cpsl.product_id = e.entity_id
LEFT JOIN `catalog_product_entity` AS `cpslcpe` ON cpsl.parent_id = cpslcpe.entity_id
INNER JOIN `catalog_category_product` AS `categories`
ON (categories.product_id = e.entity_id OR categories.product_id = cpslcpe.entity_id) AND
categories.category_id IN
(4, 5, 6, 8, 10, 11, 21, 29, 30, 34, 35, 43, 44, 48, 92, 93, 95, 96, 116, 156, 157, 159, 160,
161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 190, 191, 193, 194, 195, 196, 197, 198, 208,
209, 210, 213, 214, 216, 217, 237, 240, 254, 261, 262, 269, 272, 275, 284, 290, 293, 294, 295,
298, 299, 317, 318, 321, 326, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388,
389, 390, 391, 392, 393, 394, 511, 512, 513, 514, 524, 525, 534, 540, 541, 542, 543, 544, 551,
556, 557, 683, 710, 761, 770, 772, 774, 775, 776, 777, 778, 780, 781, 782, 784, 830, 839, 854,
889, 901, 981, 982, 1032, 1033, 1034, 1035, 1036, 1055, 1056, 1058, 1060, 1127, 1128, 1129,
1130, 1131, 1132, 1133, 1134, 1135, 1136, 1137, 1138, 1139, 1140, 1141, 1142, 1143, 1144, 1145,
1167, 1177)
INNER JOIN `catalog_category_entity` AS `cce`
ON categories.category_id = cce.entity_id AND cce.path LIKE '1/2/%'
LEFT JOIN `catalog_product_index_price` AS `price_index`
ON price_index.entity_id = e.entity_id AND customer_group_id = 0 AND price_index.website_id = 2
WHERE (IF(at_status.value_id > 0, at_status.value, at_status_default.value) = '1')
AND (IF(at_visibility.value_id > 0, at_visibility.value, at_visibility_default.value) IN ('4'))
AND ((IF(at_status.value_id > 0, at_status.value, at_status_default.value) = '1'))
LIMIT 1
You may need to create the mentioned attributes / categories etc and populate them with lots of sample data.
If you have enough records in the price index table due to products and catalog rules and customer groups, this query should take more than 10 minutes to finish.
Then during that time, try to reindex prices so that the rename operation on the price index table hits while the select is still running.
Moreover, it has to be such a large dataset that the select query will be stuck in the sending data phase where it cannot release the lock for the RENAME operation because it's actively fetching the data.
Then you will see the RENAME operation take long time to finish and finally the site will drop.
Similar to my early image
I think you are missing multiple stock sources & stock assigned to the products. @engcom-Hotel
Please also assign related/upsell products, then trigger the index with the catalog price rule.
:white_check_mark: Jira issue https://jira.corp.adobe.com/browse/AC-10991 is successfully created for this GitHub issue.
:white_check_mark: Confirmed by @engcom-Hotel. Thank you for verifying the issue.
Issue Available: @engcom-Hotel, You will be automatically unassigned. Contributors/Maintainers can claim this issue to continue. To reclaim and continue work, reassign the ticket to yourself.
I've added this optimization to the code, and will check again if the situation improves
Index: vendor/magento/module-catalog-inventory/Model/Plugin/ProductLinks.php IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/vendor/magento/module-catalog-inventory/Model/Plugin/ProductLinks.php b/vendor/magento/module-catalog-inventory/Model/Plugin/ProductLinks.php --- a/vendor/magento/module-catalog-inventory/Model/Plugin/ProductLinks.php +++ b/vendor/magento/module-catalog-inventory/Model/Plugin/ProductLinks.php (date 1695246381224) @@ -44,7 +44,7 @@ public function afterGetProductCollection(Link $subject, Collection $collection) { if ($this->configuration->isShowOutOfStock() != 1) { - $this->stockHelper->addInStockFilterToCollection($collection); + $this->stockHelper->addIsInStockFilterToCollection($collection); } return $collection; }
It will utilize the same function for filtering in stock products, thus add the flag and remove the second useless join on inventorystock# table
I can confirm this appears to have fixed an issue we were having with frequent lock-ups, Magento CE 2.4.6-p2 on PHP 8.1.
Preconditions and environment
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
Additional information
No response
Release note
No response
Triage and priority