From: Aaron Schulz Date: Fri, 8 Jun 2018 20:43:03 +0000 (-0700) Subject: Make interwiki transclusion use the WAN cache X-Git-Tag: 1.34.0-rc.0~4289^2 X-Git-Url: https://git.cyclocoop.org/%7B%24www_url%7Dadmin/compta/banques/%7B%7B%20url_for%28%27admin_users%27%29%20%7D%7D?a=commitdiff_plain;h=6504e230743da300795304f174fe1d8fccd27175;p=lhc%2Fweb%2Fwiklou.git Make interwiki transclusion use the WAN cache This means that now: * Entries actually get deleted when expired * The transclusion cache is shared across wikis * Large blobs that do not fit in cache no longer cause DB errors * DB writes are not triggered on GET requests * Keys are hashed and no longer need to be so restrictive Also, add a "check key" based purge system and process cache the text/html values similar to how regular revision text is cached. Bug: T189702 Change-Id: I8ac12b53c02bb26857175dd5a4af29d49e03dc33 --- diff --git a/includes/page/WikiPage.php b/includes/page/WikiPage.php index a1b2e574d1..e1b37c0961 100644 --- a/includes/page/WikiPage.php +++ b/includes/page/WikiPage.php @@ -3152,6 +3152,9 @@ class WikiPage implements Page, IDBAccessObject { // Image redirects RepoGroup::singleton()->getLocalRepo()->invalidateImageRedirect( $title ); + + // Purge cross-wiki cache entities referencing this page + self::purgeInterwikiCheckKey( $title ); } /** @@ -3190,14 +3193,41 @@ class WikiPage implements Page, IDBAccessObject { // Clear file cache for this page only HTMLFileCache::clearFileCache( $title ); + // Purge ?action=info cache $revid = $revision ? $revision->getId() : null; DeferredUpdates::addCallableUpdate( function () use ( $title, $revid ) { InfoAction::invalidateCache( $title, $revid ); } ); + + // Purge cross-wiki cache entities referencing this page + self::purgeInterwikiCheckKey( $title ); } /**#@-*/ + /** + * Purge the check key for cross-wiki cache entries referencing this page + * + * @param Title $title + */ + private static function purgeInterwikiCheckKey( Title $title ) { + global $wgEnableScaryTranscluding; + + if ( !$wgEnableScaryTranscluding ) { + return; // @todo: perhaps this wiki is only used as a *source* for content? + } + + DeferredUpdates::addCallableUpdate( function () use ( $title ) { + $cache = MediaWikiServices::getInstance()->getMainWANObjectCache(); + $cache->resetCheckKey( + // Do not include the namespace since there can be multiple aliases to it + // due to different namespace text definitions on different wikis. This only + // means that some cache invalidations happen that are not strictly needed. + $cache->makeGlobalKey( 'interwiki-page', wfWikiID(), $title->getDBkey() ) + ); + } ); + } + /** * Returns a list of categories this page is a member of. * Results will include hidden categories diff --git a/includes/parser/Parser.php b/includes/parser/Parser.php index 12d899bc34..3920e7bce6 100644 --- a/includes/parser/Parser.php +++ b/includes/parser/Parser.php @@ -3739,57 +3739,68 @@ class Parser { * Transclude an interwiki link. * * @param Title $title - * @param string $action + * @param string $action Usually one of (raw, render) * * @return string */ public function interwikiTransclude( $title, $action ) { - global $wgEnableScaryTranscluding; + global $wgEnableScaryTranscluding, $wgTranscludeCacheExpiry; if ( !$wgEnableScaryTranscluding ) { return wfMessage( 'scarytranscludedisabled' )->inContentLanguage()->text(); } $url = $title->getFullURL( [ 'action' => $action ] ); - - if ( strlen( $url ) > 255 ) { + if ( strlen( $url ) > 1024 ) { return wfMessage( 'scarytranscludetoolong' )->inContentLanguage()->text(); } - return $this->fetchScaryTemplateMaybeFromCache( $url ); - } - /** - * @param string $url - * @return mixed|string - */ - public function fetchScaryTemplateMaybeFromCache( $url ) { - global $wgTranscludeCacheExpiry; - $dbr = wfGetDB( DB_REPLICA ); - $tsCond = $dbr->timestamp( time() - $wgTranscludeCacheExpiry ); - $obj = $dbr->selectRow( 'transcache', [ 'tc_time', 'tc_contents' ], - [ 'tc_url' => $url, "tc_time >= " . $dbr->addQuotes( $tsCond ) ] ); - if ( $obj ) { - return $obj->tc_contents; - } - - $req = MWHttpRequest::factory( $url, [], __METHOD__ ); - $status = $req->execute(); // Status object - if ( $status->isOK() ) { - $text = $req->getContent(); - } elseif ( $req->getStatus() != 200 ) { + $wikiId = $title->getTransWikiID(); // remote wiki ID or false + + $fname = __METHOD__; + $cache = MediaWikiServices::getInstance()->getMainWANObjectCache(); + + $data = $cache->getWithSetCallback( + $cache->makeGlobalKey( + 'interwiki-transclude', + ( $wikiId !== false ) ? $wikiId : 'external', + sha1( $url ) + ), + $wgTranscludeCacheExpiry, + function ( $oldValue, &$ttl ) use ( $url, $fname, $cache ) { + $req = MWHttpRequest::factory( $url, [], $fname ); + + $status = $req->execute(); // Status object + if ( !$status->isOK() ) { + $ttl = $cache::TTL_UNCACHEABLE; + } elseif ( $req->getResponseHeader( 'X-Database-Lagged' ) !== null ) { + $ttl = min( $cache::TTL_LAGGED, $ttl ); + } + + return [ + 'text' => $status->isOK() ? $req->getContent() : null, + 'code' => $req->getStatus() + ]; + }, + [ + 'checkKeys' => ( $wikiId !== false ) + ? [ $cache->makeGlobalKey( 'interwiki-page', $wikiId, $title->getDBkey() ) ] + : [], + 'pcGroup' => 'interwiki-transclude:5', + 'pcTTL' => $cache::TTL_PROC_LONG + ] + ); + + if ( is_string( $data['text'] ) ) { + $text = $data['text']; + } elseif ( $data['code'] != 200 ) { // Though we failed to fetch the content, this status is useless. - return wfMessage( 'scarytranscludefailed-httpstatus' ) - ->params( $url, $req->getStatus() /* HTTP status */ )->inContentLanguage()->text(); + $text = wfMessage( 'scarytranscludefailed-httpstatus' ) + ->params( $url, $data['code'] )->inContentLanguage()->text(); } else { - return wfMessage( 'scarytranscludefailed', $url )->inContentLanguage()->text(); + $text = wfMessage( 'scarytranscludefailed', $url )->inContentLanguage()->text(); } - $dbw = wfGetDB( DB_MASTER ); - $dbw->replace( 'transcache', [ 'tc_url' ], [ - 'tc_url' => $url, - 'tc_time' => $dbw->timestamp( time() ), - 'tc_contents' => $text - ] ); return $text; }