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 ) );
}
/**