Description
Update on Jul 28 2020
The issue was not completely fixed in internal Jira tickets MC-29335 and MC-29206.
That solution looks like not completely fixes a problem, as sales rule usage update still potentially causes deadlock under high load when a lot of concurrent orders are placed at the same time. The only way to fix this problem is by introducing the append-only event log of sales rule counter adjustment log and using cronjob to update sales rule table.
#25862 (comment)
#25862 (comment)
During peak sales hour, one of my customers lost 400+ Magento orders but had charged credit card transactions at the PSP.
At first, I thought PSP implementation was a reason for missing orders, but after the investigation, it turned out this a result of a pull request being merged 2 years ago in Guest Place Order API endpoint of Magento related to this issue #6363.
That change added beginTransaction()
and commit()
around the whole place order process involving payment API calls and multi-entity save process ignoring that it results in lock contention on database level for an update of the same row in the same table (sales_rule
). Although intentions were good (implementation to fix stock deduction issues), but it introduced huge performance bottleneck and rollbacks complete order history for paid and valid orders if wait-lock happens during the ordering process.
Preconditions (*)
- Magento 2.2.4+ or Magento 2.3.0+
- Enabled Guest Checkout
- Site-wide sales rule like "Free shipping above x amount"
Steps to reproduce (*)
- Stress test system with concurrent guest orders involving API payment PSP
Expected result (*)
- All paid payment transactions are saved to the database and visible in the admin panel
Actual result (*)
- A small fraction of orders is saved in the database while the majority of orders lost because of lock wait timeout.
Workaround
If you are using MSI it is possible to completely remove transactions and make it work in the same way as for logged-in user checkouts. Here is a patch that can be applied for "magento/module-checkout":
Index: Model/GuestPaymentInformationManagement.php
<+>UTF-8
===================================================================
--- Model/GuestPaymentInformationManagement.php (date 1575083782000)
+++ Model/GuestPaymentInformationManagement.php (date 1575083782000)
@@ -98,33 +98,20 @@
\Magento\Quote\Api\Data\PaymentInterface $paymentMethod,
\Magento\Quote\Api\Data\AddressInterface $billingAddress = null
) {
- $salesConnection = $this->connectionPool->getConnection('sales');
- $checkoutConnection = $this->connectionPool->getConnection('checkout');
- $salesConnection->beginTransaction();
- $checkoutConnection->beginTransaction();
-
- try {
- $this->savePaymentInformation($cartId, $email, $paymentMethod, $billingAddress);
- try {
- $orderId = $this->cartManagement->placeOrder($cartId);
- } catch (\Magento\Framework\Exception\LocalizedException $e) {
- throw new CouldNotSaveException(
- __($e->getMessage()),
- $e
- );
- } catch (\Exception $e) {
- $this->getLogger()->critical($e);
- throw new CouldNotSaveException(
+ $this->savePaymentInformation($cartId, $email, $paymentMethod, $billingAddress);
+ try {
+ $orderId = $this->cartManagement->placeOrder($cartId);
+ } catch (\Magento\Framework\Exception\LocalizedException $e) {
+ throw new CouldNotSaveException(
+ __($e->getMessage()),
+ $e
+ );
+ } catch (\Exception $e) {
+ $this->getLogger()->critical($e);
+ throw new CouldNotSaveException(
__('An error occurred on the server. Please try to place the order again.'),
- $e
- );
- }
- $salesConnection->commit();
- $checkoutConnection->commit();
- } catch (\Exception $e) {
- $salesConnection->rollBack();
- $checkoutConnection->rollBack();
- throw $e;
+ $e
+ );
}
return $orderId;
Metadata
Metadata
Assignees
Labels
Type
Projects
Status