Commit e59a3263150 for woocommerce

commit e59a32631508dbfa22471321814eb13a1d51979a
Author: Vladimir Reznichenko <kalessil@gmail.com>
Date:   Tue Jul 28 08:30:49 2026 +0200

    [Performance] Audit variable product datastore (part 3) (#66767)

    Reduces the number of SQL queries on the variable products page (-4 / -6 SQLs)

diff --git a/plugins/woocommerce/changelog/performance-audit-variable-product-datastore-p3 b/plugins/woocommerce/changelog/performance-audit-variable-product-datastore-p3
new file mode 100644
index 00000000000..8a8ed0647c5
--- /dev/null
+++ b/plugins/woocommerce/changelog/performance-audit-variable-product-datastore-p3
@@ -0,0 +1,4 @@
+Significance: minor
+Type: performance
+
+Audit the variable product datastore and extend transients priming within it.
diff --git a/plugins/woocommerce/includes/class-wc-product-variable.php b/plugins/woocommerce/includes/class-wc-product-variable.php
index cba96f39486..c0a418c5dc8 100644
--- a/plugins/woocommerce/includes/class-wc-product-variable.php
+++ b/plugins/woocommerce/includes/class-wc-product-variable.php
@@ -397,27 +397,29 @@ class WC_Product_Variable extends WC_Product {
 	 * @return bool
 	 */
 	public function has_purchasable_variations() {
-		$variation_ids = $this->get_children();
-
+		/**
+		 * If you are evaluating performance, introducing a transient is not a viable solution.
+		 *
+		 * - The transient improves the 95th percentile (P95) load time of the product page by approximately 10%, but does not affect the median.
+		 * - The transient breaks backward compatibility. The woocommerce_is_purchasable filter from \WC_Product::is_purchasable is used by
+		 *   extensions to control product purchasability based on user role, membership, geolocation, or login status.
+		 */
+		$has_purchasable_variations = false;
+		$variation_ids              = $this->get_children();
 		if ( ! empty( $variation_ids ) ) {
 			// Prime caches to reduce future queries.
 			_prime_post_caches( $variation_ids );
-		}
-
-		foreach ( $variation_ids as $variation_id ) {
-
-			$variation = wc_get_product( $variation_id );

-			if ( ! $variation || ! $variation->is_purchasable() || ! $variation->is_in_stock() ) {
-				continue;
+			foreach ( $variation_ids as $variation_id ) {
+				$variation = wc_get_product( $variation_id );
+				if ( $variation && $variation->is_purchasable() && $variation->is_in_stock() ) {
+					$has_purchasable_variations = true;
+					break;
+				}
 			}
-
-			// We found at least one available variation, so return true.
-			return true;
 		}

-		// There were either no variations, or they were hidden because of the "continues" above.
-		return false;
+		return $has_purchasable_variations;
 	}

 	/**
diff --git a/plugins/woocommerce/includes/data-stores/class-wc-product-variable-data-store-cpt.php b/plugins/woocommerce/includes/data-stores/class-wc-product-variable-data-store-cpt.php
index ba58f91d4e9..75b90f62d72 100644
--- a/plugins/woocommerce/includes/data-stores/class-wc-product-variable-data-store-cpt.php
+++ b/plugins/woocommerce/includes/data-stores/class-wc-product-variable-data-store-cpt.php
@@ -5,10 +5,11 @@
  * @package WooCommerce\Classes
  */

-use Automattic\WooCommerce\Internal\Caches\ProductVersionStringInvalidator;
 use Automattic\WooCommerce\Enums\ProductStatus;
 use Automattic\WooCommerce\Enums\ProductStockStatus;
 use Automattic\WooCommerce\Enums\TaxDisplayMode;
+use Automattic\WooCommerce\Internal\Caches\ProductVersionStringInvalidator;
+use Automattic\WooCommerce\Internal\Utilities\ProductUtil;
 use Automattic\WooCommerce\Utilities\CallbackUtil;

 if ( ! defined( 'ABSPATH' ) ) {
@@ -135,10 +136,19 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
 			$product_id = $product->get_id();
 			wp_prime_option_caches(
 				array(
+					// Transients from \WC_Product_Variable_Data_Store_CPT class.
 					'_transient_wc_var_prices_' . $product_id,
 					'_transient_timeout_wc_var_prices_' . $product_id,
 					'_transient_wc_product_children_' . $product_id,
 					'_transient_timeout_wc_product_children_' . $product_id,
+					// Transients from \WC_Product_Variable class.
+					'_transient_wc_child_has_weight_' . $product_id,
+					'_transient_timeout_wc_child_has_weight_' . $product_id,
+					'_transient_wc_child_has_dimensions_' . $product_id,
+					'_transient_timeout_wc_child_has_dimensions_' . $product_id,
+					// Transients from \wc_get_related_products function.
+					'_transient_wc_related_' . $product_id,
+					'_transient_timeout_wc_related_' . $product_id,
 				)
 			);
 		}
@@ -1019,8 +1029,7 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
 			}
 		}

-		delete_transient( 'wc_product_children_' . $product_id );
-		delete_transient( 'wc_var_prices_' . $product_id );
+		wc_get_container()->get( ProductUtil::class )->delete_product_specific_transients_for_products( array( $product_id ) );
 	}

 	/**
@@ -1047,8 +1056,7 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
 			}
 		}

-		delete_transient( 'wc_product_children_' . $product_id );
-		delete_transient( 'wc_var_prices_' . $product_id );
+		wc_get_container()->get( ProductUtil::class )->delete_product_specific_transients_for_products( array( $product_id ) );
 	}

 	/**