[wp-trac] [WordPress Trac] #57924: Cron fires twice
WordPress Trac
noreply at wordpress.org
Fri Mar 17 15:05:37 UTC 2023
#57924: Cron fires twice
-------------------------------------------+------------------------------
Reporter: j3gaming | Owner: j3gaming
Type: defect (bug) | Status: assigned
Priority: normal | Milestone: Awaiting Review
Component: Cron API | Version: 6.1.1
Severity: normal | Resolution:
Keywords: changes-requested 2nd-opinion | Focuses:
-------------------------------------------+------------------------------
Changes (by j3gaming):
* keywords: => changes-requested 2nd-opinion
Comment:
It's not the same microsecond, and it doesn't have to be.
I recorded the $doing_cron_transient variable.
My cron is triggered twice, in just the same second. With different
microtimes.
Here are the logs:
{{{
[17-Mar-2023 00:40:01 UTC] ['1679013601.6444039344787597656250'] START ---
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
--- ---
[17-Mar-2023 00:40:01 UTC] ['1679013601.6444039344787597656250'] Jobs
ready: 1
[17-Mar-2023 00:40:01 UTC] ['1679013601.6447730064392089843750'] START ---
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
--- ---
[17-Mar-2023 00:40:01 UTC] ['1679013601.6447730064392089843750'] Jobs
ready: 1
[17-Mar-2023 00:40:01 UTC] Cron reschedule event error for hook:
crontrol_cron_job, Error code: could_not_set, Error message: The cron
event list could not be saved. true false, Data:
{"schedule":"minutely","args":{"code":"require_once('wp-
content\/cron\/email_send.php');","name":"Send Emails"},"interval":"60"}
[17-Mar-2023 00:40:01 UTC] Cron unschedule event error for hook:
crontrol_cron_job, Error code: could_not_set, Error message: The cron
event list could not be saved. true false, Data:
{"schedule":"minutely","args":{"code":"require_once('wp-
content\/cron\/email_send.php');","name":"Send Emails"},"interval":"60"}
[17-Mar-2023 00:40:01 UTC] Started email_send
[17-Mar-2023 00:40:01 UTC] Started email_send
[17-Mar-2023 00:40:01 UTC] Ended email_send
[17-Mar-2023 00:40:01 UTC] Ended email_send
[17-Mar-2023 00:40:01 UTC] ['1679013601.6447730064392089843750'] --- ---
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
--- END
}}}
Job # 1679013601.6444039344787597656250
and
Job # 1679013601.6447730064392089843750
Starts at nearly the same time. Notice it's not exactly the same. It's
quick enough that both are set before either script can check if something
is set.
Notice how I only have 1 "end"? That's because of this code near the
bottom of the loop:
{{{
// If the hook ran too long and another cron process stole the lock, quit.
if ( _get_cron_lock() !== $doing_wp_cron ) {
return;
}
}}}
The bottom of the loop detected that there was 2 running. It quit one of
them (after executing 1 job twice). The one that quit, was the unix
microtime that was not in the database. The one that made it into the
database gets to continue.
**My fix. wp-config.php**
{{{
// The cron lock: a unix timestamp from when the cron was spawned.
$doing_cron_transient = get_transient( 'doing_cron' );
// Use global $doing_wp_cron lock, otherwise use the GET lock. If no lock,
try to grab a new lock.
if ( empty( $doing_wp_cron ) ) {
if ( empty( $_GET['doing_wp_cron'] ) ) {
// Called from external script/job. Try setting a lock.
if ( $doing_cron_transient && ( $doing_cron_transient +
WP_CRON_LOCK_TIMEOUT > $gmt_time ) ) {
return;
}
$doing_wp_cron = sprintf( '%.22F', microtime( true
) );
$doing_cron_transient = $doing_wp_cron;
set_transient( 'doing_cron', $doing_wp_cron );
} else {
$doing_wp_cron = $_GET['doing_wp_cron'];
}
}
// Wait x seconds before checking the database.
sleep(2); // Could be WP_CONFIG configurable. Default could be 0, anyone
complaining about crons running could set this to 1 or more.
// Grab the true database value after possible racing conditions.
$doing_cron_transient = get_transient( 'doing_cron' );
/*
* The cron lock (a unix timestamp set when the cron was spawned),
* must match $doing_wp_cron (the "key").
*/
if ( $doing_cron_transient !== $doing_wp_cron ) {
return;
}
}}}
--
Ticket URL: <https://core.trac.wordpress.org/ticket/57924#comment:15>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list