Commit c463e6aa9f3 for woocommerce

commit c463e6aa9f31a1a5c83d57f605cfd2413f04f3b1
Author: Vladimir Reznichenko <kalessil@gmail.com>
Date:   Tue Jun 2 10:19:50 2026 +0200

    [Performance] Stock reservation blocks draft order addition and status transitions (#65325)

    Modifies the stock reservation SQL to reduce the exclusive locking scope: '$query_for_reserved_stock FOR UPDATE' → '$query_for_reserved_stock LOCK IN SHARE MODE', enabling higher checkout concurrency.

diff --git a/plugins/woocommerce/changelog/performance-65313-stock-reservation-bottleneck b/plugins/woocommerce/changelog/performance-65313-stock-reservation-bottleneck
new file mode 100644
index 00000000000..d436b06ed11
--- /dev/null
+++ b/plugins/woocommerce/changelog/performance-65313-stock-reservation-bottleneck
@@ -0,0 +1,4 @@
+Significance: minor
+Type: performance
+
+Optimized the stock reservation SQL to prevent blocking order status updates.
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index d9197bf6882..24191cfac91 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -56385,12 +56385,6 @@ parameters:
 			count: 1
 			path: src/Checkout/Helpers/ReserveStock.php

-		-
-			message: '#^Method Automattic\\WooCommerce\\Checkout\\Helpers\\ReserveStock\:\:get_query_for_reserved_stock\(\) never returns void so it can be removed from the return type\.$#'
-			identifier: return.unusedType
-			count: 1
-			path: src/Checkout/Helpers/ReserveStock.php
-
 		-
 			message: '#^Method Automattic\\WooCommerce\\Checkout\\Helpers\\ReserveStock\:\:release_stock_for_order\(\) has no return type specified\.$#'
 			identifier: missingType.return
diff --git a/plugins/woocommerce/src/Checkout/Helpers/ReserveStock.php b/plugins/woocommerce/src/Checkout/Helpers/ReserveStock.php
index 5b151d5affb..c884e66c04b 100644
--- a/plugins/woocommerce/src/Checkout/Helpers/ReserveStock.php
+++ b/plugins/woocommerce/src/Checkout/Helpers/ReserveStock.php
@@ -219,12 +219,13 @@ final class ReserveStock {
 		$query_for_stock          = \WC_Data_Store::load( 'product' )->get_query_for_stock( $product_id );
 		$query_for_reserved_stock = $this->get_query_for_reserved_stock( $product_id, $order->get_id() );

+		// Performance note: this method uses pessimistic locking and requires InnoDB table types to function correctly.
 		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
 		$sql = $wpdb->prepare(
 			"
 			INSERT INTO {$wpdb->wc_reserved_stock} ( `order_id`, `product_id`, `stock_quantity`, `timestamp`, `expires` )
 			SELECT %d, %d, %d, NOW(), ( NOW() + INTERVAL %d MINUTE ) FROM DUAL
-			WHERE ( $query_for_stock FOR UPDATE ) - ( $query_for_reserved_stock FOR UPDATE ) >= %d
+			WHERE ( $query_for_stock FOR UPDATE ) - ( $query_for_reserved_stock LOCK IN SHARE MODE ) >= %d
 			ON DUPLICATE KEY UPDATE `expires` = VALUES( `expires` ), `stock_quantity` = VALUES( `stock_quantity` )
 			",
 			$order->get_id(),
@@ -235,9 +236,9 @@ final class ReserveStock {
 		);
 		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

-		// Reliability: high concurrency on the same product reservation can trigger deadlocks (error codes 1213 and 1205).
-		// We currently do not have a reliable method to identify lock errors. The $wpdb interface does not consistently provide
-		// error codes, and error messages can vary by database locale. Previously, we matched messages to 'try restarting transaction'.
+		// Retry operations in high-contention environments if error codes 1213, 1205, or 1020 occur. Since error
+		// messages may be localized and we cannot reliably identify these codes, we use a generalized approach. Do
+		// not remove this loop; it is required for the 'LOCK IN SHARE MODE' locking mode in the SQL above.
 		for ( $attempt = 0; $attempt < 3; ++$attempt ) {
 			$result = $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
 			if ( false !== $result ) {
@@ -263,18 +264,19 @@ final class ReserveStock {
 	 * Returns query statement for getting reserved stock of a product.
 	 *
 	 * @param int $product_id       Product ID.
-	 * @param int $exclude_order_id Optional order to exclude from the results.
-	 * @return string|void          Query statement.
+	 * @param int $exclude_order_id Order to exclude from the results.
+	 * @return string               Query statement.
 	 */
-	private function get_query_for_reserved_stock( $product_id, $exclude_order_id = 0 ) {
+	private function get_query_for_reserved_stock( $product_id, $exclude_order_id ): string {
 		global $wpdb;

+		$pending = OrderInternalStatus::PENDING;
 		if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
 			$join         = "{$wpdb->prefix}wc_orders orders ON stock_table.`order_id` = orders.id";
-			$where_status = "orders.status IN ( 'wc-checkout-draft', '" . OrderInternalStatus::PENDING . "' )";
+			$where_status = "orders.status IN ( 'wc-checkout-draft', '$pending' )";
 		} else {
 			$join         = "{$wpdb->posts} posts ON stock_table.`order_id` = posts.ID";
-			$where_status = "posts.post_status IN ( 'wc-checkout-draft', '" . OrderInternalStatus::PENDING . "' )";
+			$where_status = "posts.post_status IN ( 'wc-checkout-draft', '$pending' )";
 		}

 		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
diff --git a/plugins/woocommerce/tests/performance/bin/init-environment.sh b/plugins/woocommerce/tests/performance/bin/init-environment.sh
index 395f9b9b416..6fdaa031234 100755
--- a/plugins/woocommerce/tests/performance/bin/init-environment.sh
+++ b/plugins/woocommerce/tests/performance/bin/init-environment.sh
@@ -2,6 +2,8 @@

 echo "Initializing WooCommerce E2E"

+wp-env run tests-cli wp config set WP_HTTP_BLOCK_EXTERNAL false --raw --type=constant
+
 wp-env run tests-cli wp plugin activate woocommerce

 wp-env run tests-cli wp user create customer customer@woocommercecoree2etestsuite.com --user_pass=password --role=subscriber --path=/var/www/html