diff --git a/vendor/magento/module-quote/Model/CustomerCartMutex.php b/vendor/magento/module-quote/Model/CustomerCartMutex.php
new file mode 100644
index 0000000000000..07853506920ec
--- /dev/null
+++ b/vendor/magento/module-quote/Model/CustomerCartMutex.php
@@ -0,0 +1,62 @@
+storeManager->getStore($storeId)->getWebsiteId();
+ $lockName = self::LOCK_PREFIX . $customerId . '_' . $websiteId;
+
+ if (!$this->lockManager->lock($lockName, $this->lockWaitTimeout)) {
+ $this->logger->critical(
+ 'The customer cart is locked, the request has been aborted.'
+ . ' Customer ID: ' . $customerId . ', Store ID: ' . $storeId
+ );
+ throw new CustomerCartMutexException(
+ __('The customer cart is locked for processing. Please try again later.')
+ );
+ }
+
+ try {
+ return $callable(...$args);
+ } finally {
+ $this->lockManager->unlock($lockName);
+ }
+ }
+}
diff --git a/vendor/magento/module-quote/Model/CustomerCartMutexException.php b/vendor/magento/module-quote/Model/CustomerCartMutexException.php
new file mode 100644
index 0000000000000..6271116123c4a
--- /dev/null
+++ b/vendor/magento/module-quote/Model/CustomerCartMutexException.php
@@ -0,0 +1,16 @@
+eventManager = $eventManager;
$this->submitQuoteValidator = $submitQuoteValidator;
@@ -271,6 +279,8 @@ public function __construct(
->get(RemoteAddress::class);
$this->cartMutex = $cartMutex
?? ObjectManager::getInstance()->get(CartMutexInterface::class);
+ $this->customerCartMutex = $customerCartMutex
+ ?? ObjectManager::getInstance()->get(CustomerCartMutexInterface::class);
}
/**
@@ -299,7 +309,30 @@ public function createEmptyCart()
*/
public function createEmptyCartForCustomer($customerId)
{
- $storeId = $this->storeManager->getStore()->getStoreId();
+ $store = $this->storeManager->getStore();
+ try {
+ return $this->customerCartMutex->execute(
+ (int)$customerId,
+ (int)$store->getStoreId(),
+ \Closure::fromCallable([$this, 'createEmptyCartForCustomerRun']),
+ [(int)$customerId, (int)$store->getStoreId()]
+ );
+ } catch (CustomerCartMutexException $e) {
+ throw new CouldNotSaveException(__("The quote can't be created."), $e);
+ }
+ }
+
+ /**
+ * Creates an empty cart for the customer, called within the customer cart mutex.
+ *
+ * @param int $customerId
+ * @param int $storeId
+ * @return int
+ * @throws CouldNotSaveException
+ * @SuppressWarnings(PHPMD.UnusedPrivateMethod)
+ */
+ private function createEmptyCartForCustomerRun(int $customerId, int $storeId): int
+ {
$quote = $this->createCustomerCart($customerId, $storeId);
$this->_prepareCustomerQuote($quote);
diff --git a/vendor/magento/module-quote/etc/di.xml b/vendor/magento/module-quote/etc/di.xml
index cd46d9e6314c5..65eb08029e678 100644
--- a/vendor/magento/module-quote/etc/di.xml
+++ b/vendor/magento/module-quote/etc/di.xml
@@ -49,6 +49,7 @@
+