Merge "HtmlCacheUpdater: Add getUrls() method and support selective purging"

This commit is contained in:
jenkins-bot 2020-04-30 18:35:45 +00:00 committed by Gerrit Code Review
commit 634179e107
5 changed files with 141 additions and 33 deletions

View file

@ -3529,6 +3529,23 @@ $title: Title object being checked against
$user: Current user object
&$whitelisted: Boolean value of whether this title is whitelisted
'HtmlCacheUpdaterAppendUrls': Declare extra URLs to purge from HTTP caches.
Use $mode to decide whether to gather all related URLs or only those affected by
a re-render of the same content. For example, after a direct revision to the
content the history page will need to be purged. However when re-rendering after
a cascading change from a template, only URLs that render content need purging.
The $mode will be either HtmlCacheUpdater::PURGE_URLS_LINKSUPDATE_ONLY or 0.
$title: Title object for the page being purged.
$mode: Integer.
&$append: Append URLs relating to $title.
'HtmlCacheUpdaterVaryUrls': Add variants of URLs to purge from HTTP caches.
Extensions that provide site-wide variants of all URLs, such as by serving from
an alternate domain or path, can use this hook to append alternative URLs for
each url in $canonical.
$urls: Canonical URLs.
&$append: Append alternative URLs for $urls.
'TitleSquidURLs': Called to determine which URLs to purge from HTTP caches.
$title: Title object to purge
&$urls: An array of URLs to purge from the caches, to be manipulated.

View file

@ -3551,28 +3551,14 @@ class Title implements LinkTarget, IDBAccessObject {
}
/**
* Get a list of URLs to purge from the CDN cache when this
* page changes
* Get a list of URLs to purge from the CDN cache when this page changes.
*
* @deprecated 1.35 Use HtmlCacheUpdater
* @return string[] Array of String the URLs
*/
public function getCdnUrls() {
$urls = [
$this->getInternalURL(),
$this->getInternalURL( 'action=history' )
];
// If we are looking at a css/js user subpage, purge the action=raw.
if ( $this->isUserJsConfigPage() ) {
$urls[] = $this->getInternalURL( 'action=raw&ctype=text/javascript' );
} elseif ( $this->isUserJsonConfigPage() ) {
$urls[] = $this->getInternalURL( 'action=raw&ctype=application/json' );
} elseif ( $this->isUserCssConfigPage() ) {
$urls[] = $this->getInternalURL( 'action=raw&ctype=text/css' );
}
Hooks::run( 'TitleSquidURLs', [ $this, &$urls ] );
return $urls;
$htmlCache = MediaWikiServices::getInstance()->getHtmlCacheUpdater();
return $htmlCache->getUrls( $this );
}
/**
@ -3580,8 +3566,8 @@ class Title implements LinkTarget, IDBAccessObject {
* @deprecated 1.35 Use HtmlCacheUpdater
*/
public function purgeSquid() {
$hcu = MediaWikiServices::getInstance()->getHtmlCacheUpdater();
$hcu->purgeTitleUrls( $this, $hcu::PURGE_INTENT_TXROUND_REFLECTED );
$htmlCache = MediaWikiServices::getInstance()->getHtmlCacheUpdater();
$htmlCache->purgeTitleUrls( $this, $htmlCache::PURGE_INTENT_TXROUND_REFLECTED );
}
/**

View file

@ -54,6 +54,16 @@ class HtmlCacheUpdater {
*/
public const PURGE_INTENT_TXROUND_REFLECTED = self::PURGE_PRESEND | self::PURGE_REBOUND;
/**
* Reduce set of URLs to be purged to only those that may be affected by
* change propagation from LinksUpdate (e.g. after a used template was edited).
*
* Specifically, this means URLs only affected by direct revision edits,
* will not be purged.
* @var int
*/
public const PURGE_URLS_LINKSUPDATE_ONLY = 4;
/**
* @param int $reboundDelay $wgCdnReboundPurgeDelay
* @param bool $useFileCache $wgUseFileCache
@ -64,6 +74,15 @@ class HtmlCacheUpdater {
$this->useFileCache = $useFileCache;
}
/**
* @param int $flags Bit field
* @param int $flag Constant to check for
* @return bool If $flags contains $flag
*/
private function fieldHasFlag( $flags, $flag ) {
return ( ( $flags & $flag ) === $flag );
}
/**
* Purge the CDN for a URL or list of URLs
*
@ -74,12 +93,12 @@ class HtmlCacheUpdater {
public function purgeUrls( $urls, $flags = self::PURGE_PRESEND ) {
$urls = is_string( $urls ) ? [ $urls ] : $urls;
$reboundDelay = ( ( $flags & self::PURGE_REBOUND ) == self::PURGE_REBOUND )
$reboundDelay = $this->fieldHasFlag( $flags, self::PURGE_REBOUND )
? $this->reboundDelay
: 0; // no second purge
$update = new CdnCacheUpdate( $urls, [ 'reboundDelay' => $reboundDelay ] );
if ( ( $flags & self::PURGE_PRESEND ) == self::PURGE_PRESEND ) {
if ( $this->fieldHasFlag( $flags, self::PURGE_PRESEND ) ) {
DeferredUpdates::addUpdate( $update, DeferredUpdates::PRESEND );
} else {
$update->doUpdate();
@ -101,7 +120,7 @@ class HtmlCacheUpdater {
if ( $this->useFileCache ) {
$update = HtmlFileCacheUpdate::newFromTitles( $titles );
if ( ( $flags & self::PURGE_PRESEND ) == self::PURGE_PRESEND ) {
if ( $this->fieldHasFlag( $flags, self::PURGE_PRESEND ) ) {
DeferredUpdates::addUpdate( $update, DeferredUpdates::PRESEND );
} else {
$update->doUpdate();
@ -115,4 +134,53 @@ class HtmlCacheUpdater {
}
$this->purgeUrls( $urls, $flags );
}
/**
* Get a list of URLs to purge from the CDN cache when this page changes.
*
* @param Title $title
* @param int $flags Bit field of `PURGE_URLS_*` class constants (optional).
* @return string[] URLs
*/
public function getUrls( Title $title, int $flags = 0 ) : array {
// These urls are affected both by direct revisions as well,
// as re-rendering of the same content during a LinksUpdate.
$urls = [
$title->getInternalURL()
];
// Language variant page views are currently not cached
// and thus not purged (T250511).
// These urls are only affected by direct revisions, and do not require
// purging when a LinksUpdate merely rerenders the same content.
// This exists to avoid large amounts of redundant PURGE traffic (T250261).
if ( !$this->fieldHasFlag( $flags, self::PURGE_URLS_LINKSUPDATE_ONLY ) ) {
$urls[] = $title->getInternalURL( 'action=history' );
// Canonical action=raw URLs for user config pages
if ( $title->isUserJsConfigPage() ) {
$urls[] = $title->getInternalURL( 'action=raw&ctype=text/javascript' );
} elseif ( $title->isUserJsonConfigPage() ) {
$urls[] = $title->getInternalURL( 'action=raw&ctype=application/json' );
} elseif ( $title->isUserCssConfigPage() ) {
$urls[] = $title->getInternalURL( 'action=raw&ctype=text/css' );
}
}
// Extensions may add novel ways to access this content
$append = [];
$mode = $flags & self::PURGE_URLS_LINKSUPDATE_ONLY;
Hooks::run( 'HtmlCacheUpdaterAppendUrls', [ $title, $mode, &$append ] );
$urls = array_merge( $urls, $append );
// Extensions may add novel ways to access the site overall
$append = [];
Hooks::run( 'HtmlCacheUpdaterVaryUrls', [ $urls, &$append ] );
$urls = array_merge( $urls, $append );
// Legacy. TODO: Deprecate this
Hooks::run( 'TitleSquidURLs', [ $title, &$urls ] );
return $urls;
}
}

View file

@ -1723,15 +1723,5 @@ class TitleTest extends MediaWikiTestCase {
Title::makeTitle( NS_MAIN, 'Example' )->getCdnUrls(),
'article'
);
$this->assertEquals(
[
'https://example.org/wiki/User:Example/foo.js',
'https://example.org/w/index.php?title=User:Example/foo.js&action=history',
'https://example.org/w/index.php?title=User:Example/foo.js&action=raw&ctype=text/javascript',
],
Title::makeTitle( NS_USER, 'Example/foo.js' )->getCdnUrls(),
'user config page'
);
}
}

View file

@ -0,0 +1,47 @@
<?php
/**
* @group Cache
* @covers HtmlCacheUpdater
*/
class HtmlCacheUpdaterTest extends MediaWikiUnitTestCase {
public function testGetCdnUrls() {
$htmlCache = new HtmlCacheUpdater( 0, false );
$title = $this->createMock( Title::class );
$title->method( 'getInternalURL' )->will( $this->returnCallback( function ( $query = '' ) {
return 'https://test/?title=Example' . ( $query !== '' ? "&$query" : '' );
} ) );
$this->assertEquals(
[
'https://test/?title=Example',
'https://test/?title=Example&action=history',
],
$htmlCache->getUrls( $title ),
'all urls for an article'
);
$this->assertEquals(
[
'https://test/?title=Example',
],
$htmlCache->getUrls( $title, $htmlCache::PURGE_URLS_LINKSUPDATE_ONLY ),
'linkupdate urls for an article'
);
$title = $this->createMock( Title::class );
$title->method( 'getInternalURL' )->will( $this->returnCallback( function ( $query = '' ) {
return 'https://test/?title=User:Example/foo.js' . ( $query !== '' ? "&$query" : '' );
} ) );
$title->method( 'isUserJsConfigPage' )->willReturn( true );
$this->assertEquals(
[
'https://test/?title=User:Example/foo.js',
'https://test/?title=User:Example/foo.js&action=history',
'https://test/?title=User:Example/foo.js&action=raw&ctype=text/javascript',
],
$htmlCache->getUrls( $title ),
'all urls for a user js page'
);
}
}