WordPress.org

Make WordPress Core

#42866 closed defect (bug) (worksforme)

WordPress Community Scheduler Interval Bug

Reported by: mrperl Owned by:
Milestone: Priority: normal
Severity: normal Version: 4.4.2
Component: Cron API Keywords:
Focuses: administration Cc:

Description

When scheduling tasks using the WordPress API at 15 minute (900 seconds) intervals, tasks often run early by about 30 seconds.

This shouldn't happen, and indicates a bug in the task scheduler that's fairly serious. Late I would understand, but early doesn't make any sense.

Below is a log fragment showing several entries so you can calculate the times. Each event should be 900 or more seconds apart, yet several are 830'ish.

Here is the source code:
https://github.com/jamesbriggs/rackping-wordpress-widget/blob/master/rackping/rackping.php

Thanks, James.

[2017-12-11 04:08:01 UTC] update ok
[2017-12-11 04:22:17 UTC] Files are less than 900 seconds old, returning.
[2017-12-11 04:38:01 UTC] update ok
[2017-12-11 04:52:18 UTC] Files are less than 900 seconds old, returning.
[2017-12-11 05:08:02 UTC] update ok
[2017-12-11 05:22:33 UTC] Files are less than 900 seconds old, returning.
[2017-12-11 05:38:01 UTC] update ok
[2017-12-11 05:53:02 UTC] update ok
[2017-12-11 06:08:01 UTC] Files are less than 900 seconds old, returning.

Change History (1)

#1 @dd32
18 months ago

  • Milestone Awaiting Review deleted
  • Resolution set to worksforme
  • Status changed from new to closed

Hi @mrperl and welcome to Trac.

It appears that cron is working as expected here to me - although I can see why you'd see differently.

The WordPress cron is a best-effort scheduler, and not a guaranteed scheduler, it runs based on non-cached visits to WordPress in most situations.

Here's an example of what I think is happening here:

  • I schedule a job to run every 15 minutes, starting at 00:00:00.
  • At 00:00:01 a request hits my WordPress site, it see's that I've got a cron task that needs running. WordPress spawns a process to run that job.
  • At 00:00:02 the loopback HTTP request WordPress has made hits PHP and the cron task is run. The task takes 2 seconds to run. At 00:00:04 WordPress notices that it's a recurring job, it schedules the job to run again - $scheduled_time + $interval = 00:15:00 - in 14m56s
  • A bunch of HTTP requests hit a caching plugin, WordPress never loads.
  • At 00:14:55 another request hits WordPress, no scheduled jobs need running.
  • A bunch of HTTP requests hit a caching plugin, WordPress never loads.
  • At 00:16:30 the next HTTP request hits WordPress, WordPress see's that a job should've run at 00:15:00, it spawns cron via HTTP loopback. It's been 16m24s since the last cron ran. *Cron runs late*
  • At 00:16:32 the cron task runs, it takes 30s this time due to a slow network. At 00:17:02 the task has finished and WordPress schedules the task to run again - $scheduled_time + $interval = 00:30:00 - in 12m58s
  • A bunch of HTTP requests hit a caching plugin, WordPress never loads.
  • At 00:30:03 the next HTTP request hits WordPress, WordPress see's that a job should've run at 00:30:00, it spawns cron via HTTP loopback. It's been 13m01s since the last cron ran. *Cron runs "early"*
  • At 00:30:04 the cron task runs, it takes 1s. At 00:30:05 the task has finished and WordPress schedules the task to run again - $scheduled_time + $interval = 00:45:00 - in 14m:55s

.. etc.

In order to improve cron spawning, some people have been known to run a real cron task which hits site.com/wp-cron.php regularly - however this has the downside that it can cause cron tasks to double up (as it bypasses the WordPress cron locks).
There also exist external cron daemons which can be used to run tasks more regularly, such as Cavalcade.

But the best option here, is to simply expect that the cron task will never run on the exact schedule, it may run faster or slower depending on when cron tasks are scheduled and the amount of visitors to WordPress - If no-one visits WordPress, the cron jobs will never even run for example.
It's also possible that the job may run multiple times if the job takes too long to finish, as WordPress may think the cron task failed to execute.

If you wish for your cron tasks to run at least 15 minutes after the time the last job finished you may wish to consider not using a recurring cron task, and instead schedule a task to run in 15 minutes from now at the end of your job, for example:

add_action( "init", "job_setup" );
function job_setup() {
  if ( ! wp_next_scheduled( 'my_job' ) ) {
    wp_schedule_single_event( "my_job", time() );
  }
}

add_action( "my_job_name", "job_callback" );
function job_callback() {
  // do stuff that takes 1s ~ 15 minutes
  // ... 
  // Schedule this to run again in 15+ minutes time that his job finished at
   wp_schedule_single_event( "my_job", time() + 15 * 60 * 60 );
}

I'd recommend that you always check if the job is scheduled though and not just do it on activation, as the job may fail or PHP timeout and it never gets to the reschedule event.

I'm marking this as worksforme as it seems like it's working as expected for me, I hope this reply has helped you understand what you're most likely seeing.

Note: See TracTickets for help on using tickets.