For a reason I have yet to discover, the www user on our server was suddenly unable to write to the ~/wurfl/cache/FILE_CACHE_PROVIDER directory this afternoon. Rather than fail gracefully and log a watchdog error, this led to a fatal error in wurfl.module, immediate WSOD and complete failure of all our sites.

It is possible to check is_writable() for a folder before attempting to write into it. Can we add this to the module to prevent such total devastation if the cache fails to be writable again?

Comments

martin_q’s picture

Status: Active » Needs review

I have written a little module to catch the exception and attempt a page-reload after a short delay. This will only attempt up to three reloads and will stop if pages are already loading slowly. Meanwhile, a watchdog report will be written.

I have also changed FileManager.php so that lines 76 and 85 use @fopen and @fwrite respectively.

The module (in its entirety) looks like this:

function wurfl_exception_handler($exception) {
  watchdog('wurfl_exception', "Uncaught exception: %message", array('%message' => $exception->getMessage()), WATCHDOG_ERROR);

  if (substr((get_class($exception)), 0, 5) == 'WURFL') {
    // Set delay length in seconds.
    $delay = 4;
    // Set maximum number of retries.
    $max_retries = 3;
    // Set permissible page load time in seconds.
    $page_load_time = 10;
    // Get current server time in seconds.
    $this_start_time = time();
    $current_time = $start_time;
    // Delay by $delay seconds.
    while($current_time < $this_start_time + $delay) {
      $current_time = time();
    }

    $page_path = $_GET['q'];
    $page_query = $_GET;
    unset ($page_query['q']);
    // Retry the same URL up to 3 times, as long as previous attempts haven't taken too long.
    $page_query['timestamp'] = $page_query['timestamp'] ? $page_query['timestamp'] : $this_start_time;
    if (
      $page_query['retries_already'] < $max_retries
        &&
      $this_start_time < $page_query['timestamp'] + ($page_query['retries_already'] + 1) * ($page_load_time + $delay)
    ) {
      $page_query['retries_already']++;
      drupal_goto($page_path, $page_query);
    }
  }
  // Otherwise, fail.
  echo "Uncaught exception: " , get_class($exception), " with message '" , $exception->getMessage(), "'\n";
}

set_exception_handler('wurfl_exception_handler');

I have yet to see if it will do the trick. Comments welcome.