diff --git a/features/cron-event.feature b/features/cron-event.feature index 19186500..648a71f4 100644 --- a/features/cron-event.feature +++ b/features/cron-event.feature @@ -244,3 +244,31 @@ Feature: Manage WP Cron events """ Debug: Beginning execution of cron event 'wp_version_check' """ + + Scenario: --due-now respects the doing_cron transient and skips when another run is in progress + When I run `wp cron event schedule wp_cli_test_event_lock now hourly` + Then STDOUT should contain: + """ + Success: Scheduled event with hook 'wp_cli_test_event_lock' + """ + + # Simulate an in-progress cron run by setting the doing_cron transient. + When I run `wp eval 'set_transient( "doing_cron", sprintf( "%.22F", microtime( true ) ) );'` + + And I try `wp cron event run --due-now` + Then STDERR should contain: + """ + Warning: A cron event run is already in progress; skipping. + """ + And STDOUT should not contain: + """ + wp_cli_test_event_lock + """ + + # After the transient is cleared, the run should proceed normally. + When I run `wp eval 'delete_transient( "doing_cron" );'` + And I try `wp cron event run --due-now` + Then STDOUT should contain: + """ + Executed the cron event 'wp_cli_test_event_lock' + """ diff --git a/features/cron.feature b/features/cron.feature index 946b3c5e..7656a1c5 100644 --- a/features/cron.feature +++ b/features/cron.feature @@ -307,8 +307,13 @@ Feature: Manage WP-Cron events and schedules # executes the "wp_privacy_delete_old_export_files" event there. @require-wp-5.0 Scenario: Run currently scheduled events + # Disable web-triggered cron so spawn_cron() cannot set doing_cron between + # steps - wp cron event run always defines DOING_CRON so this has no effect + # on the commands being tested. + When I run `wp config set DISABLE_WP_CRON true --raw` + # WP throws a notice here for older versions of core. - When I try `wp cron event run --all` + And I try `wp cron event run --all` Then STDOUT should contain: """ Executed the cron event 'wp_version_check' diff --git a/src/Cron_Event_Command.php b/src/Cron_Event_Command.php index 263deb64..ca56e3a7 100644 --- a/src/Cron_Event_Command.php +++ b/src/Cron_Event_Command.php @@ -226,7 +226,8 @@ public function schedule( $args, $assoc_args ) { * : One or more hooks to run. * * [--due-now] - * : Run all hooks due right now. + * : Run all hooks due right now. Respects the doing_cron transient to + * prevent overlapping runs. * * [--exclude=] * : Comma-separated list of hooks to exclude. @@ -243,10 +244,27 @@ public function schedule( $args, $assoc_args ) { * Success: Executed a total of 2 cron events. */ public function run( $args, $assoc_args ) { + $due_now = Utils\get_flag_value( $assoc_args, 'due-now' ); + + $doing_cron_value = null; + + if ( $due_now ) { + $lock_timeout = defined( 'WP_CRON_LOCK_TIMEOUT' ) ? WP_CRON_LOCK_TIMEOUT : 60; + $doing_cron_transient = get_transient( 'doing_cron' ); + if ( is_numeric( $doing_cron_transient ) && (float) $doing_cron_transient > microtime( true ) - $lock_timeout ) { + WP_CLI::warning( 'A cron event run is already in progress; skipping.' ); + return; + } + $doing_cron_value = sprintf( '%.22F', microtime( true ) ); + set_transient( 'doing_cron', $doing_cron_value, $lock_timeout ); + } $events = self::get_selected_cron_events( $args, $assoc_args ); if ( is_wp_error( $events ) ) { + if ( $due_now && get_transient( 'doing_cron' ) === $doing_cron_value ) { + delete_transient( 'doing_cron' ); + } WP_CLI::error( $events ); } @@ -263,6 +281,10 @@ public function run( $args, $assoc_args ) { } } + if ( $due_now && get_transient( 'doing_cron' ) === $doing_cron_value ) { + delete_transient( 'doing_cron' ); + } + $message = ( 1 === $executed ) ? 'Executed a total of %d cron event.' : 'Executed a total of %d cron events.'; WP_CLI::success( sprintf( $message, $executed ) ); }