Cron & WordPress

Uit De Vliegende Brigade
Naar navigatie springen Naar zoeken springen

The WordPress Cron system is a pseudo-cron system implemented within WordPress to execute scheduled tasks and functions. It doesn't work like a traditional cron job set up on a server, but rather relies on the visits to your WordPress site to trigger scheduled events.

Here's how it generally works:

  • Visitor Access: When a user accesses your WordPress site, WordPress checks if there are any scheduled tasks that need to be executed
  • Task Check: WordPress compares the current time against the scheduled time of tasks stored in the database. Tasks due for execution are identified
  • Task Execution: If a scheduled task's execution time has arrived, WordPress executes the associated function or action
  • Completion and Recurrence: After the task is completed, WordPress reschedules it for the next occurrence based on the recurrence schedule defined for that task.

This system relies heavily on user traffic. If your site doesn’t have regular traffic, scheduled tasks might not execute on time or at all. However, there are ways to address this. For instance, you can set up external services or server-level cron jobs to trigger WordPress's wp-cron.php file at regular intervals. This ensures that scheduled tasks are triggered even if your site doesn’t receive frequent visits.

To manually trigger the WordPress Cron system, you can visit http://yoursite.com/wp-cron.php in your browser or set up an external service to ping this URL at specified intervals.

Keep in mind that WordPress Cron can sometimes cause performance issues, especially on high-traffic sites, as it runs whenever someone visits the site. Some plugins and hosting setups offer options to disable WordPress Cron and set up a server-level cron job for better efficiency.

Internal organisation

Internally, WordPress manages its cron system through a set of functions and classes that interact to handle scheduled tasks. Here’s an overview of the internal organization:

  • wp-cron.php: This file acts as the entry point for WordPress cron jobs. When a request is made to wp-cron.php, it checks the database for scheduled events and triggers the necessary actions
  • WP-Cron Functions: WordPress provides a set of functions to manage cron-related tasks:
    • wp_schedule_event(): This function schedules a recurring event.
    • wp_schedule_single_event(): This schedules a one-time event.
    • wp_unschedule_event(): Used to unschedule an already scheduled event.
    • wp_next_scheduled(): Checks when the next instance of a scheduled event will occur.
    • wp_clear_scheduled_hook(): Clears all scheduled instances of a particular hook.
  • Hooks and Actions: WordPress hooks like wp_cron and init are used to trigger the cron system and process scheduled events respectively. When WordPress loads, it checks for scheduled tasks and hooks necessary functions to be executed at the specified times
  • Cron API: WordPress includes a Cron API that manages the scheduling and execution of events. It maintains a record in table wp_options in the database where it stores the scheduled events with their hook names, schedules, and parameters
  • Execution Process: When a visitor accesses the site, WordPress checks if any cron events are due. If so, it triggers the associated actions. The execution of these tasks occurs within the request, meaning it happens within the scope of the user visit
  • Server Configuration: WordPress relies on site visits to trigger its cron system. However, for high-traffic or critical tasks, it's recommended to set up a server-level cron job that triggers wp-cron.php at regular intervals. This ensures that scheduled tasks are processed even if the site has low traffic.

Cron list

The cron list is the list of all cron jobs. It is stored as one serialized value, in database table wp_options with cron as option_name:

select * from wp_options where option_name="cron"
Cron events, cron list, made visible through the WP Crontrol plugin. Note that this plugin doesn't add cron functionalities; it only makes visible what is already inside WordPress. Some things to note:
  • The jobs with the WordPress logo, are system cron jobs and you cannot change them
  • The other jobs are from plugins, themes or whatever, and you can delete them as you please
  • I think that the orange text in column Next Run (UTC+1) only serves to indicate that these tasks are overdue for some time. Not that there is a problem with execution
  • In this list, all jobs have an associated Action. Sometimes, there are events without action. WP Crontrol offers the possibility to remove such jobs. So, this plugin actually does add some functionality
  • There are some messed-up cron jobs here, although you can't tell by just looking at this list. E.g., the two jetpack jobs have a value for action, but these actions (hooks) don't exist
Content of the field in wp_options that stores the whole cron list in just one field, through SQL: select * from wp_options where option_name="cron". This is the same data as displayed through WP Crontrol
Once more the same list, now through WP-CLI command wp cron event list

As all cron jobs are stored serialised in one field in wp_options, we can retrieve it using wp option get. That's what we did here, for a pruned version of this list, discussed below:

$ wp option get cron
array (
  1701795030 => 
  array (
    'wp_privacy_delete_old_export_files' => 
    array (
      '40cd750bba9870f18aada2478b24840a' => 
      array (
        'schedule' => 'hourly',
        'args' => 
        array (
        ),
        'interval' => 3600,
      ),
    ),
  ),
  1701834630 => 
  array (
    'wp_update_plugins' => 
    array (
      '40cd750bba9870f18aada2478b24840a' => 
      array (
        'schedule' => 'twicedaily',
        'args' => 
        array (
        ),
        'interval' => 43200,
      ),
    ),
  ),
  1701834631 => 
  array (
    'wp_update_themes' => 
    array (
      '40cd750bba9870f18aada2478b24840a' => 
      array (
        'schedule' => 'twicedaily',
        'args' => 
        array (
        ),
        'interval' => 43200,
      ),
    ),
    'wp_version_check' => 
    array (
      '40cd750bba9870f18aada2478b24840a' => 
      array (
        'schedule' => 'twicedaily',
        'args' => 
        array (
        ),
        'interval' => 43200,
      ),
    ),
  ),
  1701834636 => 
  array (
    'wp_update_user_counts' => 
    array (
      '40cd750bba9870f18aada2478b24840a' => 
      array (
        'schedule' => 'twicedaily',
        'args' => 
        array (
        ),
        'interval' => 43200,
      ),
    ),
  ),
  1701869911 => 
  array (
    'wp_site_health_scheduled_check' => 
    array (
      '40cd750bba9870f18aada2478b24840a' => 
      array (
        'schedule' => 'weekly',
        'args' => 
        array (
        ),
        'interval' => 604800,
      ),
    ),
  ),
  1701877829 => 
  array (
    'recovery_mode_clean_expired_keys' => 
    array (
      '40cd750bba9870f18aada2478b24840a' => 
      array (
        'schedule' => 'daily',
        'args' => 
        array (
        ),
        'interval' => 86400,
      ),
    ),
  ),
  1701877837 => 
  array (
    'wp_scheduled_delete' => 
    array (
      '40cd750bba9870f18aada2478b24840a' => 
      array (
        'schedule' => 'daily',
        'args' => 
        array (
        ),
        'interval' => 86400,
      ),
    ),
    'delete_expired_transients' => 
    array (
      '40cd750bba9870f18aada2478b24840a' => 
      array (
        'schedule' => 'daily',
        'args' => 
        array (
        ),
        'interval' => 86400,
      ),
    ),
  ),
  'version' => 2,
)

Cron Schedule

The terms cron list and cron schedule are used interchangeably. However, cron schedule also has a specific meaning: the list of intervals that is used by cron - Don't ask me why they didn't call it intervals or so. E.g.:

$ wp cron schedule list

+------------+-------------+----------+
| name       | display     | interval |
+------------+-------------+----------+
| hourly     | Once Hourly | 3600     |
| twicedaily | Twice Daily | 43200    |
| daily      | Once Daily  | 86400    |
| weekly     | Once Weekly | 604800   |
+------------+-------------+----------+

Details of a cron job

Let's have a closer look at a cron job. We'll start at the the usual cron list:

$ wp cron event list
+------------------------------------+---------------------+---------------------+------------+
| hook                               | next_run_gmt        | next_run_relative   | recurrence |
+------------------------------------+---------------------+---------------------+------------+
| recovery_mode_clean_expired_keys   | 2023-12-05 15:50:29 | now                 | 1 day      |
| wp_privacy_delete_old_export_files | 2023-12-05 15:50:30 | now                 | 1 hour     |
| wp_update_plugins                  | 2023-12-05 15:50:30 | now                 | 12 hours   |
| wp_update_themes                   | 2023-12-05 15:50:31 | now                 | 12 hours   |
| wp_version_check                   | 2023-12-05 15:50:31 | now                 | 12 hours   |
| wp_update_user_counts              | 2023-12-05 15:50:36 | now                 | 12 hours   |
| wp_scheduled_delete                | 2023-12-05 15:50:37 | now                 | 1 day      |
| delete_expired_transients          | 2023-12-05 15:50:37 | now                 | 1 day      |
| wp_site_health_scheduled_check     | 2023-12-06 13:38:31 | 21 hours 37 minutes | 1 week     |
+------------------------------------+---------------------+---------------------+------------+

"now"?

The usual cron list in WP Crontrol, just after wp-cron.php has been invoked. It seems that all jobs nicely executed: There are no yellow texts with exlamation mark in column Next Run (UTC+1) which seems to indicate that there is something wrong, maybe jobs that are long overdue, or that haven't finished as expected?

Note that in the table above, 8 out of 9 tasks have now for next_run_relative:

When the value in next_run_relative is "now", it means that the task is scheduled to run at the next available opportunity, which could be very soon or almost immediately. WordPress attempts to execute tasks marked as "now" as soon as the system gets a chance, usually triggered by a site visit or when the cron system checks for pending tasks.

Tasks listed with "now" in next_run_relative are often transient, triggered on-demand, and might not stay in the scheduled list for long, as they'll execute promptly when the cron system checks and detects them as due.

As an example: Let's invoke file wp-cron.php (through someting like https://example.com/wp-cron.php. Now the cron list looks quite differently (and you can tell that this site is currently not visited):

$ wp cron event list
+------------------------------------+---------------------+-----------------------+------------+
| hook                               | next_run_gmt        | next_run_relative     | recurrence |
+------------------------------------+---------------------+-----------------------+------------+
| wp_privacy_delete_old_export_files | 2023-12-05 16:50:30 | 38 minutes 27 seconds | 1 hour     |
| wp_update_plugins                  | 2023-12-06 03:50:30 | 11 hours 38 minutes   | 12 hours   |
| wp_update_themes                   | 2023-12-06 03:50:31 | 11 hours 38 minutes   | 12 hours   |
| wp_version_check                   | 2023-12-06 03:50:31 | 11 hours 38 minutes   | 12 hours   |
| wp_update_user_counts              | 2023-12-06 03:50:36 | 11 hours 38 minutes   | 12 hours   |
| wp_site_health_scheduled_check     | 2023-12-06 13:38:31 | 21 hours 26 minutes   | 1 week     |
| recovery_mode_clean_expired_keys   | 2023-12-06 15:50:29 | 23 hours 38 minutes   | 1 day      |
| wp_scheduled_delete                | 2023-12-06 15:50:37 | 23 hours 38 minutes   | 1 day      |
| delete_expired_transients          | 2023-12-06 15:50:37 | 23 hours 38 minutes   | 1 day      |
+------------------------------------+---------------------+-----------------------+------------+

Any details?

All that WP Crontrol can tell about this cron job. It's basically just a hook that gets executed when the time is right

Let's delve into this first cron job, wp_privacy_delete_old_export_files and see what's more.

Above, the field in wp_options that contains the cron list, was explored using wp option get cron. Let's use wp option pluck to zoom on on this one job:

$ wp option pluck cron 1701795030

array (
  'wp_privacy_delete_old_export_files' => 
  array (
    '40cd750bba9870f18aada2478b24840a' => 
    array (
      'schedule' => 'hourly',
      'args' => 
      array (
      ),
      'interval' => 3600,
    ),
  ),
)

It doesn't seem to get more interesting than this. I think those hex values are just identifiers. Again: Just the name of a hook and something about timing.

Can you just delete all cron jobs?

At some sites, we seem to have some issues with an ever-growing cron list, especially with some stale jobs. Can't I just remove them all? Won't the plugins eventually fix stuff if needed? Or not, because they will never get executed again anymore?

Maybe that could be done with just one satisfying WP-CLI command, like (works, eventhough there is a small error):

wp cron event list --fields=hook --format=csv | xargs -I % wp cron event delete % 

See also