Commit 7841e7fd73e for woocommerce

commit 7841e7fd73e641d697f217b6d7a8a5f7445bf943
Author: Seghir Nadir <nadir.seghir@gmail.com>
Date:   Mon Sep 28 11:01:36 2026 +0100

    Stop scheduling Pattern Fetcher jobs (#68701)

    * Stop scheduling Pattern Fetcher jobs

    * Add changelog entry for Pattern Fetcher jobs

    * Cancel pending Pattern Fetcher jobs during database update

    * Update changelog for Pattern Fetcher job cleanup

    * Remove daily warning for empty PTK patterns

    * Update Pattern Fetcher changelog text

    * Update plugins/woocommerce/includes/class-wc-install.php

    * Fix CI checks for disabled pattern fetch jobs

diff --git a/plugins/woocommerce/changelog/disable-pattern-fetch-jobs b/plugins/woocommerce/changelog/disable-pattern-fetch-jobs
new file mode 100644
index 00000000000..1dc6400aa08
--- /dev/null
+++ b/plugins/woocommerce/changelog/disable-pattern-fetch-jobs
@@ -0,0 +1,4 @@
+Significance: patch
+Type: tweak
+
+Stop scheduling Pattern Fetcher jobs.
diff --git a/plugins/woocommerce/includes/class-wc-install.php b/plugins/woocommerce/includes/class-wc-install.php
index 9328bcfb38f..d1b9a29a63e 100644
--- a/plugins/woocommerce/includes/class-wc-install.php
+++ b/plugins/woocommerce/includes/class-wc-install.php
@@ -350,7 +350,9 @@ class WC_Install {
 		'11.1.0-1' => array(
 			'wc_update_11101_remove_deprecated_variation_gallery_option',
 		),
-		'11.2.0'   => array(
+		'11.2.0-1' => array(
+			// Run again to cancel all pattern fetch jobs now that scheduling is disabled.
+			'wc_update_1040_cleanup_legacy_ptk_patterns_fetching',
 			'wc_update_1120_remove_abandoned_cart_recovery',
 			'wc_update_1120_migrate_stock_notifications_alpha_constant',
 			'wc_update_1120_delete_surface_cart_checkout_note',
diff --git a/plugins/woocommerce/src/Blocks/BlockPatterns.php b/plugins/woocommerce/src/Blocks/BlockPatterns.php
index 93aae0eb089..2acfea83826 100644
--- a/plugins/woocommerce/src/Blocks/BlockPatterns.php
+++ b/plugins/woocommerce/src/Blocks/BlockPatterns.php
@@ -209,18 +209,6 @@ class BlockPatterns {

 		$patterns = $this->ptk_patterns_store->get_patterns();
 		if ( empty( $patterns ) || ! is_array( $patterns ) ) {
-			// Only log once per day by using a transient.
-			$transient_key = 'wc_ptk_pattern_store_warning';
-			// By only logging when patterns are empty and no fetch is scheduled,
-			// we ensure that warnings are only generated in genuinely problematic situations,
-			// such as when the pattern fetching mechanism has failed entirely.
-			if ( ! get_transient( $transient_key ) && ! ActionSchedulerUtil::has_scheduled_action( 'fetch_patterns' ) ) {
-				wc_get_logger()->warning(
-					__( 'Empty patterns received from the PTK Pattern Store', 'woocommerce' ),
-				);
-				// Set the transient to true to indicate that the warning has been logged in the current day.
-				set_transient( $transient_key, true, DAY_IN_SECONDS );
-			}
 			return;
 		}

diff --git a/plugins/woocommerce/src/Blocks/Patterns/PTKPatternsStore.php b/plugins/woocommerce/src/Blocks/Patterns/PTKPatternsStore.php
index 9cec87a9f66..1df05b99ac2 100644
--- a/plugins/woocommerce/src/Blocks/Patterns/PTKPatternsStore.php
+++ b/plugins/woocommerce/src/Blocks/Patterns/PTKPatternsStore.php
@@ -2,7 +2,6 @@

 namespace Automattic\WooCommerce\Blocks\Patterns;

-use Automattic\WooCommerce\Internal\Utilities\ActionSchedulerUtil;
 use WP_Upgrader;

 /**
@@ -41,30 +40,25 @@ class PTKPatternsStore {
 		// - The WooCommerce plugin is deactivated.
 		// - The `woocommerce_allow_tracking` option is disabled.
 		//
-		// We also want to re-fetch the patterns and update the cache when:
-		// - The `woocommerce_allow_tracking` option changes to enabled.
-		// - The WooCommerce plugin is activated (if `woocommerce_allow_tracking` is enabled).
-		// - The WooCommerce plugin is updated.
+		// Cancel any old fetch jobs when tracking is enabled or the plugin is updated.
 		add_action( 'woocommerce_activated_plugin', array( $this, 'flush_or_fetch_patterns' ), 10, 2 );
 		add_action( 'update_option_woocommerce_allow_tracking', array( $this, 'flush_or_fetch_patterns' ), 10, 2 );
 		add_action( 'deactivated_plugin', array( $this, 'flush_cached_patterns' ), 10, 2 );
 		add_action( 'upgrader_process_complete', array( $this, 'fetch_patterns_on_plugin_update' ), 10, 2 );
 		add_action( 'action_scheduler_ensure_recurring_actions', array( $this, 'ensure_recurring_fetch_patterns_if_enabled' ) );

-		// This is the scheduled action that takes care of flushing and re-fetching the patterns from the PTK API.
+		// Keep the callback for fetch jobs that were already scheduled.
 		add_action( self::FETCH_PATTERNS_ACTION, array( $this, 'fetch_patterns' ) );
 	}

 	/**
-	 * Resets the cached patterns when the `woocommerce_allow_tracking` option is disabled.
-	 * Resets and fetch the patterns from the PTK when it is enabled (if the scheduler
-	 * is initialized, it's done asynchronously via a scheduled action).
+	 * Resets the cached patterns when tracking is disabled and cancels old fetch jobs otherwise.
 	 *
 	 * @return void
 	 */
 	public function flush_or_fetch_patterns() {
 		if ( $this->allowed_tracking_is_enabled() ) {
-			$this->schedule_fetch_patterns();
+			$this->cancel_fetch_patterns_when_ready();
 			return;
 		}

@@ -72,49 +66,35 @@ class PTKPatternsStore {
 	}

 	/**
-	 * Schedule an async action to fetch the PTK patterns when the scheduler is initialized.
+	 * Cancel old pattern fetch actions when Action Scheduler is initialized.
 	 *
 	 * @return void
 	 */
-	private function schedule_fetch_patterns() {
+	private function cancel_fetch_patterns_when_ready() {
+		if ( ! function_exists( 'as_unschedule_all_actions' ) ) {
+			return;
+		}
+
 		if ( did_action( 'action_scheduler_init' ) ) {
-			$this->schedule_action_if_not_pending( self::FETCH_PATTERNS_ACTION );
+			as_unschedule_all_actions( self::FETCH_PATTERNS_ACTION, array(), 'woocommerce' );
 		} else {
 			add_action(
 				'action_scheduler_init',
 				function () {
-					$this->schedule_action_if_not_pending( self::FETCH_PATTERNS_ACTION );
+					as_unschedule_all_actions( self::FETCH_PATTERNS_ACTION, array(), 'woocommerce' );
 				}
 			);
 		}
 	}

 	/**
-	 * Ensure a recurring fetch patterns action is scheduled.
+	 * Cancel old fetch pattern actions.
 	 * This is called by the `action_scheduler_ensure_recurring_actions` hook.
 	 *
 	 * @return void
 	 */
 	public function ensure_recurring_fetch_patterns_if_enabled() {
-		if ( ! $this->allowed_tracking_is_enabled() ) {
-			return;
-		}
-
-		$this->schedule_action_if_not_pending( self::FETCH_PATTERNS_ACTION );
-	}
-
-	/**
-	 * Schedule an action if it's not already pending.
-	 *
-	 * @param string $action The action name to schedule.
-	 * @return void
-	 */
-	private function schedule_action_if_not_pending( $action ) {
-		if ( ActionSchedulerUtil::has_scheduled_action( $action, array(), 'woocommerce' ) ) {
-			return;
-		}
-
-		as_schedule_recurring_action( time(), DAY_IN_SECONDS, $action, array(), 'woocommerce' );
+		$this->cancel_fetch_patterns_when_ready();
 	}

 	/**
@@ -125,9 +105,8 @@ class PTKPatternsStore {
 	public function get_patterns() {
 		$patterns = get_option( self::OPTION_NAME );

-		// If the current data doesn't exist or is invalid, schedule fetching the patterns from the PTK.
+		// Return no patterns when the cache is missing or invalid.
 		if ( false === $patterns || ! $this->ptk_client->is_valid_schema( $patterns ) ) {
-			$this->schedule_fetch_patterns();
 			return array();
 		}

@@ -164,7 +143,7 @@ class PTKPatternsStore {
 	}

 	/**
-	 * Re-fetch the patterns when the WooCommerce plugin is updated.
+	 * Cancel old fetch jobs when the WooCommerce plugin is updated.
 	 *
 	 * @param WP_Upgrader $upgrader_object WP_Upgrader instance.
 	 * @param array       $options Array of bulk item update data.
@@ -175,7 +154,7 @@ class PTKPatternsStore {
 		if ( 'update' === $options['action'] && 'plugin' === $options['type'] && isset( $options['plugins'] ) ) {
 			foreach ( $options['plugins'] as $plugin ) {
 				if ( str_contains( $plugin, 'woocommerce.php' ) ) {
-					$this->schedule_fetch_patterns();
+					$this->cancel_fetch_patterns_when_ready();
 				}
 			}
 		}
@@ -190,22 +169,7 @@ class PTKPatternsStore {
 	public function flush_cached_patterns() {
 		delete_option( self::OPTION_NAME );

-		if ( ! function_exists( 'as_unschedule_all_actions' ) ) {
-			return;
-		}
-
-		// Unschedule any existing fetch_patterns actions.
-		// Defer unscheduling until Action Scheduler is ready to avoid errors during early initialization.
-		if ( did_action( 'action_scheduler_init' ) ) {
-			as_unschedule_all_actions( self::FETCH_PATTERNS_ACTION, array(), 'woocommerce' );
-		} else {
-			add_action(
-				'action_scheduler_init',
-				function () {
-					as_unschedule_all_actions( self::FETCH_PATTERNS_ACTION, array(), 'woocommerce' );
-				}
-			);
-		}
+		$this->cancel_fetch_patterns_when_ready();
 	}

 	/**
diff --git a/plugins/woocommerce/tests/php/src/Blocks/Patterns/PTKPatternsStoreTest.php b/plugins/woocommerce/tests/php/src/Blocks/Patterns/PTKPatternsStoreTest.php
index 9f3245944d0..a21c9402051 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/Patterns/PTKPatternsStoreTest.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/Patterns/PTKPatternsStoreTest.php
@@ -174,9 +174,9 @@ class PTKPatternsStoreTest extends \WP_UnitTestCase {
 	}

 	/**
-	 * Test fetching patterns is scheduled when tracking is allowed.
+	 * @testdox Old fetch jobs are canceled when tracking is allowed.
 	 */
-	public function test_fetching_patterns_is_schedule_when_tracking_is_allowed() {
+	public function test_fetching_patterns_is_not_scheduled_when_tracking_is_allowed() {
 		update_option( 'woocommerce_allow_tracking', 'yes' );
 		$expected_patterns = array(
 			array(
@@ -194,10 +194,11 @@ class PTKPatternsStoreTest extends \WP_UnitTestCase {
 			),
 		);
 		update_option( PTKPatternsStore::OPTION_NAME, $expected_patterns );
+		as_schedule_single_action( time(), 'fetch_patterns', array(), 'woocommerce' );

 		$this->pattern_store->flush_or_fetch_patterns();

-		$this->assertTrue( as_has_scheduled_action( 'fetch_patterns', array(), 'woocommerce' ) );
+		$this->assertFalse( as_has_scheduled_action( 'fetch_patterns', array(), 'woocommerce' ), 'Old fetch jobs should be canceled' );
 	}

 	/**
@@ -434,14 +435,15 @@ class PTKPatternsStoreTest extends \WP_UnitTestCase {
 	}

 	/**
-	 * Test ensure_recurring_fetch_patterns_if_enabled schedules recurring action when tracking is enabled.
+	 * @testdox The recurring action hook cancels old fetch jobs.
 	 */
-	public function test_ensure_recurring_fetch_patterns_schedules_recurring_action_when_tracking_enabled() {
+	public function test_ensure_recurring_fetch_patterns_cancels_old_actions() {
 		update_option( 'woocommerce_allow_tracking', 'yes' );
+		as_schedule_recurring_action( time(), DAY_IN_SECONDS, 'fetch_patterns', array(), 'woocommerce' );

 		$this->pattern_store->ensure_recurring_fetch_patterns_if_enabled();

-		$this->assertTrue( as_has_scheduled_action( 'fetch_patterns', array(), 'woocommerce' ), 'fetch_patterns action should be scheduled' );
+		$this->assertFalse( as_has_scheduled_action( 'fetch_patterns', array(), 'woocommerce' ), 'Old fetch jobs should be canceled' );
 	}

 	/**