I recently worked with a Wordpress MU installation. As an ease of deployment and probably because it is largely used on shared hosting which may not offer access to cron jobs, Wordpress default way of processing its scheduled tasks is through front-end and admin page requests. As a result, everytime a page is requested, the system looks to see if there are jobs to run. When so a call is performed to execute the tasks. The asynchronous nature of the call makes it somewhat transparent from the webuser perspective.
Although it works well, I do not feel confident to use this mechanism on a site with a lot of traffic. After some google searches to find out how it works, I’ve read many stories about hosting companies denying access to wp-cron.php because of bad impact on their server.
I had another issue while using WP-SuperCache. Since the super cache is super because it prevents loading the whole PHP engine for guest requests. Unfortunately, no php code execution means no cron execution for super cached pages… Since the cron is responsible of cleaning the super cached files, they are served indefinetly or until a user logs in (logged users do not get super cached files).
So for all the good reason I had, I wanted to run Wordpress cron jobs from a real cron schedule.
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):
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.
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 :
for a standard Wordpress installation, configure a crontab that:
for a Wordpress MU installation, configure a crontab per blog like:
or, a single crontab calling my wp-cron-multi-blog.php (placed in root installation of WP):
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.
(sorry about the links)