Aaron Schulz has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/373147 )
Change subject: Avoid DB replication waits for farm cross-wiki redirects ...................................................................... Avoid DB replication waits for farm cross-wiki redirects This previously only worked if $wgLocalVirtualHosts was set, which was too specific to check and not used by WMF. Use the more generic WikiMap class. Two methods have been added there to do the work of enumerating canonical wiki farm URLs and checking them against a given URL. Bug: T172357 Change-Id: Id2415bab5d7f5a08b9f536858c32d329138384a2 --- M includes/MediaWiki.php M includes/WikiMap.php M tests/phpunit/includes/WikiMapTest.php 3 files changed, 127 insertions(+), 29 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core refs/changes/47/373147/1 diff --git a/includes/MediaWiki.php b/includes/MediaWiki.php index 10b9e2b..7b59ee9 100644 --- a/includes/MediaWiki.php +++ b/includes/MediaWiki.php @@ -607,7 +607,7 @@ $request->wasPosted() && $output->getRedirect() && $lbFactory->hasOrMadeRecentMasterChanges( INF ) - ) ? self::getUrlDomainDistance( $output->getRedirect(), $context ) : false; + ) ? self::getUrlDomainDistance( $output->getRedirect() ) : false; $allowHeaders = !( $output->isDisabled() || headers_sent() ); if ( $urlDomainDistance === 'local' || $urlDomainDistance === 'remote' ) { @@ -676,34 +676,14 @@ /** * @param string $url - * @param IContextSource $context * @return string Either "local", "remote" if in the farm, "external" otherwise */ - private static function getUrlDomainDistance( $url, IContextSource $context ) { - static $relevantKeys = [ 'host' => true, 'port' => true ]; - - $infoCandidate = wfParseUrl( $url ); - if ( $infoCandidate === false ) { - return 'external'; - } - - $infoCandidate = array_intersect_key( $infoCandidate, $relevantKeys ); - $clusterHosts = array_merge( - // Local wiki host (the most common case) - [ $context->getConfig()->get( 'CanonicalServer' ) ], - // Any local/remote wiki virtual hosts for this wiki farm - $context->getConfig()->get( 'LocalVirtualHosts' ) - ); - - foreach ( $clusterHosts as $i => $clusterHost ) { - $parseUrl = wfParseUrl( $clusterHost ); - if ( !$parseUrl ) { - continue; - } - $infoHost = array_intersect_key( $parseUrl, $relevantKeys ); - if ( $infoCandidate === $infoHost ) { - return ( $i === 0 ) ? 'local' : 'remote'; - } + private static function getUrlDomainDistance( $url ) { + $clusterWiki = WikiMap::getWikiFromUrl( $url ); + if ( $clusterWiki === wfWikiID() ) { + return 'local'; // the current wiki + } elseif ( $clusterWiki !== false ) { + return 'remote'; // another wiki in this cluster/farm } return 'external'; diff --git a/includes/WikiMap.php b/includes/WikiMap.php index 6a532e5..2e10f9f 100644 --- a/includes/WikiMap.php +++ b/includes/WikiMap.php @@ -20,8 +20,10 @@ * @file */ +use MediaWiki\MediaWikiServices; + /** - * Helper tools for dealing with other wikis. + * Helper tools for dealing with other locally-hosted wikis. */ class WikiMap { @@ -81,7 +83,7 @@ * @return WikiReference|null WikiReference object or null if the wiki was not found */ private static function getWikiWikiReferenceFromSites( $wikiID ) { - $siteLookup = \MediaWiki\MediaWikiServices::getInstance()->getSiteLookup(); + $siteLookup = MediaWikiServices::getInstance()->getSiteLookup(); $site = $siteLookup->getSite( $wikiID ); if ( !$site instanceof MediaWikiSite ) { @@ -174,4 +176,67 @@ return false; } + + /** + * Get canonical server info for all local wikis in the map that have one + * + * @return array Map of (local wiki ID => map of (url,parts)) + * @since 1.30 + */ + public static function getCanonicalServerInfoForAllWikis() { + $cache = MediaWikiServices::getInstance()->getLocalServerObjectCache(); + + return $cache->getWithSetCallback( + $cache->makeGlobalKey( 'wikimap', 'canonical-urls' ), + $cache::TTL_DAY, + function () { + global $wgLocalDatabases, $wgCanonicalServer; + + $infoMap = []; + // Make sure at least the current wiki is set, for simple configurations. + // This also makes it the first in the map, which is useful for common cases. + $infoMap[wfWikiID()] = [ + 'url' => $wgCanonicalServer, + 'parts' => wfParseUrl( $wgCanonicalServer ) + ]; + + foreach ( $wgLocalDatabases as $wikiId ) { + $wikiReeference = self::getWiki( $wikiId ); + if ( $wikiReeference ) { + $url = $wikiReeference->getCanonicalServer(); + $infoMap[$wikiId] = [ 'url' => $url, 'parts' => wfParseUrl( $url ) ]; + } + } + + return $infoMap; + } + ); + } + + /** + * @param string $url + * @return bool|string Wiki ID or false + * @since 1.30 + */ + public static function getWikiFromUrl( $url ) { + $urlPartsCheck = wfParseUrl( $url ); + if ( $urlPartsCheck === false ) { + return false; + } + + $urlPartsCheck = array_intersect_key( $urlPartsCheck, [ 'host' => 1, 'port' => 1 ] ); + foreach ( self::getCanonicalServerInfoForAllWikis() as $wikiId => $info ) { + $urlParts = $info['parts']; + if ( $urlParts === false ) { + continue; // sanity + } + + $urlParts = array_intersect_key( $urlParts, [ 'host' => 1, 'port' => 1 ] ); + if ( $urlParts == $urlPartsCheck ) { + return $wikiId; + } + } + + return false; + } } diff --git a/tests/phpunit/includes/WikiMapTest.php b/tests/phpunit/includes/WikiMapTest.php index 12878b3..ed1dc56 100644 --- a/tests/phpunit/includes/WikiMapTest.php +++ b/tests/phpunit/includes/WikiMapTest.php @@ -16,6 +16,7 @@ 'enwiki' => 'http://en.example.org', 'ruwiki' => '//ru.example.org', 'nopathwiki' => '//nopath.example.org', + 'thiswiki' => '//this.wiki.org' ], 'wgArticlePath' => [ 'enwiki' => '/w/$1', @@ -25,6 +26,10 @@ $conf->suffixes = [ 'wiki' ]; $this->setMwGlobals( [ 'wgConf' => $conf, + 'wgLocalDatabases' => [ 'enwiki', 'ruwiki', 'nopathwiki' ], + 'wgCanonicalServer' => '//this.wiki.org', + 'wgDBname' => 'thiswiki', + 'wgDBprefix' => '' ] ); TestSites::insertIntoDb(); @@ -175,4 +180,52 @@ $this->assertEquals( $expected, WikiMap::getForeignURL( $wikiId, $page, $fragment ) ); } + public function testGetCanonicalServerInfoForAllWikis() { + $expected = [ + 'thiswiki' => [ + 'url' => '//this.wiki.org', + 'parts' => [ 'scheme' => '', 'host' => 'this.wiki.org', 'delimiter' => '//' ] + ], + 'enwiki' => [ + 'url' => 'http://en.example.org', + 'parts' => [ + 'scheme' => 'http', 'host' => 'en.example.org', 'delimiter' => '://' ] + ], + 'ruwiki' => [ + 'url' => '//ru.example.org', + 'parts' => [ 'scheme' => '', 'host' => 'ru.example.org', 'delimiter' => '//' ] + ] + ]; + + $this->assertArrayEquals( + $expected, + WikiMap::getCanonicalServerInfoForAllWikis(), + true, + true + ); + } + + public function provideGetWikiFromUrl() { + return [ + [ 'http://this.wiki.org', 'thiswiki' ], + [ 'https://this.wiki.org', 'thiswiki' ], + [ 'http://this.wiki.org/$1', 'thiswiki' ], + [ 'https://this.wiki.org/$2', 'thiswiki' ], + [ 'http://en.example.org', 'enwiki' ], + [ 'https://en.example.org', 'enwiki' ], + [ 'http://en.example.org/$1', 'enwiki' ], + [ 'https://en.example.org/$2', 'enwiki' ], + [ 'http://ru.example.org', 'ruwiki' ], + [ 'https://ru.example.org', 'ruwiki' ], + [ 'http://ru.example.org/$1', 'ruwiki' ], + [ 'https://ru.example.org/$2', 'ruwiki' ], + ]; + } + + /** + * @dataProvider provideGetWikiFromUrl + */ + public function testGetWikiFromUrl( $url, $wiki ) { + $this->assertEquals( $wiki, WikiMap::getWikiFromUrl( $url ) ); + } } -- To view, visit https://gerrit.wikimedia.org/r/373147 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Id2415bab5d7f5a08b9f536858c32d329138384a2 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/core Gerrit-Branch: master Gerrit-Owner: Aaron Schulz <asch...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits