From 108ccb0352241cc7dbf0d73a48bd0b1b6f9fc8f4 Mon Sep 17 00:00:00 2001 From: Aaron Schulz Date: Wed, 14 Sep 2016 13:36:34 -0700 Subject: [PATCH] Make rebuildFileCache cover ?action=history Also simplified the logic slightly Change-Id: I6145d52b6b701735fa4bd8e41e07fb2bf6fdcee3 --- includes/MediaWiki.php | 3 +- includes/actions/HistoryAction.php | 3 +- includes/cache/FileCacheBase.php | 6 --- includes/cache/HTMLFileCache.php | 38 ++++++++++------- includes/page/Article.php | 5 ++- maintenance/rebuildFileCache.php | 65 +++++++++++++++++++----------- 6 files changed, 71 insertions(+), 49 deletions(-) diff --git a/includes/MediaWiki.php b/includes/MediaWiki.php index 2fce08ced2..8cf009f47a 100644 --- a/includes/MediaWiki.php +++ b/includes/MediaWiki.php @@ -529,11 +529,12 @@ class MediaWiki { } } catch ( Exception $e ) { $context = $this->context; + $action = $context->getRequest()->getVal( 'action', 'view' ); if ( $e instanceof DBConnectionError && $context->hasTitle() && $context->getTitle()->canExist() && - $context->getRequest()->getVal( 'action', 'view' ) === 'view' && + in_array( $action, [ 'view', 'history' ], true ) && HTMLFileCache::useFileCache( $this->context, HTMLFileCache::MODE_OUTAGE ) ) { // Try to use any (even stale) file during outages... diff --git a/includes/actions/HistoryAction.php b/includes/actions/HistoryAction.php index 41378fb693..f3ef3b3cb6 100644 --- a/includes/actions/HistoryAction.php +++ b/includes/actions/HistoryAction.php @@ -105,8 +105,7 @@ class HistoryAction extends FormlessAction { $config = $this->context->getConfig(); # Fill in the file cache if not set already - $useFileCache = $config->get( 'UseFileCache' ); - if ( $useFileCache && HTMLFileCache::useFileCache( $this->getContext() ) ) { + if ( HTMLFileCache::useFileCache( $this->getContext() ) ) { $cache = new HTMLFileCache( $this->getTitle(), 'history' ); if ( !$cache->isCacheGood( /* Assume up to date */ ) ) { ob_start( [ &$cache, 'saveToFileCache' ] ); diff --git a/includes/cache/FileCacheBase.php b/includes/cache/FileCacheBase.php index 360420b615..e25f882b37 100644 --- a/includes/cache/FileCacheBase.php +++ b/includes/cache/FileCacheBase.php @@ -157,12 +157,6 @@ abstract class FileCacheBase { * @return string Compressed text */ public function saveText( $text ) { - global $wgUseFileCache; - - if ( !$wgUseFileCache ) { - return false; - } - if ( $this->useGzip() ) { $text = gzencode( $text ); } diff --git a/includes/cache/HTMLFileCache.php b/includes/cache/HTMLFileCache.php index 71011e03a0..a85639faf4 100644 --- a/includes/cache/HTMLFileCache.php +++ b/includes/cache/HTMLFileCache.php @@ -21,6 +21,8 @@ * @ingroup Cache */ +use MediaWiki\MediaWikiServices; + /** * Page view caching in the file system. * The only cacheable actions are "view" and "history". Also special pages @@ -31,6 +33,7 @@ class HTMLFileCache extends FileCacheBase { const MODE_NORMAL = 0; // normal cache mode const MODE_OUTAGE = 1; // fallback cache for DB outages + const MODE_REBUILD = 2; // background cache rebuild mode /** * Construct an HTMLFileCache object from a Title and an action @@ -52,6 +55,7 @@ class HTMLFileCache extends FileCacheBase { */ public function __construct( $title, $action ) { parent::__construct(); + $allowedTypes = self::cacheablePageActions(); if ( !in_array( $action, $allowedTypes ) ) { throw new MWException( 'Invalid file cache type given.' ); @@ -96,16 +100,15 @@ 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 + * @param integer $mode One of the HTMLFileCache::MODE_* constants (since 1.28) * @return bool */ public static function useFileCache( IContextSource $context, $mode = self::MODE_NORMAL ) { - global $wgUseFileCache, $wgDebugToolbar, $wgContLang; + $config = MediaWikiServices::getInstance()->getMainConfig(); - if ( !$wgUseFileCache ) { + if ( !$config->get( 'UseFileCache' ) && $mode !== self::MODE_REBUILD ) { return false; - } - if ( $wgDebugToolbar ) { + } elseif ( $config->get( 'DebugToolbar' ) ) { wfDebug( "HTML file cache skipped. \$wgDebugToolbar on\n" ); return false; @@ -133,7 +136,7 @@ class HTMLFileCache extends FileCacheBase { $ulang = $context->getLanguage(); // Check that there are no other sources of variation - if ( $user->getId() || !$ulang->equals( $wgContLang ) ) { + if ( $user->getId() || $ulang->getCode() !== $config->get( 'LanguageCode' ) ) { return false; } @@ -154,7 +157,7 @@ class HTMLFileCache extends FileCacheBase { * @return void */ public function loadFromFileCache( IContextSource $context, $mode = self::MODE_NORMAL ) { - global $wgMimeType, $wgContLang; + $config = MediaWikiServices::getInstance()->getMainConfig(); wfDebug( __METHOD__ . "()\n" ); $filename = $this->cachePath(); @@ -165,8 +168,8 @@ class HTMLFileCache extends FileCacheBase { } $context->getOutput()->sendCacheControl(); - header( "Content-Type: $wgMimeType; charset=UTF-8" ); - header( 'Content-Language: ' . $wgContLang->getHtmlCode() ); + header( "Content-Type: {$config->get( 'MimeType' )}; charset=UTF-8" ); + header( "Content-Language: {$config->get( 'LanguageCode' )}" ); if ( $this->useGzip() ) { if ( wfClientAcceptsGzip() ) { header( 'Content-Encoding: gzip' ); @@ -179,19 +182,24 @@ class HTMLFileCache extends FileCacheBase { } else { readfile( $filename ); } + $context->getOutput()->disable(); // tell $wgOut that output is taken care of } /** * Save this cache object with the given text. * Use this as an ob_start() handler. + * + * Normally this is only registed as a handler if $wgUseFileCache is on. + * If can be explicitly called by rebuildFileCache.php when it takes over + * handling file caching itself, disabling any automatic handling the the + * process. + * * @param string $text - * @return bool Whether $wgUseFileCache is enabled + * @return string|bool The annotated $text or false on error */ public function saveToFileCache( $text ) { - global $wgUseFileCache; - - if ( !$wgUseFileCache || strlen( $text ) < 512 ) { + if ( strlen( $text ) < 512 ) { // Disabled or empty/broken output (OOM and PHP errors) return $text; } @@ -234,9 +242,9 @@ class HTMLFileCache extends FileCacheBase { * @return bool Whether $wgUseFileCache is enabled */ public static function clearFileCache( Title $title ) { - global $wgUseFileCache; + $config = MediaWikiServices::getInstance()->getMainConfig(); - if ( !$wgUseFileCache ) { + if ( !$config->get( 'UseFileCache' ) ) { return false; } diff --git a/includes/page/Article.php b/includes/page/Article.php index db6df864ff..af897ddc98 100644 --- a/includes/page/Article.php +++ b/includes/page/Article.php @@ -1932,12 +1932,13 @@ class Article implements Page { /** * Check if the page can be cached + * @param integer $mode One of the HTMLFileCache::MODE_* constants (since 1.28) * @return bool */ - public function isFileCacheable() { + public function isFileCacheable( $mode = HTMLFileCache::MODE_NORMAL ) { $cacheable = false; - if ( HTMLFileCache::useFileCache( $this->getContext() ) ) { + if ( HTMLFileCache::useFileCache( $this->getContext(), $mode ) ) { $cacheable = $this->mPage->getId() && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect(); // Extension may have reason to disable file caching on some pages. diff --git a/maintenance/rebuildFileCache.php b/maintenance/rebuildFileCache.php index b278e989f1..2287559795 100644 --- a/maintenance/rebuildFileCache.php +++ b/maintenance/rebuildFileCache.php @@ -29,6 +29,8 @@ require_once __DIR__ . '/Maintenance.php'; * @ingroup Maintenance */ class RebuildFileCache extends Maintenance { + private $enabled = true; + public function __construct() { parent::__construct(); $this->addDescription( 'Build file cache for content pages' ); @@ -39,23 +41,27 @@ class RebuildFileCache extends Maintenance { } public function finalSetup() { - global $wgDebugToolbar; + global $wgDebugToolbar, $wgUseFileCache, $wgReadOnly; + $this->enabled = $wgUseFileCache; + // Script will handle capturing output and saving it itself + $wgUseFileCache = false; // Debug toolbar makes content uncacheable so we disable it. // Has to be done before Setup.php initialize MWDebug $wgDebugToolbar = false; + // Avoid DB writes (like enotif/counters) + $wgReadOnly = 'Building cache'; // avoid DB writes (like enotif/counters) + parent::finalSetup(); } public function execute() { - global $wgUseFileCache, $wgReadOnly, $wgRequestTime; - global $wgOut; - if ( !$wgUseFileCache ) { + global $wgRequestTime; + + if ( !$this->enabled ) { $this->error( "Nothing to do -- \$wgUseFileCache is disabled.", true ); } - $wgReadOnly = 'Building cache'; // avoid DB writes (like enotif/counters) - $start = $this->getOption( 'start', "0" ); if ( !ctype_digit( $start ) ) { $this->error( "Invalid value for start parameter.", true ); @@ -90,6 +96,7 @@ class RebuildFileCache extends Maintenance { $blockEnd = $start + $this->mBatchSize - 1; $dbw = $this->getDB( DB_MASTER ); + $mainContext = RequestContext::getMain(); // Go through each page and save the output while ( $blockEnd <= $end ) { // Get the pages @@ -104,7 +111,6 @@ class RebuildFileCache extends Maintenance { $this->beginTransaction( $dbw, __METHOD__ ); // for any changes foreach ( $res as $row ) { $rebuilt = false; - $wgRequestTime = microtime( true ); # bug 22852 $title = Title::makeTitleSafe( $row->page_namespace, $row->page_title ); if ( null == $title ) { @@ -112,39 +118,52 @@ class RebuildFileCache extends Maintenance { continue; // broken title? } - $context = new RequestContext; + $context = new RequestContext(); $context->setTitle( $title ); $article = Article::newFromTitle( $title, $context ); $context->setWikiPage( $article->getPage() ); - $wgOut = $context->getOutput(); // set display title - // If the article is cacheable, then load it - if ( $article->isFileCacheable() ) { - $cache = new HTMLFileCache( $title, 'view' ); - if ( $cache->isCacheGood() ) { + if ( $article->isFileCacheable( HTMLFileCache::MODE_REBUILD ) ) { + $viewCache = new HTMLFileCache( $title, 'view' ); + $historyCache = new HTMLFileCache( $title, 'history' ); + if ( $viewCache->isCacheGood() && $historyCache->isCacheGood() ) { if ( $overwrite ) { $rebuilt = true; } else { - $this->output( "Page {$row->page_id} already cached\n" ); + $this->output( "Page '$title' (id {$row->page_id}) already cached\n" ); continue; // done already! } } - ob_start( [ &$cache, 'saveToFileCache' ] ); // save on ob_end_clean() - $wgUseFileCache = false; // hack, we don't want $article fiddling with filecache - $article->view(); + MediaWiki\suppressWarnings(); // header notices - $wgOut->output(); + // Cache ?action=view + $wgRequestTime = microtime( true ); # bug 22852 + ob_start(); + $article->view(); + $context->getOutput()->output(); + $context->getOutput()->clearHTML(); + $viewHtml = ob_get_clean(); + $viewCache->saveToFileCache( $viewHtml ); + // Cache ?action=history + $wgRequestTime = microtime( true ); # bug 22852 + ob_start(); + Action::factory( 'history', $article, $context )->show(); + $context->getOutput()->output(); + $context->getOutput()->clearHTML(); + $historyHtml = ob_get_clean(); + $historyCache->saveToFileCache( $historyHtml ); MediaWiki\restoreWarnings(); - $wgUseFileCache = true; - ob_end_clean(); // clear buffer + if ( $rebuilt ) { - $this->output( "Re-cached page {$row->page_id}\n" ); + $this->output( "Re-cached page '$title' (id {$row->page_id})..." ); } else { - $this->output( "Cached page {$row->page_id}\n" ); + $this->output( "Cached page '$title' (id {$row->page_id})..." ); } + $this->output( "[view: " . strlen( $viewHtml ) . " bytes; " . + "history: " . strlen( $historyHtml ) . " bytes]\n" ); } else { - $this->output( "Page {$row->page_id} not cacheable\n" ); + $this->output( "Page '$title' (id {$row->page_id}) not cacheable\n" ); } } $this->commitTransaction( $dbw, __METHOD__ ); // commit any changes (just for sanity) -- 2.20.1