From: Aaron Schulz Date: Sun, 11 Sep 2016 18:49:42 +0000 (-0700) Subject: Avoid page_touched update for HTTP GET action=purge requests X-Git-Tag: 1.31.0-rc.0~5614^2 X-Git-Url: http://git.cyclocoop.org/%7B%24admin_url%7Dmes_infos.php?a=commitdiff_plain;h=c84ba4d86420d7af918e572e2cd4613d7be185b3;p=lhc%2Fweb%2Fwiklou.git Avoid page_touched update for HTTP GET action=purge requests This will still clear the local DC parser cache and the CDN cache in all DCs. Therefore, the next page views served by the local DC will reflect the refreshed content, as will further GET/HEAD requests by the client that issued the purge using GET/HEAD. If the problem was imply a stale CDN cache entry, then all DCs will be up-to-date. If the problem is stale parser cache, then a proper POST purge request is required to refresh all DCs. Bug: T92357 Change-Id: I9af12ca8cfff73298f404fd3e2dd4f546621c546 --- diff --git a/includes/actions/PurgeAction.php b/includes/actions/PurgeAction.php index b2002ffae9..942b731634 100644 --- a/includes/actions/PurgeAction.php +++ b/includes/actions/PurgeAction.php @@ -42,7 +42,7 @@ class PurgeAction extends FormAction { } public function onSubmit( $data ) { - return $this->page->doPurge(); + return $this->page->doPurge( WikiPage::PURGE_ALL ); } public function show() { diff --git a/includes/actions/ViewAction.php b/includes/actions/ViewAction.php index 55507f5aa4..4a7f623825 100644 --- a/includes/actions/ViewAction.php +++ b/includes/actions/ViewAction.php @@ -58,6 +58,9 @@ class ViewAction extends FormlessAction { $touched = null; } + // If a page was purged on HTTP GET, relect that timestamp to avoid sending 304s + $touched = max( $touched, $this->page->getLastPurgeTimestamp() ); + // Send HTTP 304 if the IMS matches or otherwise set expiry/last-modified headers if ( $touched && $this->getOutput()->checkLastModified( $touched ) ) { wfDebug( __METHOD__ . ": done 304\n" ); diff --git a/includes/api/ApiPurge.php b/includes/api/ApiPurge.php index f671103fb5..5d1352ce3f 100644 --- a/includes/api/ApiPurge.php +++ b/includes/api/ApiPurge.php @@ -55,7 +55,12 @@ class ApiPurge extends ApiBase { ApiQueryBase::addTitleInfo( $r, $title ); $page = WikiPage::factory( $title ); if ( !$user->pingLimiter( 'purge' ) ) { - $page->doPurge(); // Directly purge and skip the UI part of purge(). + $flags = WikiPage::PURGE_ALL; + if ( !$this->getRequest()->wasPosted() ) { + $flags ^= WikiPage::PURGE_GLOBAL_PCACHE; // skip DB_MASTER write + } + // Directly purge and skip the UI part of purge() + $page->doPurge( $flags ); $r['purged'] = true; } else { $error = $this->parseMsg( [ 'actionthrottledtext' ] ); diff --git a/includes/page/Article.php b/includes/page/Article.php index 449c9ffe58..b36217ec18 100644 --- a/includes/page/Article.php +++ b/includes/page/Article.php @@ -2140,8 +2140,16 @@ class Article implements Page { * Call to WikiPage function for backwards compatibility. * @see WikiPage::doPurge */ - public function doPurge() { - return $this->mPage->doPurge(); + public function doPurge( $flags = WikiPage::PURGE_ALL ) { + return $this->mPage->doPurge( $flags ); + } + + /** + * Call to WikiPage function for backwards compatibility. + * @see WikiPage::getLastPurgeTimestamp + */ + public function getLastPurgeTimestamp() { + return $this->mPage->getLastPurgeTimestamp(); } /** diff --git a/includes/page/WikiFilePage.php b/includes/page/WikiFilePage.php index 0dc28bd5d1..c478550d0b 100644 --- a/includes/page/WikiFilePage.php +++ b/includes/page/WikiFilePage.php @@ -162,12 +162,9 @@ class WikiFilePage extends WikiPage { return $this->mDupes; } - /** - * Override handling of action=purge - * @return bool - */ - public function doPurge() { + public function doPurge( $flags = self::PURGE_ALL ) { $this->loadFile(); + if ( $this->mFile->exists() ) { wfDebug( 'ImagePage::doPurge purging ' . $this->mFile->getName() . "\n" ); DeferredUpdates::addUpdate( new HTMLCacheUpdate( $this->mTitle, 'imagelinks' ) ); @@ -183,7 +180,8 @@ class WikiFilePage extends WikiPage { // Purge redirect cache $this->mRepo->invalidateImageRedirect( $this->mTitle ); } - return parent::doPurge(); + + return parent::doPurge( $flags ); } /** diff --git a/includes/page/WikiPage.php b/includes/page/WikiPage.php index 938f292887..800b583c13 100644 --- a/includes/page/WikiPage.php +++ b/includes/page/WikiPage.php @@ -83,6 +83,11 @@ class WikiPage implements Page, IDBAccessObject { */ protected $mLinksUpdated = '19700101000000'; + const PURGE_CDN_CACHE = 1; // purge CDN cache for page variant URLs + const PURGE_CLUSTER_PCACHE = 2; // purge parser cache in the local datacenter + const PURGE_GLOBAL_PCACHE = 4; // set page_touched to clear parser cache in all datacenters + const PURGE_ALL = 7; + /** * Constructor and clear the article * @param Title $title Reference to a Title object. @@ -1111,22 +1116,38 @@ class WikiPage implements Page, IDBAccessObject { /** * Perform the actions of a page purging + * @param integer $flags Bitfield of WikiPage::PURGE_* constants * @return bool */ - public function doPurge() { + public function doPurge( $flags = self::PURGE_ALL ) { if ( !Hooks::run( 'ArticlePurge', [ &$this ] ) ) { return false; } - $this->mTitle->invalidateCache(); + if ( ( $flags & self::PURGE_GLOBAL_PCACHE ) == self::PURGE_GLOBAL_PCACHE ) { + // Set page_touched in the database to invalidate all DC caches + $this->mTitle->invalidateCache(); + } elseif ( ( $flags & self::PURGE_CLUSTER_PCACHE ) == self::PURGE_CLUSTER_PCACHE ) { + // Delete the parser options key in the local cluster to invalidate the DC cache + ParserCache::singleton()->deleteOptionsKey( $this ); + // Avoid sending HTTP 304s in ViewAction to the client who just issued the purge + $cache = ObjectCache::getLocalClusterInstance(); + $cache->set( + $cache->makeKey( 'page', 'last-dc-purge', $this->getId() ), + wfTimestamp( TS_MW ), + $cache::TTL_HOUR + ); + } - // Clear file cache - HTMLFileCache::clearFileCache( $this->getTitle() ); - // Send purge after above page_touched update was committed - DeferredUpdates::addUpdate( - new CdnCacheUpdate( $this->mTitle->getCdnUrls() ), - DeferredUpdates::PRESEND - ); + if ( ( $flags & self::PURGE_CDN_CACHE ) == self::PURGE_CDN_CACHE ) { + // Clear any HTML file cache + HTMLFileCache::clearFileCache( $this->getTitle() ); + // Send purge after any page_touched above update was committed + DeferredUpdates::addUpdate( + new CdnCacheUpdate( $this->mTitle->getCdnUrls() ), + DeferredUpdates::PRESEND + ); + } if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) { // @todo move this logic to MessageCache @@ -1150,6 +1171,18 @@ class WikiPage implements Page, IDBAccessObject { return true; } + /** + * Get the last time a user explicitly purged the page via action=purge + * + * @return string|bool TS_MW timestamp or false + * @since 1.28 + */ + public function getLastPurgeTimestamp() { + $cache = ObjectCache::getLocalClusterInstance(); + + return $cache->get( $cache->makeKey( 'page', 'last-dc-purge', $this->getId() ) ); + } + /** * Insert a new empty page record for this article. * This *must* be followed up by creating a revision diff --git a/includes/parser/ParserCache.php b/includes/parser/ParserCache.php index c6265a7ccd..9e96540975 100644 --- a/includes/parser/ParserCache.php +++ b/includes/parser/ParserCache.php @@ -72,12 +72,19 @@ class ParserCache { } /** - * @param WikiPage $article + * @param WikiPage $page * @return mixed|string */ - protected function getOptionsKey( $article ) { - $pageid = $article->getId(); - return wfMemcKey( 'pcache', 'idoptions', "{$pageid}" ); + protected function getOptionsKey( $page ) { + return wfMemcKey( 'pcache', 'idoptions', $page->getId() ); + } + + /** + * @param WikiPage $page + * @since 1.28 + */ + public function deleteOptionsKey( $page ) { + $this->mMemc->delete( $this->getOptionsKey( $page ) ); } /**