Merge "Avoid preemptive DB replication waits for farm cross-wiki redirects"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Fri, 25 Aug 2017 23:13:52 +0000 (23:13 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Fri, 25 Aug 2017 23:13:52 +0000 (23:13 +0000)
includes/DefaultSettings.php
includes/MediaWiki.php
includes/WikiMap.php
tests/phpunit/includes/WikiMapTest.php

index 5d95964..44461b3 100644 (file)
@@ -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
  */
index 10b9e2b..7b59ee9 100644 (file)
@@ -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';
index 6a532e5..4f3c461 100644 (file)
  * @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;
+       }
 }
index 12878b3..186ffdb 100644 (file)
@@ -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 ) );
+       }
 }