Jan 29

Run WordPress tasks from real cron job

Category: How-To
Tags: ,,

I had to pull off my sleeves, open WP code and see by myself because all I finally found was this article (well I must admit that it was very helpful):
http://wordpress.org/support/topic/273381

To explain, in short, how WordPress crons are working, here is a shortened call stack:

  • Web User Request
  • => WP API initialisation
  • ==> wp_cron()  // if authorized to run, the cron array is loaded (from a get_option call)
  • ===> spawn_cron()     // this is called if a job pending execution is found in the cron array.
  • ====> wp_remote_post(‘wp-cron.php?doing_wp_cron’)     // the asynchronous call

wp_cron() is the function that initiate the process of executing the jobs if one needs to be executed.  And wp-cron.php is the one that will, at the end, execute those jobs.

wp_cron function code

In order to run the jobs from a real cron, it is simple as scheduling a wget call to wp-cron.php and disabling PHP calls to wp_cron() function.  Damn, if I knew that from beginning…  When you open the wp_cron() function declaration, you will notice that first line checks for a constant definition: DISABLE_WP_CRON.  So you define(‘DISABLE_WP_CRON’, true) in your wp-config.php and that’s it.

Well, if you are using WordPress MU, that’s not totally it. Remember the comments in the stack call above? The cron array is loaded from a function call to get_option.  In WordPress MU, get_option reads data from wp_xx_options which is per blog database table.  So it has a set of scheduled tasks per blog.  So you need to call wp-cron.php in the context of each blog.  So it’s starting to bug me… I don’t want to do maintenance tasks everytime a blog is created in WordPress MU!  To prevent this, I created a small php script that I called wp-cron-multi-blog.php that is calling wp-cron.php for each active blogs defined in WordPress MU.

Here is a wrap-up of the information without the bells and whistles.  To run WP tasks from a real scheduled cron you should do this:

in wp-config.php, add the following line :

define('DISABLE_WP_CRON', true);

for a standard WordPress installation, configure a crontab that:

  • wget http://www.server.com/wp-cron.php?doing_wp_cron

for a WordPress MU installation, configure a crontab per blog like:

  • wget http://www.server.com/wp-cron.php?doing_wp_cron
  • wget http://www.server.com/whatever/wp-cron.php?doing_wp_cron
  • wget http://www.server.com/what-a-blogger/wp-cron.php?doing_wp_cron
  • ...

or, a single crontab calling my wp-cron-multi-blog.php (placed in root installation of WP):

  • wget http://www.server.com/wp-cron-multi-blog.php

A final note about wp-cron-multi-blog.php.  If your WordPress MU installation has a lot of blogs and each of them tends to have many time consuming tasks (ex: cache cleaning), it may be a good idea to edit this script in order to limit the number of blogs to run for at one time.

Hope this helps.

Pascal.

Pages: 1 2

30 comments

30 Comments so far

  1. DS February 9th, 2010 11:17 am

    I implemented this in WPMU 2.9.1.1 with the wp-cron-multi-blog.php script and a /etc/crontab setup and it does not work at all for me. If I manually browse to the script, it reports back as if its working/doing something, but doesn’t appear to actually spawn the cron jobs.

  2. Pascal February 9th, 2010 9:12 pm

    Ok I will double-check it in case I did a copy/paste mistake.

    What are your cron jobs skipped? pre-publishing?

    Pascal.

  3. Helgi Hrafn Halldórsson February 17th, 2010 4:07 am

    Thank you for this post. It helped me alot :P

  4. מרק ק. March 3rd, 2010 10:43 pm

    You can probably make your script more efficient by managing the list of scheduled task, or the blog which has them, in a separate DB table.

    Another enhancement might be to directly call the WP cron task execution API, after calling switch_to_blog for the appropriate blog number.

  5. Audun April 13th, 2010 4:09 am

    How often do you run this crontab? Every minute? Every hour? If you turn it of on a standard wp-mu instance.. What will stop working? Only posting set to be published in the future?

  6. Pascal April 13th, 2010 2:21 pm

    Hi,

    I run it every 30 minutes. From what I have seen, it is used for publishing in future and cache clean-up. So the good answer is it depends…
    It depends on the plugins you are using. Some plugins (like caching) sets scheduled tasks. So based on your plugins and the latency (for publishing future posts for example) you are willing to accept, you should set the longest period as possible. It is possible to run it every minutes as long as it doesn’t run over 1 minute. Otherwise it may overlap and start overloading your server.

    Pascal.

  7. maheshwar April 28th, 2010 2:19 am

    Hi,

    Can anyone suggest that how can I stop the execution of a cron through page load for a particular plugin not for whole wordpress.
    Actually, I want to run cron script through cron job.
    I would appreciate your suggestion.

  8. MPopovici April 30th, 2010 7:06 am

    Thanks for writing this. The line which disables wp-cron looks like its doing wonders for my sites, at least for now..

  9. [...] here: Pascal’s blog » Run WordPress tasks from real cron job » page 2 Tags: [...]

  10. roman May 31st, 2010 11:16 am

    Thanks for this good post!

    I’ve searched long to get this information.
    But I’m wondering why the cronjob isn’t directly triggered like this:

    /path/to/php/cli/cli/php -f /path/to/wordpress/wp-cron.php

    in my php experince I always call cron jobs like this – i use wordpress since three months and haven’t found no page or post where this practice is used?

  11. Pascal May 31st, 2010 8:25 pm

    Hi,

    For a standard WordPress installation, you are right. And I would prefer that way instead of the wget method. But for a WordPress MU installation, that’s different because the API determine the current blog context based on the URI, so it cannot run from command line PHP.

    Pascal.

  12. [...] the original here: Pascal’s blog » Run WordPress tasks from real cron job Tags: disable-wp-cron, good-explanation, [...]

  13. Binh Nguyen June 28th, 2010 11:06 pm

    Thanks for this. I spent days looking for a solution for every minute cron run.

    Well I use WPMU so also the php command didn’t work. I didn’t know I must use the wget command.

    Bravo!

  14. Dan O'Neil July 5th, 2010 2:25 pm

    Thank you, thank you, thank you. Switched to a standard cron job and my scheduled posts and all the other stuff is now working beautifully… saved me a big headache!

  15. Jason Potkanski July 6th, 2010 11:36 am

    Change wp_remote_post line to:

    switch_to_blog($blog->blog_id);
    wp_cron();
    restore_current_blog();

    Will run much faster.

  16. Pascal July 6th, 2010 5:21 pm

    I will definitly take a look at this switch_to_blog function and update my post accordingly.

    Thanks!
    Pascal.

  17. Len July 15th, 2010 5:45 pm

    I just upgraded to WP 3.0 and am having issues with the wp-cron.php spiking my vps. I am looking at solutions like yours but wondering about the new multisite function in WP.

    Will this need to be run like the MU example, and what difference in setup with sub-domain or sub-directory.

    Thanks,
    Len

  18. Pascal July 17th, 2010 12:39 pm

    I haven’t try WordPress 3.0 yet but I guess it would be like WordPress MU.

    Also consider doing the change stated by Jason Potkanski above.

    Pascal.

  19. Rick September 5th, 2010 10:02 am

    Pascal:

    I tried your fix however when I put in line of code
    define(‘DISABLE_WP_CRON’, true);
    I get blank page on my wp admin panel that states “syntax error line 1″ which causes me to have to go back into wp-config.php file and delete that line of code. As a result I am unable to disable the wp-cron….Any other suggestions on how I can make this work?

    Thanks,

    Rick

  20. Pascal September 14th, 2010 7:52 pm

    Looks like you are breaking the PHP code.

    Syntax error on line 1… hum did you put the define before the “

    Pascal.

  21. DrLightman October 1st, 2010 4:53 am

    @Rick : be aware if you copied the DEFINE from here, the quotes getting copied are not the ones PHP like. You have to manually edit them (I’m talking about the single charachters preceding and after DISABLE_WP_CRON)

  22. chovy October 8th, 2010 1:06 am

    I’m not sure how relevant this is with WP3.0

    w3 total cache is what’s killing my cpu with this internal wp-cron.php garbage.

  23. q292u November 3rd, 2010 7:32 am

    One of my sites (www.supercars-for-sale.com) is on a free hosting account.
    It kept getting suspended due to excessive resource use. (Feedwordpress plugin on wordpress..)
    I’ve switched fwp to manual/cron option, AND used the disable_wp_cron setting. If it stays up 24 hours I’m going to look at implementing a Non-MU version of your script. (I’ve already written it.).
    I’ll let you know what happens..

  24. Joe Brewer November 24th, 2010 6:45 am

    I’ve successfully implemented this on one of my sites, however it is leaving a load of zero byte files in my root folder. Is there anything I can add after the wget command so that the zero byte files are not saved. If there is anything in the files, then I would need to see that for debugging purposes.

    Thanks for the useful post.

  25. Joe Brewer November 26th, 2010 3:13 pm

    I’ve worked it out! I should have looked at the manual for wget, but anyway… Here is what I have:

    wget http://www.domain.com/wp-cron.php?doing_wp_cron –output-document=cron.txt

    Hope someone finds it useful.

  26. Forumcivico March 19th, 2011 9:15 am

    @Joe Brewer

    Just add this:

    wget http://domain.tld/wp-cron-multi-blog.php > /dev/null 2>&1

    For the /dev/null 2>&1 part:

    If one run a cron job, any output that the script would send to stderr is sent as a mail to the owner of the script, which can be a tad annoying if the script runs regularily, with output you don’t need or want, or can do anything about.

    The graceful way to handle this in any shell is as follows: Redirect the output of stderr to stdout, and then redirect this combined output to /dev/null

  27. Michael April 6th, 2011 8:44 am

    Simple but brilliant. Thank you. Your idea saved me hours.

  28. tony May 6th, 2011 12:23 pm

    Thanks for sharing your research and script. I was having the same issue you experienced using WP-Supercache with mod_rewrite caching not triggering garbage collection, and future posts failing to publish with “schedule missed” errors.

  29. arena August 9th, 2011 10:44 pm

    Hi,

    Thank you for this post.

    Here is a new version of wp-cron-multi-blog.php

    http://goo.gl/Tz9GC

  30. Sam Kear August 13th, 2011 10:42 pm

    Thanks for the detailed information! The WordPress developers should revisit how they implement cron in core.

Leave a comment