Debugging the Drupal cron

A Blog Post by

Debugging the Drupal cron can be a pain. I covered a few techniques in the Drupal debugging like a pro post, but I wanted to focus on cron specifically for this one.

So you have a site and the cron gets stuck and you get error messages like these:

  • "Attempting to re-run cron while it is already running."
  • "Cron run failed."
  • "Cron run exceeded the time limit and was aborted."



Now first things first.. you need to unstick the cron if it's stuck. You can do this by removing the cron semaphore variable in the database.
You can do it in one of three ways:

1. Delete the variables from the database manually

The cron_semaphore and cron_last variables are located in the variables table of your Drupal database. You would need to flush cache afterwards too.

DELETE FROM `variable` WHERE name = 'cron_semaphore';
DELETE FROM `variable` WHERE name = 'cron_last';

2. Use Drush to delete the variables for you

drush vdel cron_last
drush vdel cron_semaphore

3. Use the Unstick module

The Unstick module is handy if your running a dev/staging site as it's easy to access.

Now that you can unstick your cron you can start debugging it. Taking from the Drupal debugging like a pro post, you can insert a line in the module.inc module_invoke_all function to catch the modules that implement hook_cron. Now because you can't necessarily have the output on-screen, you'll have to log the message with watchdog.

function module_invoke_all() {
  $args = func_get_args();
  $hook = $args[0];
  unset($args[0]);
  $return = array();
  foreach (module_implements($hook) as $module) {
    $function = $module .'_'. $hook;
    $result = call_user_func_array($function, $args);
    if (isset($result) && is_array($result)) {
      $return = array_merge_recursive($return, $result);
    }
    else if (isset($result)) {
      $return[] = $result;
    }
   
    // Custom code: Catching hook_cron.
    if ($hook == 'cron')
      watchdog('cron', 'Cron call: ' . $module cron);
  }

  return $return;
}

That will allow you to see what the last module was the executed hook_cron. Now sometimes cron gets stuck and you don't know what module is next, and because of module weight it can be any number of modules.

Now this can be checked with a nifty little script from Tim Millwood that you can run from Drush:

drush php-eval '$args = array(); global $timers; $hook = 'cron'; $return = array(); foreach (module_implements($hook) as $module) { $function = $module . '_' . $hook; print($function ." - "); timer_start($function); $result = call_user_func_array($function, $args); if (isset($result) && is_array($result)) { $return = array_merge_recursive($return, $result); } else if (isset($result)) { $return[] = $result; } timer_stop($function); print($timers[$function]['time'] ."\r\n"); }'

The script even adds a timer so you can see how long certain modules take with the hook implementations.

Created: Tue, 09/10/2012 - 17:16