While dealing with server slowdowns, I'm getting into a relatively little known function in WordPress: wp-cron and what it is doing behind the scenes. A cron is a subroutine that performs future tasks according to a schedule, so it's necessary to make sure it is running and running well. It's also good to lighten the load on your server to ensure the best experience for your visitors. There are reports of wp-cron sometimes using a lot of CPU; which happened to us a couple of days back and brought the server to it knees. Everything was going along as normal then BOOM! the server load spiked through the roof: Apache had 500+ connections open, CPU at 100%, physical RAM exhausted, swap space nearly full before it finally died down. During that time, the blogs exhibited Error Connecting To Database in the browser. All blogs were off line. Not good.
Checking the Apache access logs, I found that just before the server went wheels up, wp-cron had run. OK, what caused that? A couple of lines up, there was an access from YandexBot, a crawler from Russia that is not known for its good behavior. Could be it. I blocked it temporarily to see what happens.
Just following the crawler access, there was a line from Feedburner. Going through the logs again looking for correlations, both Yandex and Feedburner show up so many times and right before wp-cron that it is difficult to tell which may be causing the wp-cron spawn; but the pattern is too prevalent to miss — or dismiss.
There are 5 blogs active, 4 with 11 scheduled cron runs each and one with 13 - totaling 57 scheduled runs in a 24 hour period. Yet, wp-cron ran 305 times the day the server choked. No posts were added or updated during this period and none were set for posting at a later date; both of which would spawn a cron run. There are spurious jobs that pop up from time to time; tweetbacks, for instance, runs a (now) job every so often but not a whole bunch. Maybe we have a smoking gun. Unsure which gun, yet, but there's definitely gunpowder in the air.
WordPress core developer Andrew Nacin visited my thread on the support forums and offered some valuable insight:
The cron gets spawned via a loopback HTTP request, which in turn is triggered by standard pageloads. Feedburner has a knack for hitting your blog at the times that the cron needs to be spawned. (Having the cron triggered by feed readers and search engine crawlers is rather common, as they make up a good portion of HTTP requests, depending on the size of the site.)
Installing wp-crontrol, a plugin by Edward Dale, to see WordPress internal cronjobs that are scheduled. (Note: the plugin is circa 2008 and compatible up to v2.5.1. In order to make it partially compatible with >= v2.7, visit this thread. After making this edit, there have been no side effects in v3.0.1 that I am aware of.)
Running it, I find that there are a number of jobs that really could be run only by the main site. Each sub-site has its own cronjobs that check: wp_update_plugins, wp_update_themes and wp_version_check, for instance, twice a day each. These only need to be run once by the main site; not by each and every sub-site in the network; they're redundant. 24 of these can be eliminated without any real loss in functionality. With the above condition of crawlers and Feedburner spawning crons with each access, reducing scheduled runs can only be a Good Thing.
Is there a way to do this?
Andrew had this to share:
I agree that the update checks should only be running on the main blog in the network. There is a related issue I discovered yesterday about this, and the ultimate solution will be to limit update checks to the server. That said, 3.0 is actually (unwittingly) way better at this than MU was. I plan to address this in 3.1.
He also pointed me to a couple of DIFF patches in Trac, which I applied. They don't directly speak to this situation but I committed them, so as to keep current with the state of the code.
Having a viewpoint from someone who sees WP from deep within the code is invaluable. There might be something further I can do with this. Using wp-crontrol again, I set the wp_version_check, wp_update_themes and wp_update_plugins cronjobs on only the sub-sites to "Non-repeating", leaving them as recurring on the main site. Didn't know if these changes would be sticky but one never knows until (s)he tries. Today, I fired up wp-crontrol again. The sub-site cronjobs do not return after running the one final time. Until the core is updated to add this efficiency, this will work just fine. Thanks, Andrew!
If you have a lot of sites, this can save a lot of wp-cron runs. Curious thing is, with these sub-site crons now out of the queue, and only the main site running them, the sub-sites still display updates of plugins and themes as they become available; as if you'd never changed anything. I'd say that makes them really redundant. Either that, or they're badly mis-named for their function.