Skip to content

Commit

Permalink
First try at fixing cron locks
Browse files Browse the repository at this point in the history
  • Loading branch information
schlessera committed Jan 12, 2022
1 parent f0f8537 commit 4800f1a
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 21 deletions.
6 changes: 3 additions & 3 deletions features/cron-event.feature
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ Feature: Manage WP Cron events
When I try `wp cron event run --all`
Then STDOUT should contain:
"""
Success: Executed a total of
Success: Executed
"""

When I run `wp cron event run --due-now`
Then STDOUT should contain:
"""
Executed a total of 0 cron events
Executed 0 out of
"""

When I run `wp cron event schedule wp_cli_test_event_1 now hourly`
Expand All @@ -36,7 +36,7 @@ Feature: Manage WP Cron events
"""
And STDOUT should contain:
"""
Executed a total of 1 cron event
Executed 1 out of
"""

@require-wp-4.9.0
Expand Down
14 changes: 7 additions & 7 deletions features/cron.feature
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Feature: Manage WP-Cron events and schedules
| hook | recurrence |
| wp_cli_test_event_3 | Non-repeating |

When I run `wp cron event run wp_cli_test_event_3`
When I try `wp cron event run wp_cli_test_event_3`
Then STDOUT should not be empty

When I run `wp cron event list`
Expand Down Expand Up @@ -95,7 +95,7 @@ Feature: Manage WP-Cron events and schedules
"""
And STDOUT should contain:
"""
Success: Executed a total of 2 cron events.
Success: Executed 2 out of
"""

When I run `wp cron event list`
Expand Down Expand Up @@ -214,7 +214,7 @@ Feature: Manage WP-Cron events and schedules
"""
And STDOUT should contain:
"""
Success: Executed a total of 2 cron events.
Success: Executed 2 out of
"""

# WP throws a notice here for older versions of core.
Expand Down Expand Up @@ -253,13 +253,13 @@ Feature: Manage WP-Cron events and schedules
"""
And STDOUT should contain:
"""
Success: Executed a total of
Success: Executed
"""

When I run `wp cron event run --due-now`
Then STDOUT should contain:
"""
Executed a total of 0 cron events
Executed 0 out of
"""

When I run `wp cron event schedule wp_cli_test_event_1 now hourly`
Expand All @@ -275,13 +275,13 @@ Feature: Manage WP-Cron events and schedules
"""
And STDOUT should contain:
"""
Executed a total of 1 cron event
Executed 1 out of
"""

When I run `wp cron event run --due-now`
Then STDOUT should contain:
"""
Executed a total of 0 cron events
Executed 0 out of
"""

Scenario: Don't trigger cron when ALTERNATE_WP_CRON is defined
Expand Down
81 changes: 70 additions & 11 deletions src/Cron_Event_Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,7 @@ public function schedule( $args, $assoc_args ) {
WP_CLI::warning( 'Numeric keys should be used for the hook arguments.' );
}

$hook = $args[0];
$next_run = Utils\get_flag_value( $args, 1, 'now' );

$recurrence = Utils\get_flag_value( $args, 2, false );

if ( empty( $next_run ) ) {
Expand Down Expand Up @@ -261,15 +260,23 @@ public function run( $args, $assoc_args ) {

$executed = 0;
foreach ( $events as $event ) {
$start = microtime( true );
$result = self::run_event( $event );
$total = round( microtime( true ) - $start, 3 );
$executed++;
WP_CLI::log( sprintf( "Executed the cron event '%s' in %ss.", $event->hook, $total ) );
$start = microtime( true );
$success = self::run_event( $event );
$total = round( microtime( true ) - $start, 3 );
if ( $success ) {
$executed++;
WP_CLI::log( sprintf( "Executed the cron event '%s' in %ss.", $event->hook, $total ) );
}
}

$message = ( 1 === $executed ) ? 'Executed a total of %d cron event.' : 'Executed a total of %d cron events.';
WP_CLI::success( sprintf( $message, $executed ) );
WP_CLI::success(
sprintf(
'Executed %d out of %d %s.',
$executed,
count( $events ),
Utils\pluralize( 'cron event', count( $events ) )
)
);
}

/**
Expand Down Expand Up @@ -326,12 +333,28 @@ public function unschedule( $args, $assoc_args ) {
* @return bool Whether the event was successfully executed or not.
*/
protected static function run_event( stdClass $event ) {

if ( ! defined( 'DOING_CRON' ) ) {
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound -- Using native WordPress constant.
define( 'DOING_CRON', true );
}

$gmt_time = microtime( true );

$doing_wp_cron = static::get_cron_lock();

if ( $doing_wp_cron
&& ( $doing_wp_cron + WP_CRON_LOCK_TIMEOUT > $gmt_time ) ) {
WP_CLI::warning( "Active cron lock found, skipping execution of {$event->hook}." );
return false;
}

$doing_wp_cron = sprintf( '%.22F', microtime( true ) );

$success = set_transient( 'doing_cron', $doing_wp_cron );
if ( ! $success ) {
WP_CLI::warning( 'Could not persist new cron lock.' );
}

if ( false !== $event->schedule ) {
$new_args = array( $event->time, $event->schedule, $event->hook, $event->args );
call_user_func_array( 'wp_reschedule_event', $new_args );
Expand All @@ -342,8 +365,11 @@ protected static function run_event( stdClass $event ) {
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound -- Can't prefix dynamic hooks here, calling registered hooks.
do_action_ref_array( $event->hook, $event->args );

return true;
if ( static::get_cron_lock() === $doing_wp_cron ) {
delete_transient( 'doing_cron' );
}

return true;
}

/**
Expand Down Expand Up @@ -528,4 +554,37 @@ private function get_formatter( &$assoc_args ) {
return new \WP_CLI\Formatter( $assoc_args, $this->fields, 'event' );
}

/**
* Retrieves the cron lock.
*
* Returns the uncached `doing_cron` transient.
*
* Copied from WordPress Core `wp-cron.php` file.
*
* @global wpdb $wpdb WordPress database abstraction object.
*
* @return string|false Value of the `doing_cron` transient, 0|false otherwise.
*/
private static function get_cron_lock() {
global $wpdb;
$value = 0;
if ( wp_using_ext_object_cache() ) {
/*
* Skip local cache and force re-fetch of doing_cron transient
* in case another process updated the cache.
*/
$value = wp_cache_get( 'doing_cron', 'transient', true );
} else {
$row = $wpdb->get_row(
$wpdb->prepare(
"SELECT option_value FROM {$wpdb->options} WHERE option_name = %s LIMIT 1",
'_transient_doing_cron'
)
);
if ( is_object( $row ) ) {
$value = $row->option_value;
}
}
return $value;
}
}

0 comments on commit 4800f1a

Please sign in to comment.