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

Reply via email to