From 3ad9e41be9a959cca92527f964c8b2bea5fbb25e Mon Sep 17 00:00:00 2001 From: Aaron Schulz Date: Wed, 14 Sep 2016 13:07:22 -0700 Subject: [PATCH] Fix $wgFileCache DB outage fallback Change-Id: I5c41b4669ca29d119de5c08a2c61dbadae7cf55c --- includes/MediaWiki.php | 50 ++++++++++++++-------- includes/cache/HTMLFileCache.php | 25 +++++++++-- includes/exception/MWExceptionRenderer.php | 2 +- 3 files changed, 56 insertions(+), 21 deletions(-) diff --git a/includes/MediaWiki.php b/includes/MediaWiki.php index 2aa4b80c66..2fce08ced2 100644 --- a/includes/MediaWiki.php +++ b/includes/MediaWiki.php @@ -528,6 +528,24 @@ class MediaWiki { $e->report(); // display the GUI error } } catch ( Exception $e ) { + $context = $this->context; + if ( + $e instanceof DBConnectionError && + $context->hasTitle() && + $context->getTitle()->canExist() && + $context->getRequest()->getVal( 'action', 'view' ) === 'view' && + HTMLFileCache::useFileCache( $this->context, HTMLFileCache::MODE_OUTAGE ) + ) { + // Try to use any (even stale) file during outages... + $cache = new HTMLFileCache( $context->getTitle(), 'view' ); + if ( $cache->isCached() ) { + $cache->loadFromFileCache( $context, HTMLFileCache::MODE_OUTAGE ); + print MWExceptionRenderer::getHTML( $e ); + exit; + } + + } + MWExceptionHandler::handleException( $e ); } @@ -819,24 +837,22 @@ class MediaWiki { } } - if ( $this->config->get( 'UseFileCache' ) && $title->getNamespace() >= 0 ) { - if ( HTMLFileCache::useFileCache( $this->context ) ) { - // Try low-level file cache hit - $cache = new HTMLFileCache( $title, $action ); - if ( $cache->isCacheGood( /* Assume up to date */ ) ) { - // Check incoming headers to see if client has this cached - $timestamp = $cache->cacheTimestamp(); - if ( !$output->checkLastModified( $timestamp ) ) { - $cache->loadFromFileCache( $this->context ); - } - // Do any stats increment/watchlist stuff - // Assume we're viewing the latest revision (this should always be the case with file cache) - $this->context->getWikiPage()->doViewUpdates( $this->context->getUser() ); - // Tell OutputPage that output is taken care of - $output->disable(); - - return; + if ( $title->canExist() && HTMLFileCache::useFileCache( $this->context ) ) { + // Try low-level file cache hit + $cache = new HTMLFileCache( $title, $action ); + if ( $cache->isCacheGood( /* Assume up to date */ ) ) { + // Check incoming headers to see if client has this cached + $timestamp = $cache->cacheTimestamp(); + if ( !$output->checkLastModified( $timestamp ) ) { + $cache->loadFromFileCache( $this->context ); } + // Do any stats increment/watchlist stuff, assuming user is viewing the + // latest revision (which should always be the case for file cache) + $this->context->getWikiPage()->doViewUpdates( $this->context->getUser() ); + // Tell OutputPage that output is taken care of + $output->disable(); + + return; } } diff --git a/includes/cache/HTMLFileCache.php b/includes/cache/HTMLFileCache.php index 1bab0f5a8e..52ae27912d 100644 --- a/includes/cache/HTMLFileCache.php +++ b/includes/cache/HTMLFileCache.php @@ -29,6 +29,9 @@ * @ingroup Cache */ class HTMLFileCache extends FileCacheBase { + const MODE_NORMAL = 0; // normal cache mode + const MODE_OUTAGE = 1; // fallback cache for DB outages + /** * Construct an HTMLFileCache object from a Title and an action * @@ -93,10 +96,12 @@ class HTMLFileCache extends FileCacheBase { /** * Check if pages can be cached for this request/user * @param IContextSource $context + * @param integer $mode One of the HTMLFileCache::MODE_* constants * @return bool */ - public static function useFileCache( IContextSource $context ) { + public static function useFileCache( IContextSource $context, $mode = self::MODE_NORMAL ) { global $wgUseFileCache, $wgDebugToolbar, $wgContLang; + if ( !$wgUseFileCache ) { return false; } @@ -121,15 +126,23 @@ class HTMLFileCache extends FileCacheBase { return false; } + $user = $context->getUser(); // Check for non-standard user language; this covers uselang, // and extensions for auto-detecting user language. $ulang = $context->getLanguage(); // Check that there are no other sources of variation - if ( $user->getId() || $user->getNewtalk() || !$ulang->equals( $wgContLang ) ) { + if ( $user->getId() || !$ulang->equals( $wgContLang ) ) { return false; } + + if ( $mode === self::MODE_NORMAL ) { + if ( $user->getNewtalk() ) { + return false; + } + } + // Allow extensions to disable caching return Hooks::run( 'HTMLFileCache::useFileCache', [ $context ] ); } @@ -137,14 +150,20 @@ class HTMLFileCache extends FileCacheBase { /** * Read from cache to context output * @param IContextSource $context + * @param integer $mode One of the HTMLFileCache::MODE_* constants * @return void */ - public function loadFromFileCache( IContextSource $context ) { + public function loadFromFileCache( IContextSource $context, $mode = self::MODE_NORMAL ) { global $wgMimeType, $wgLanguageCode; wfDebug( __METHOD__ . "()\n" ); $filename = $this->cachePath(); + if ( $mode === self::MODE_OUTAGE ) { + // Avoid DB errors for queries in sendCacheControl() + $context->getTitle()->resetArticleID( 0 ); + } + $context->getOutput()->sendCacheControl(); header( "Content-Type: $wgMimeType; charset=UTF-8" ); header( "Content-Language: $wgLanguageCode" ); diff --git a/includes/exception/MWExceptionRenderer.php b/includes/exception/MWExceptionRenderer.php index 58b4ac705d..bb7a01f18e 100644 --- a/includes/exception/MWExceptionRenderer.php +++ b/includes/exception/MWExceptionRenderer.php @@ -209,7 +209,7 @@ class MWExceptionRenderer { * @param Exception $e * @return string Html to output */ - private static function getHTML( Exception $e ) { + public static function getHTML( Exception $e ) { if ( self::showBackTrace( $e ) ) { $html = "

" . nl2br( htmlspecialchars( MWExceptionHandler::getLogMessage( $e ) ) ) . -- 2.20.1