mirror of
https://github.com/woocommerce/woocommerce.git
synced 2025-08-18 10:21:16 +08:00
Migrate all of Woo cron jobs to use action scheduler (#59325)
Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
parent
bf1915fcf2
commit
1e883791e5
9 changed files with 137 additions and 66 deletions
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: update
|
||||
|
||||
Migrate all cron jobs (session clean up, unpaid order clean up, sale schedules, log clean up...) to use action scheduler instead.
|
|
@ -561,7 +561,7 @@ class WC_Install {
|
|||
self::create_roles();
|
||||
self::setup_environment();
|
||||
self::create_terms();
|
||||
self::create_cron_jobs();
|
||||
self::clear_cron_jobs();
|
||||
self::delete_obsolete_notes();
|
||||
self::create_files();
|
||||
self::maybe_create_pages();
|
||||
|
@ -870,20 +870,20 @@ class WC_Install {
|
|||
*/
|
||||
public static function cron_schedules( $schedules ) {
|
||||
$schedules['monthly'] = array(
|
||||
'interval' => 2635200,
|
||||
'interval' => MONTH_IN_SECONDS,
|
||||
'display' => __( 'Monthly', 'woocommerce' ),
|
||||
);
|
||||
$schedules['fifteendays'] = array(
|
||||
'interval' => 1296000,
|
||||
'interval' => 15 * DAY_IN_SECONDS,
|
||||
'display' => __( 'Every 15 Days', 'woocommerce' ),
|
||||
);
|
||||
return $schedules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create cron jobs (clear them first).
|
||||
* Removes old cron jobs now that we moved to Action Scheduler.
|
||||
*/
|
||||
private static function create_cron_jobs() {
|
||||
private static function clear_cron_jobs() {
|
||||
wp_clear_scheduled_hook( 'woocommerce_scheduled_sales' );
|
||||
wp_clear_scheduled_hook( 'woocommerce_cancel_unpaid_orders' );
|
||||
wp_clear_scheduled_hook( 'woocommerce_cleanup_sessions' );
|
||||
|
@ -892,44 +892,6 @@ class WC_Install {
|
|||
wp_clear_scheduled_hook( 'woocommerce_geoip_updater' );
|
||||
wp_clear_scheduled_hook( 'woocommerce_tracker_send_event' );
|
||||
wp_clear_scheduled_hook( 'woocommerce_cleanup_rate_limits' );
|
||||
|
||||
$ve = get_option( 'gmt_offset' ) > 0 ? '-' : '+';
|
||||
|
||||
wp_schedule_event( strtotime( '00:00 tomorrow ' . $ve . absint( get_option( 'gmt_offset' ) ) . ' HOURS' ), 'daily', 'woocommerce_scheduled_sales' );
|
||||
|
||||
$held_duration = get_option( 'woocommerce_hold_stock_minutes', '60' );
|
||||
|
||||
if ( '' !== $held_duration ) {
|
||||
/**
|
||||
* Determines the interval at which to cancel unpaid orders in minutes.
|
||||
*
|
||||
* @since 5.1.0
|
||||
*/
|
||||
$cancel_unpaid_interval = apply_filters( 'woocommerce_cancel_unpaid_orders_interval_minutes', absint( $held_duration ) );
|
||||
wp_schedule_single_event( time() + ( absint( $cancel_unpaid_interval ) * 60 ), 'woocommerce_cancel_unpaid_orders' );
|
||||
}
|
||||
|
||||
// Delay the first run of `woocommerce_cleanup_personal_data` by 10 seconds
|
||||
// so it doesn't occur in the same request. WooCommerce Admin also schedules
|
||||
// a daily cron that gets lost due to a race condition. WC_Privacy's background
|
||||
// processing instance updates the cron schedule from within a cron job.
|
||||
wp_schedule_event( time() + 10, 'daily', 'woocommerce_cleanup_personal_data' );
|
||||
wp_schedule_event( time() + ( 3 * HOUR_IN_SECONDS ), 'daily', 'woocommerce_cleanup_logs' );
|
||||
wp_schedule_event( time() + ( 6 * HOUR_IN_SECONDS ), 'twicedaily', 'woocommerce_cleanup_sessions' );
|
||||
wp_schedule_event( time() + MINUTE_IN_SECONDS, 'fifteendays', 'woocommerce_geoip_updater' );
|
||||
/**
|
||||
* How frequent to schedule the tracker send event.
|
||||
*
|
||||
* @since 2.3.0
|
||||
*/
|
||||
wp_schedule_event( time() + 10, apply_filters( 'woocommerce_tracker_event_recurrence', 'daily' ), 'woocommerce_tracker_send_event' );
|
||||
wp_schedule_event( time() + ( 3 * HOUR_IN_SECONDS ), 'daily', 'woocommerce_cleanup_rate_limits' );
|
||||
|
||||
if ( ! wp_next_scheduled( 'wc_admin_daily' ) ) {
|
||||
wp_schedule_event( time(), 'daily', 'wc_admin_daily' );
|
||||
}
|
||||
// Note: this is potentially redundant when the core package exists.
|
||||
wp_schedule_single_event( time() + 10, 'generate_category_lookup_table' );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -309,6 +309,7 @@ final class WooCommerce {
|
|||
add_action( 'woocommerce_updated', array( $this, 'add_woocommerce_remote_variant' ) );
|
||||
add_action( 'woocommerce_newly_installed', 'wc_set_hooked_blocks_version', 10 );
|
||||
add_action( 'update_option_woocommerce_allow_tracking', array( $this, 'get_tracking_history' ), 10, 2 );
|
||||
add_action( 'action_scheduler_schedule_recurring_actions', array( $this, 'register_recurring_actions' ) );
|
||||
|
||||
add_filter( 'robots_txt', array( $this, 'robots_txt' ) );
|
||||
add_filter( 'wp_plugin_dependencies_slug', array( $this, 'convert_woocommerce_slug' ) );
|
||||
|
@ -1384,6 +1385,74 @@ final class WooCommerce {
|
|||
update_option( 'woocommerce_allow_tracking_last_modified', time() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register recurring actions.
|
||||
*/
|
||||
public function register_recurring_actions() {
|
||||
// Check if Action Scheduler is available.
|
||||
if ( ! function_exists( 'as_schedule_recurring_action' ) || ! function_exists( 'as_schedule_single_action' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$ve = get_option( 'gmt_offset' ) > 0 ? '-' : '+';
|
||||
|
||||
// Schedule daily sales event at midnight tomorrow.
|
||||
$scheduled_sales_time = strtotime( '00:00 tomorrow ' . $ve . absint( get_option( 'gmt_offset' ) ) . ' HOURS' );
|
||||
|
||||
as_schedule_recurring_action( $scheduled_sales_time, DAY_IN_SECONDS, 'woocommerce_scheduled_sales', array(), 'woocommerce', true );
|
||||
|
||||
$held_duration = get_option( 'woocommerce_hold_stock_minutes', '60' );
|
||||
|
||||
if ( '' !== $held_duration ) {
|
||||
/**
|
||||
* Determines the interval at which to cancel unpaid orders in minutes.
|
||||
*
|
||||
* @since 5.1.0
|
||||
*/
|
||||
$cancel_unpaid_interval = apply_filters( 'woocommerce_cancel_unpaid_orders_interval_minutes', absint( $held_duration ) );
|
||||
|
||||
as_schedule_single_action( time() + ( absint( $cancel_unpaid_interval ) * 60 ), 'woocommerce_cancel_unpaid_orders', array(), 'woocommerce', true );
|
||||
|
||||
}
|
||||
|
||||
// Delay the first run of `woocommerce_cleanup_personal_data` by 10 seconds
|
||||
// so it doesn't occur in the same request. WooCommerce Admin also schedules
|
||||
// a daily cron that gets lost due to a race condition. WC_Privacy's background
|
||||
// processing instance updates the cron schedule from within a cron job.
|
||||
|
||||
as_schedule_recurring_action( time() + 10, DAY_IN_SECONDS, 'woocommerce_cleanup_personal_data', array(), 'woocommerce', true );
|
||||
|
||||
// Schedule daily cleanup logs at 3 AM.
|
||||
|
||||
as_schedule_recurring_action( time() + ( 3 * HOUR_IN_SECONDS ), DAY_IN_SECONDS, 'woocommerce_cleanup_logs', array(), 'woocommerce', true );
|
||||
|
||||
// Schedule twice daily cleanup sessions at 6 AM and 6 PM.
|
||||
|
||||
as_schedule_recurring_action( time() + ( 6 * HOUR_IN_SECONDS ), 12 * HOUR_IN_SECONDS, 'woocommerce_cleanup_sessions', array(), 'woocommerce', true );
|
||||
|
||||
// Schedule geoip updater every 15 days.
|
||||
|
||||
as_schedule_recurring_action( time() + MINUTE_IN_SECONDS, 15 * DAY_IN_SECONDS, 'woocommerce_geoip_updater', array(), 'woocommerce', true );
|
||||
|
||||
/**
|
||||
* How frequent to schedule the tracker send event.
|
||||
*
|
||||
* @since 2.3.0
|
||||
*/
|
||||
$tracker_recurrence = apply_filters( 'woocommerce_tracker_event_recurrence', 'daily' );
|
||||
$core_internals = wp_get_schedules();
|
||||
as_schedule_recurring_action( time() + 10, $core_internals[ $tracker_recurrence ]['interval'], 'woocommerce_tracker_send_event', array(), 'woocommerce', true );
|
||||
|
||||
// Schedule daily cleanup rate limits at 3 AM.
|
||||
|
||||
as_schedule_recurring_action( time() + ( 3 * HOUR_IN_SECONDS ), DAY_IN_SECONDS, 'woocommerce_cleanup_rate_limits', array(), 'woocommerce', true );
|
||||
|
||||
as_schedule_recurring_action( time(), DAY_IN_SECONDS, 'wc_admin_daily', array(), 'woocommerce', true );
|
||||
|
||||
// Note: this is potentially redundant when the core package exists.
|
||||
as_schedule_single_action( time() + 10, 'generate_category_lookup_table', array(), 'woocommerce', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the customizer on the plugins_loaded action.
|
||||
* If WooCommerce is network activated, wp_is_block_theme() will be called too early,
|
||||
|
|
|
@ -1225,11 +1225,28 @@ add_filter( 'woocommerce_admin_settings_sanitize_option_woocommerce_price_num_de
|
|||
function wc_format_option_hold_stock_minutes( $value, $option, $raw_value ) {
|
||||
$value = ! empty( $raw_value ) ? absint( $raw_value ) : ''; // Allow > 0 or set to ''.
|
||||
|
||||
wp_clear_scheduled_hook( 'woocommerce_cancel_unpaid_orders' );
|
||||
// Clear existing scheduled events.
|
||||
if ( function_exists( 'as_unschedule_all_actions' ) ) {
|
||||
as_unschedule_all_actions( 'woocommerce_cancel_unpaid_orders' );
|
||||
} else {
|
||||
wp_clear_scheduled_hook( 'woocommerce_cancel_unpaid_orders' );
|
||||
}
|
||||
|
||||
if ( '' !== $value ) {
|
||||
/**
|
||||
* Filters the interval at which to cancel unpaid orders in minutes.
|
||||
*
|
||||
* @since 5.1.0
|
||||
*
|
||||
* @param int $cancel_unpaid_interval The interval at which to cancel unpaid orders in minutes.
|
||||
*/
|
||||
$cancel_unpaid_interval = apply_filters( 'woocommerce_cancel_unpaid_orders_interval_minutes', absint( $value ) );
|
||||
wp_schedule_single_event( time() + ( absint( $cancel_unpaid_interval ) * 60 ), 'woocommerce_cancel_unpaid_orders' );
|
||||
|
||||
if ( function_exists( 'as_schedule_single_action' ) ) {
|
||||
as_schedule_single_action( time() + ( absint( $cancel_unpaid_interval ) * 60 ), 'woocommerce_cancel_unpaid_orders', array(), 'woocommerce', true );
|
||||
} else {
|
||||
wp_schedule_single_event( time() + ( absint( $cancel_unpaid_interval ) * 60 ), 'woocommerce_cancel_unpaid_orders' );
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
|
|
|
@ -1075,13 +1075,32 @@ add_action( 'woocommerce_trash_order', 'wc_update_coupon_usage_counts' );
|
|||
* Cancel all unpaid orders after held duration to prevent stock lock for those products.
|
||||
*/
|
||||
function wc_cancel_unpaid_orders() {
|
||||
$held_duration = get_option( 'woocommerce_hold_stock_minutes' );
|
||||
$held_duration = get_option( 'woocommerce_hold_stock_minutes', '60' );
|
||||
|
||||
// Re-schedule the event before cancelling orders
|
||||
// this way in case of a DB timeout or (plugin) crash the event is always scheduled for retry.
|
||||
wp_clear_scheduled_hook( 'woocommerce_cancel_unpaid_orders' );
|
||||
/**
|
||||
* Filters the interval at which to cancel unpaid orders in minutes.
|
||||
*
|
||||
* @since 5.1.0
|
||||
*
|
||||
* @param int $cancel_unpaid_interval The interval at which to cancel unpaid orders in minutes.
|
||||
*/
|
||||
$cancel_unpaid_interval = apply_filters( 'woocommerce_cancel_unpaid_orders_interval_minutes', absint( $held_duration ) );
|
||||
wp_schedule_single_event( time() + ( absint( $cancel_unpaid_interval ) * 60 ), 'woocommerce_cancel_unpaid_orders' );
|
||||
|
||||
// Clear existing scheduled events.
|
||||
if ( function_exists( 'as_unschedule_all_actions' ) ) {
|
||||
as_unschedule_all_actions( 'woocommerce_cancel_unpaid_orders' );
|
||||
} else {
|
||||
wp_clear_scheduled_hook( 'woocommerce_cancel_unpaid_orders' );
|
||||
}
|
||||
|
||||
// Schedule the next event using Action Scheduler if available, otherwise fall back to WordPress cron.
|
||||
if ( function_exists( 'as_schedule_single_action' ) ) {
|
||||
as_schedule_single_action( time() + ( absint( $cancel_unpaid_interval ) * 60 ), 'woocommerce_cancel_unpaid_orders', array(), 'woocommerce', true );
|
||||
} else {
|
||||
wp_schedule_single_event( time() + ( absint( $cancel_unpaid_interval ) * 60 ), 'woocommerce_cancel_unpaid_orders' );
|
||||
}
|
||||
|
||||
if ( $held_duration < 1 || 'yes' !== get_option( 'woocommerce_manage_stock' ) ) {
|
||||
return;
|
||||
|
|
|
@ -51,9 +51,6 @@ class OrderCountCacheService {
|
|||
add_action( 'woocommerce_before_delete_order', array( $this, 'update_on_order_deleted' ), 10, 2 );
|
||||
add_action( self::BACKGROUND_EVENT_HOOK, array( $this, 'refresh_cache' ) );
|
||||
add_action( 'action_scheduler_ensure_recurring_actions', array( $this, 'schedule_background_actions' ) );
|
||||
// This is a temporary fix to ensure the background actions are scheduled.
|
||||
// @todo: Remove this once the Action Scheduler package is updated to >= 3.9.3.
|
||||
add_action( 'admin_init', array( $this, 'schedule_background_actions' ) );
|
||||
|
||||
if ( defined( 'WC_PLUGIN_BASENAME' ) ) {
|
||||
add_action( 'deactivate_' . WC_PLUGIN_BASENAME, array( $this, 'unschedule_background_actions' ) );
|
||||
|
|
|
@ -152,7 +152,7 @@ class WC_Admin_Tests_Reports_Regenerate_Batching extends WC_REST_Unit_Test_Case
|
|||
)
|
||||
);
|
||||
// Verify that a second follow up action was queued.
|
||||
WC_Helper_Queue::run_all_pending();
|
||||
WC_Helper_Queue::run_all_pending( 'wc-admin-data' );
|
||||
$this->assertCount(
|
||||
2,
|
||||
OrdersScheduler::queue()->search(
|
||||
|
@ -175,7 +175,7 @@ class WC_Admin_Tests_Reports_Regenerate_Batching extends WC_REST_Unit_Test_Case
|
|||
)
|
||||
);
|
||||
// Verify that no follow up action was queued.
|
||||
WC_Helper_Queue::run_all_pending();
|
||||
WC_Helper_Queue::run_all_pending( 'wc-admin-data' );
|
||||
$this->assertCount(
|
||||
0,
|
||||
OrdersScheduler::queue()->search(
|
||||
|
|
|
@ -81,18 +81,6 @@ class WC_Admin_Tests_Install extends WP_UnitTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* By the time we hit this test method, we should have the following cron jobs.
|
||||
* - wc_admin_daily
|
||||
* - generate_category_lookup_table
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_cron_job_creation() {
|
||||
$this->assertNotFalse( wp_next_scheduled( 'wc_admin_daily' ) );
|
||||
$this->assertNotFalse( wp_next_scheduled( 'generate_category_lookup_table' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider that returns DB Update version string and # of expected pending jobs.
|
||||
*
|
||||
|
@ -234,5 +222,4 @@ class WC_Admin_Tests_Install extends WP_UnitTestCase {
|
|||
$this->assertNotFalse( get_option( $new_option ), $new_option );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ global $wpdb, $wp_version, $wc_uninstalling_plugin;
|
|||
|
||||
$wc_uninstalling_plugin = true;
|
||||
|
||||
// Clear WordPress cron events.
|
||||
wp_clear_scheduled_hook( 'woocommerce_scheduled_sales' );
|
||||
wp_clear_scheduled_hook( 'woocommerce_cancel_unpaid_orders' );
|
||||
wp_clear_scheduled_hook( 'woocommerce_cleanup_sessions' );
|
||||
|
@ -26,6 +27,21 @@ wp_clear_scheduled_hook( 'wc_admin_daily' );
|
|||
wp_clear_scheduled_hook( 'generate_category_lookup_table' );
|
||||
wp_clear_scheduled_hook( 'wc_admin_unsnooze_admin_notes' );
|
||||
|
||||
// Clear Action Scheduler events.
|
||||
if ( function_exists( 'as_unschedule_all_actions' ) ) {
|
||||
as_unschedule_all_actions( 'woocommerce_scheduled_sales' );
|
||||
as_unschedule_all_actions( 'woocommerce_cancel_unpaid_orders' );
|
||||
as_unschedule_all_actions( 'woocommerce_cleanup_sessions' );
|
||||
as_unschedule_all_actions( 'woocommerce_cleanup_personal_data' );
|
||||
as_unschedule_all_actions( 'woocommerce_cleanup_logs' );
|
||||
as_unschedule_all_actions( 'woocommerce_geoip_updater' );
|
||||
as_unschedule_all_actions( 'woocommerce_tracker_send_event' );
|
||||
as_unschedule_all_actions( 'woocommerce_cleanup_rate_limits' );
|
||||
as_unschedule_all_actions( 'wc_admin_daily' );
|
||||
as_unschedule_all_actions( 'generate_category_lookup_table' );
|
||||
as_unschedule_all_actions( 'wc_admin_unsnooze_admin_notes' );
|
||||
}
|
||||
|
||||
/*
|
||||
* Only remove ALL product and page data if WC_REMOVE_ALL_DATA constant is set to true in user's
|
||||
* wp-config.php. This is to prevent data loss when deleting the plugin from the backend
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue