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
}
public function onSubmit( $data ) {
}
public function onSubmit( $data ) {
- return $this->page->doPurge();
+ return $this->page->doPurge( WikiPage::PURGE_ALL );
}
public function show() {
}
public function show() {
+ // 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" );
// 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" );
ApiQueryBase::addTitleInfo( $r, $title );
$page = WikiPage::factory( $title );
if ( !$user->pingLimiter( 'purge' ) ) {
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' ] );
$r['purged'] = true;
} else {
$error = $this->parseMsg( [ 'actionthrottledtext' ] );
* Call to WikiPage function for backwards compatibility.
* @see WikiPage::doPurge
*/
* 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();
- /**
- * Override handling of action=purge
- * @return bool
- */
- public function doPurge() {
+ public function doPurge( $flags = self::PURGE_ALL ) {
if ( $this->mFile->exists() ) {
wfDebug( 'ImagePage::doPurge purging ' . $this->mFile->getName() . "\n" );
DeferredUpdates::addUpdate( new HTMLCacheUpdate( $this->mTitle, 'imagelinks' ) );
if ( $this->mFile->exists() ) {
wfDebug( 'ImagePage::doPurge purging ' . $this->mFile->getName() . "\n" );
DeferredUpdates::addUpdate( new HTMLCacheUpdate( $this->mTitle, 'imagelinks' ) );
// Purge redirect cache
$this->mRepo->invalidateImageRedirect( $this->mTitle );
}
// Purge redirect cache
$this->mRepo->invalidateImageRedirect( $this->mTitle );
}
- return parent::doPurge();
+
+ return parent::doPurge( $flags );
*/
protected $mLinksUpdated = '19700101000000';
*/
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.
/**
* Constructor and clear the article
* @param Title $title Reference to a Title object.
/**
* Perform the actions of a page purging
/**
* Perform the actions of a page purging
+ * @param integer $flags Bitfield of WikiPage::PURGE_* constants
- public function doPurge() {
+ public function doPurge( $flags = self::PURGE_ALL ) {
if ( !Hooks::run( 'ArticlePurge', [ &$this ] ) ) {
return false;
}
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
if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
// @todo move this logic to MessageCache
+ /**
+ * 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
/**
* Insert a new empty page record for this article.
* This *must* be followed up by creating a revision
- * @param WikiPage $article
+ * @param WikiPage $page
* @return mixed|string
*/
* @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 ) );