I have noticed that Boost will cache pages even when there is no working connection to the database. This causes anonymous users to be served pages with error messages stating "MySQL server has gone away" even after MySQL has been restarted. The only way I know to solve this currently is to clear the entire Boost cache.

It would be great if there was a way for Boost to test for a MySQL connection before caching pages.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

EvanDonovan’s picture

Title: Boost caches pages when the MySQL connection is down » Don't cache pages when they are PHP/MySQL errors
Version: 5.x-1.0 » 6.x-1.0-alpha4

Maybe this should actually be a feature request, but I think it would be great if Boost didn't cache pages when either the MySQL connection was down or when there were fatal PHP errors.

I know the Authcache module handles the latter case as follows (in the _authcache_shutdown_save_page() function of authcache.helpers.inc, lines 116-119):

  // Don't cache pages with PHP errors (Drupal can't catch fatal errors)
  if(count(error_get_last())) {
    return;
  }
 

Since I think a broken MySQL connection causes PHP errors that might actually solve both cases at once. I'm not completely sure.

EvanDonovan’s picture

Title: Don't cache pages when they are PHP/MySQL errors » Don't cache pages when there are PHP/MySQL errors
mikeytown2’s picture

Priority: Normal » Critical

I'm currently in Los Angeles (not home) until the 20th, but this will be a top priority once I get back. Fix is easy, if the above code you gave is correct.

Before

/**
 * Implementation of hook_init(). Performs page setup tasks if page not cached.
 */
function boost_init() {
  // Stop right here unless we're being called for an ordinary page request
  if (strpos($_SERVER['SCRIPT_FILENAME'], 'index.php') === FALSE || variable_get('site_offline', 0))
    return;

After

/**
 * Implementation of hook_init(). Performs page setup tasks if page not cached.
 */
function boost_init() {
  // Stop right here unless we're being called for an ordinary, error free page request
  if (strpos($_SERVER['SCRIPT_FILENAME'], 'index.php') === FALSE || variable_get('site_offline', 0) || count(error_get_last()))
    return;

While here we should also check for drupal messages; those shouldn't be cached.
http://api.drupal.org/api/function/drupal_set_message
http://api.drupal.org/api/function/page_get_cache

EvanDonovan’s picture

Sounds great. They check for those in Authcache as well; I can find the specific code if you need it.

EvanDonovan’s picture

Something that may be a problem, though, is error reporting level. When installing Authcache a few days ago, I found pages were not being saved to the cache due to an E_NOTICE-level error. Since those could happen quite frequently on some Drupal sites, and don't generally show to the screen, then this check would stop more pages from being cached than necessary.

Therefore, the checking code might need to be more elaborate, similar to that in comment #2 of http://php.market.in.th/manual/sl/function.set-error-handler.php.

if ($error = error_get_last()){
            switch($error['type']){
                 case E_ERROR:
                 // other cases that should not be cached
                return;
                default:
                //do nothing, continue as normal
             }
}
mikeytown2’s picture

Here's a list of errors
http://www.php.net/errorfunc.constants
http://www.php.net/error_get_last

If you could write the logic so it only halts on critical & writing to the output, errors that would be quite helpful.

mikeytown2’s picture

I think these are the 4 errors in which boost should ignore and cache the page

if ($error = error_get_last()){
  switch($error['type']){
    case E_NOTICE: //Ignore run-time notices
    case E_USER_NOTICE: //Ignore user-generated notice message
    case E_DEPRECATED: //Ignore run-time notices
    case E_USER_DEPRECATED: //Ignore user-generated notice message
      break;
    default: //Do not cache page on all other errors
      return;
    }
  }

So this is what the new code block would look like

/**
* Implementation of hook_init(). Performs page setup tasks if page not cached.
*/
function boost_init() {
  // Stop right here unless we're being called for an ordinary, error free page request
  if (strpos($_SERVER['SCRIPT_FILENAME'], 'index.php') === FALSE || variable_get('site_offline', 0) || $user->uid || $_SERVER['REQUEST_METHOD'] != 'GET' || count(drupal_get_messages(NULL, FALSE)) || $_SERVER['SERVER_SOFTWARE'] === 'PHP CLI')
    return;
if ($error = error_get_last()){
  switch($error['type']){
    case E_NOTICE: //Ignore run-time notices
    case E_USER_NOTICE: //Ignore user-generated notice message
    case E_DEPRECATED: //Ignore run-time notices
    case E_USER_DEPRECATED: //Ignore user-generated notice message
      break;
    default: //Do not cache page on all other errors
      return;
    }
  }
EvanDonovan’s picture

Sounds good. But what if there were an error message that was generated on the page request and printed to the screen after the boost module had already passed the init hook?

mikeytown2’s picture

Good point, I'll add the error checks into the _boost_ob_handler() function, so count(drupal_get_messages(NULL, FALSE)) and error_get_last() would go in there.

EvanDonovan’s picture

That's good - that's exactly how Authcache handles it.

mikeytown2’s picture

Status: Active » Needs review
FileSize
4.42 KB

Decided to clean up boost_init while we are here. This really hasn't been tested since I have to run; will test later.

Here's how it looks

/**
 * Implementation of hook_init(). Performs page setup tasks if page not cached.
 */
function boost_init() {
  // Make the proper filename for our query
  $fname = '';
  foreach ($_GET as $key => $val) {
    if ($key != 'q') {
      $fname .= (empty($fname) ? '' : '&') . $key . '=' . $val;
    }
  }
  //set variables
  $GLOBALS['_boost_query'] = (empty($fname) ? '' : '_' . $fname);
  $GLOBALS['_boost_path'] = (empty($_REQUEST['q'])) ? 'index' : $_REQUEST['q'];
  global $user;
  
  // Make sure the page is/should be cached according to our current configuration
  if (   strpos($_SERVER['SCRIPT_FILENAME'], 'index.php') === FALSE 
      || variable_get('site_offline', 0) 
      || $_SERVER['REQUEST_METHOD'] != 'GET' 
      || $_SERVER['SERVER_SOFTWARE'] === 'PHP CLI'
      || variable_get('cache', CACHE_DISABLED)
      || !BOOST_ENABLED
      || isset($_GET['nocache'])
      || !boost_is_cacheable($GLOBALS['_boost_path'])
      ) {
    return;
  }
    
  // For authenticated users, set a special cookie to prevent them
  // inadvertently getting served pages from the static page cache.
  if (!empty($user->uid)) {
    boost_set_cookie($user);
  }
  // We only generate cached pages for anonymous visitors.
  else {
    ob_start('_boost_ob_handler');
  }
}
mikeytown2’s picture

Tested above code and it works; meaning the hook_init rewrite is good. Inside the patch is the do not cache errors code as well; this is a little bit harder to test so any error testing is greatly appreciated.

mikeytown2’s picture

Status: Needs review » Fixed

committed

mikeytown2’s picture

Priority: Critical » Normal
Status: Fixed » Needs review
FileSize
1.92 KB

Adding code to boost block so admin knows the page can not be cached due to php/drupal errors/messages. Also used hook_help as a hack to get the correct values from drupal_get_messages().

mikeytown2’s picture

FileSize
2.13 KB

Changing above patch do to this issue #480266: PHP < 5.2.0 - No files created since update to 6.x-1.0-beta1 due to missing error_get_last(). Also did some code cleanup of the patch.

mikeytown2’s picture

Version: 6.x-1.0-alpha4 » 6.x-1.x-dev
FileSize
3.52 KB

incorporating #480266: PHP < 5.2.0 - No files created since update to 6.x-1.0-beta1 due to missing error_get_last() into this patch since it's related. Formatted the block better as well.

mikeytown2’s picture

FileSize
5.53 KB

Added a setting to control if boost will cache on errors.

mikeytown2’s picture

FileSize
5.53 KB
mikeytown2’s picture

Status: Needs review » Fixed

committed

XerraX’s picture

Status: Fixed » Needs work

if i uncheck the new checkbox and save the settings, the checkbox is checked again

mikeytown2’s picture

FileSize
3.13 KB

I can not confirm this, it works for me. Try unchecking it one more time; it makes no since. In the mean time I've done more cosmetic changes to boost's watchdog errors, and added more as well. Also changed the default to false, because php throws errors too easily.

This patch requires the one above to be applied first.

mikeytown2’s picture

Status: Needs work » Needs review
mikeytown2’s picture

committed above changes so next dev will have the default set to false

XerraX’s picture

Ok it was my fault, somehow the previous patch messed up my module file. I re applied Patch 3 and then Patch 4, now everything is working as expected.

Thanks for all your support and this great module.

mikeytown2’s picture

Status: Needs review » Fixed

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.