Align stock item lock and qty update on the primary key (#39417) - #41224
Open
lbajsarowicz wants to merge 1 commit into
Open
Align stock item lock and qty update on the primary key (#39417)#41224lbajsarowicz wants to merge 1 commit into
lbajsarowicz wants to merge 1 commit into
Conversation
registerProductsSale locked cataloginventory_stock_item rows by item_id and then updated the same rows by product_id and website_id, so one transaction requested locks twice, through two indexes, in two unrelated orders. Resolve the item ids up front and run the qty update against the primary key so the update touches only rows the FOR UPDATE already holds.
|
Hi @lbajsarowicz. Thank you for your contribution!
Allowed build names are:
You can find more information about the builds here For more details, review the Code Contributions documentation. |
Contributor
Author
|
@magento run all tests |
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Description (*)
Under concurrent order placement on the legacy CatalogInventory path, InnoDB reports deadlocks (
1213) oncataloginventory_stock_item, and the victim is theSELECT ... FOR UPDATEfromStock::lockProductsStock().StockManagement::registerProductsSale()locks the rows through one index and then updates them through another, inside a single transaction:lockProductsStock()resolvesitem_ids and runsSELECT si.* ... WHERE item_id IN (...) FOR UPDATE, arangeonPRIMARY, so locks are taken initem_idorder.correctItemsQty()runsUPDATE ... SET qty = CASE product_id ... WHERE product_id IN (...) AND website_id = ?, arangeonCATALOGINVENTORY_STOCK_ITEM_PRODUCT_ID_STOCK_ID, so it requests locks again inproduct_idorder, through secondary index records first. Its nestedbeginTransaction()/commit()releases nothing, because the adapter refcounts nesting.item_idandproduct_idorderings are independent after imports and deletions, so two overlapping carts can request the same rows in opposite order.The fix makes
correctItemsQty()resolveitem_ids with the same non-locking pre-selectlockProductsStock()already uses, sort them, and updateWHERE item_id IN (...)withCASE item_id. InregisterProductsSale()the update then touches only rows theFOR UPDATEalready holds onPRIMARY, so the transaction has exactly one lock acquisition order.lockProductsStock()additionally sorts its ids so that order is ascending for every transaction.QtyCounterInterface, the method signature and the$itemscontract are unchanged.EXPLAIN on the same five rows (1200-row table):
UPDATE ... WHERE product_id IN (...) AND website_id = 0CATALOGINVENTORY_STOCK_ITEM_PRODUCT_ID_STOCK_IDUPDATE ... WHERE item_id IN (...)PRIMARYSELECT ... WHERE item_id IN (...) FOR UPDATEPRIMARYPRIMARYThe deadlock itself needs the reporter's concurrency (100 JMeter users) and did not fire in a two-session interleave on a 1200-row table, so this PR is justified by the access paths, not by a reproduced deadlock. Please re-run the load scenario before closing the issue.
Cost: one extra indexed, non-locking
SELECTpercorrectItemsQty()call, covered byCATALOGINVENTORY_STOCK_ITEM_WEBSITE_ID_PRODUCT_ID.Fixed Issues (if relevant)
Manual testing scenarios (*)
WHERE item_id IN (...)andEXPLAINshowsPRIMARY.1213rate.Contribution checklist (*)