From: Cormac Parle Date: Wed, 10 Jan 2018 18:02:02 +0000 (+0000) Subject: Clear the backlink cache on file delete X-Git-Tag: 1.31.0-rc.0~899^2 X-Git-Url: http://git.cyclocoop.org//%22javascript:ModifierStyle%28%27%22.%24id.%22%27%29/%22?a=commitdiff_plain;h=23ead9bbd6e4b626c6c9924ccd4d83f1174d01d1;p=lhc%2Fweb%2Fwiklou.git Clear the backlink cache on file delete When a file is deleted pages that link to the file (backlinks) are purged. The set of backlinks for a file is cached in the backlink cache, and this is where the set of backlinks that need purging is read from. If, at file delete time, there is a backlink cache for the file but it is not up to date, then backlinks missing from the set of cached backlinks for that file will not be purged, leading to broken links. This patch clears the backlink cache on file delete before initiating purging of backlinks. Bug: T183478 Change-Id: I3bbd79e5a8fa14bf80ceee81e944108edada322e --- diff --git a/includes/cache/BacklinkCache.php b/includes/cache/BacklinkCache.php index 4341daafe6..48809d07e0 100644 --- a/includes/cache/BacklinkCache.php +++ b/includes/cache/BacklinkCache.php @@ -28,6 +28,7 @@ use Wikimedia\Rdbms\ResultWrapper; use Wikimedia\Rdbms\FakeResultWrapper; use Wikimedia\Rdbms\IDatabase; +use MediaWiki\MediaWikiServices; /** * Class for fetching backlink lists, approximate backlink counts and @@ -70,6 +71,11 @@ class BacklinkCache { */ protected $fullResultCache = []; + /** + * @var WANObjectCache + */ + protected $wanCache; + /** * Local copy of a database object. * @@ -93,6 +99,7 @@ class BacklinkCache { */ public function __construct( Title $title ) { $this->title = $title; + $this->wanCache = MediaWikiServices::getInstance()->getMainWANObjectCache(); } /** @@ -122,11 +129,12 @@ class BacklinkCache { } /** - * Clear locally stored data and database object. + * Clear locally stored data and database object. Invalidate data in memcache. */ public function clear() { $this->partitionCache = []; $this->fullResultCache = []; + $this->wanCache->touchCheckKey( $this->makeCheckKey() ); unset( $this->db ); } @@ -324,7 +332,6 @@ class BacklinkCache { public function getNumLinks( $table, $max = INF ) { global $wgUpdateRowsPerJob; - $cache = ObjectCache::getMainWANInstance(); // 1) try partition cache ... if ( isset( $this->partitionCache[$table] ) ) { $entry = reset( $this->partitionCache[$table] ); @@ -337,15 +344,22 @@ class BacklinkCache { return min( $max, $this->fullResultCache[$table]->numRows() ); } - $memcKey = $cache->makeKey( + $memcKey = $this->wanCache->makeKey( 'numbacklinks', md5( $this->title->getPrefixedDBkey() ), $table ); // 3) ... fallback to memcached ... - $count = $cache->get( $memcKey ); - if ( $count ) { + $curTTL = INF; + $count = $this->wanCache->get( + $memcKey, + $curTTL, + [ + $this->makeCheckKey() + ] + ); + if ( $count && ( $curTTL > 0 ) ) { return min( $max, $count ); } @@ -359,7 +373,7 @@ class BacklinkCache { // Fetch the full title info, since the caller will likely need it next $count = $this->getLinks( $table, false, false, $max )->count(); if ( $count < $max ) { // full count - $cache->set( $memcKey, $count, self::CACHE_EXPIRY ); + $this->wanCache->set( $memcKey, $count, self::CACHE_EXPIRY ); } } @@ -383,7 +397,6 @@ class BacklinkCache { return $this->partitionCache[$table][$batchSize]['batches']; } - $cache = ObjectCache::getMainWANInstance(); $this->partitionCache[$table][$batchSize] = false; $cacheEntry =& $this->partitionCache[$table][$batchSize]; @@ -395,7 +408,7 @@ class BacklinkCache { return $cacheEntry['batches']; } - $memcKey = $cache->makeKey( + $memcKey = $this->wanCache->makeKey( 'backlinks', md5( $this->title->getPrefixedDBkey() ), $table, @@ -403,8 +416,15 @@ class BacklinkCache { ); // 3) ... fallback to memcached ... - $memcValue = $cache->get( $memcKey ); - if ( is_array( $memcValue ) ) { + $curTTL = 0; + $memcValue = $this->wanCache->get( + $memcKey, + $curTTL, + [ + $this->makeCheckKey() + ] + ); + if ( is_array( $memcValue ) && ( $curTTL > 0 ) ) { $cacheEntry = $memcValue; wfDebug( __METHOD__ . ": got from memcached $memcKey\n" ); @@ -435,15 +455,15 @@ class BacklinkCache { } // Save partitions to memcached - $cache->set( $memcKey, $cacheEntry, self::CACHE_EXPIRY ); + $this->wanCache->set( $memcKey, $cacheEntry, self::CACHE_EXPIRY ); // Save backlink count to memcached - $memcKey = $cache->makeKey( + $memcKey = $this->wanCache->makeKey( 'numbacklinks', md5( $this->title->getPrefixedDBkey() ), $table ); - $cache->set( $memcKey, $cacheEntry['numRows'], self::CACHE_EXPIRY ); + $this->wanCache->set( $memcKey, $cacheEntry['numRows'], self::CACHE_EXPIRY ); wfDebug( __METHOD__ . ": got from database\n" ); @@ -543,4 +563,16 @@ class BacklinkCache { return TitleArray::newFromResult( new FakeResultWrapper( array_values( $mergedRes ) ) ); } + + /** + * Returns check key for the backlinks cache for a particular title + * + * @return String + */ + private function makeCheckKey() { + return $this->wanCache->makeKey( + 'backlinks', + md5( $this->title->getPrefixedDBkey() ) + ); + } } diff --git a/includes/page/WikiPage.php b/includes/page/WikiPage.php index 5029d1d788..68476715f8 100644 --- a/includes/page/WikiPage.php +++ b/includes/page/WikiPage.php @@ -3353,6 +3353,8 @@ class WikiPage implements Page, IDBAccessObject { */ public static function onArticleDelete( Title $title ) { // Update existence markers on article/talk tabs... + // Clear Backlink cache first so that purge jobs use more up-to-date backlink information + BacklinkCache::get( $title )->clear(); $other = $title->getOtherPage(); $other->purgeSquid();