How WordPress Scheduled Posts Work

There’s a bit of a misnomer in the WordPress community about scheduled posts that I hope to help clarify.

To start, let’s do a quick overview of the WordPress cron system. It’s a psuedo-cron, meaning that it doesn’t rely on a true linux crontab or the Windows version of scheduled task running. It depends on user activity on the site. If a user visits a page, it triggers a check of the scheduled tasks, and if it finds one that is in the past, it invokes the task callback. If WP_DISABLE_CRON is set to true, then the implicit behavior-based system is disabled and awaits for an explicit call to wp-cron.php. This is really the better option of the two as you can run a curl on the wp-cron.php URL from a true crontab and invoke the scheduler on a cyclical basis from the os task runner.

Scheduled posts use the WordPress cron system to magically publish at approximately the set time. I’ve been developing on WordPress for more than a decade and I had some assumptions as to how this worked. The main assumption I had was that a scheduled post would get picked up in a broad query based on the `future` post_status and then evaluated in a loop during a cron run. This is not the case. This assumption cost me quite a bit of time debugging this past week.

What really happens is that when a post is future published, a cron schedule entry is added to the `cron` option in the options table. This schedule entry is the evaluated when it is ready to be published. It’s pretty simple. Here’s what one of those entries looks like in the cron option array:

[1602878925] => Array
(
    [publish_future_post] => Array
        (
            [2af2e0878689b7c2fa62ccf0765f5768] => Array
                (
                    [schedule] => 
                    [args] => Array
                        (
                            [0] => 25
                        )
                )
        )
)

If this schedule is not present, WordPress is essentially agnostic to the fact that this needs to go live. I ran into an issue where there were hundreds of scheduled posts that were not going live, and I assumed running the cron explicitly would publish them. That did not happen. After some digging in the wp-cron.php file I figured out that each post needs its own scheduled entry in the scheduling system. Some bug somewhere caused the posts to never be truly scheduled. This could happen if a plugin interferes with the publish flow. It’s a bit disheartening when you are left with the possibility of having to manually publish hundreds of posts (especially since some of our custom plugins add meta on post publish).

Don’t fret! I dug up a snippet from the upgrade.php file which should help save the day. This will grab all of the current posts with a `future` post_status and schedule them properly to publish the next time the cron is run.

$posts = $wpdb->get_results("SELECT ID, post_date FROM $wpdb->posts WHERE post_status ='future'");
if ( !empty($posts) ) {
    foreach ( $posts as $post ) {
        wp_schedule_single_event(mysql2date('U', $post->post_date, false), 'publish_future_post', array($post->ID));
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *