From: jenkins-bot Date: Fri, 25 Aug 2017 23:13:52 +0000 (+0000) Subject: Merge "Avoid preemptive DB replication waits for farm cross-wiki redirects" X-Git-Tag: 1.31.0-rc.0~2296 X-Git-Url: http://git.cyclocoop.org/%7B%24admin_url%7Dmes_infos.php?a=commitdiff_plain;h=0a176a1e01d67d6a15dc9435a57018d5082e1b35;hp=5bb2ceb7e9678bbe0f202dd05f857007b6101f8e;p=lhc%2Fweb%2Fwiklou.git Merge "Avoid preemptive DB replication waits for farm cross-wiki redirects" --- diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 5d95964e93..44461b36f9 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -2055,8 +2055,8 @@ $wgDBmysql5 = false; $wgDBOracleDRCP = false; /** - * Other wikis on this site, can be administered from a single developer - * account. + * Other wikis on this site, can be administered from a single developer account. + * * Array numeric key => database name */ $wgLocalDatabases = []; @@ -8303,8 +8303,6 @@ $wgHTTPProxy = false; * subdomain thereof, then no proxy will be used. * Command-line scripts are not affected by this setting and will always use * the proxy if it is configured. - * - ChronologyProtector: Decide to shutdown LBFactory asynchronously instead - * synchronously if the current response redirects to a local virtual host. * * @since 1.25 */ diff --git a/includes/MediaWiki.php b/includes/MediaWiki.php index 10b9e2b9ca..7b59ee93dc 100644 --- a/includes/MediaWiki.php +++ b/includes/MediaWiki.php @@ -607,7 +607,7 @@ class MediaWiki { $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 @@ class MediaWiki { /** * @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 6a532e5da5..4f3c461699 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 @@ class WikiMap { * @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 @@ class WikiMap { 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 ) { + $wikiReference = self::getWiki( $wikiId ); + if ( $wikiReference ) { + $url = $wikiReference->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 12878b37ed..186ffdbcc3 100644 --- a/tests/phpunit/includes/WikiMapTest.php +++ b/tests/phpunit/includes/WikiMapTest.php @@ -16,6 +16,7 @@ class WikiMapTest extends MediaWikiLangTestCase { '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 @@ class WikiMapTest extends MediaWikiLangTestCase { $conf->suffixes = [ 'wiki' ]; $this->setMwGlobals( [ 'wgConf' => $conf, + 'wgLocalDatabases' => [ 'enwiki', 'ruwiki', 'nopathwiki' ], + 'wgCanonicalServer' => '//this.wiki.org', + 'wgDBname' => 'thiswiki', + 'wgDBprefix' => '' ] ); TestSites::insertIntoDb(); @@ -175,4 +180,57 @@ class WikiMapTest extends MediaWikiLangTestCase { $this->assertEquals( $expected, WikiMap::getForeignURL( $wikiId, $page, $fragment ) ); } + /** + * @covers WikiMap::getCanonicalServerInfoForAllWikis() + */ + 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' ], + [ 'http://not.defined.org', false ] + ]; + } + + /** + * @dataProvider provideGetWikiFromUrl + * @covers WikiMap::getWikiFromUrl() + */ + public function testGetWikiFromUrl( $url, $wiki ) { + $this->assertEquals( $wiki, WikiMap::getWikiFromUrl( $url ) ); + } }