Merge "rdbms: Use "mixed" type for $profiler field in LoadBalancer"
[lhc/web/wiklou.git] / includes / libs / rdbms / loadbalancer / LoadBalancer.php
index f36d98e..2a6175f 100644 (file)
@@ -28,6 +28,7 @@ use BagOStuff;
 use EmptyBagOStuff;
 use WANObjectCache;
 use ArrayUtils;
+use UnexpectedValueException;
 use InvalidArgumentException;
 use RuntimeException;
 use Exception;
@@ -46,7 +47,7 @@ class LoadBalancer implements ILoadBalancer {
        private $srvCache;
        /** @var WANObjectCache */
        private $wanCache;
-       /** @var object|string Class name or object With profileIn/profileOut methods */
+       /** @var mixed Class name or object With profileIn/profileOut methods */
        private $profiler;
        /** @var TransactionProfiler */
        private $trxProfiler;
@@ -203,11 +204,7 @@ class LoadBalancer implements ILoadBalancer {
                        $this->maxLag = $params['maxLag'];
                }
 
-               if ( isset( $params['loadMonitor'] ) ) {
-                       $this->loadMonitorConfig = $params['loadMonitor'];
-               } else {
-                       $this->loadMonitorConfig = [ 'class' => 'LoadMonitorNull' ];
-               }
+               $this->loadMonitorConfig = $params['loadMonitor'] ?? [ 'class' => 'LoadMonitorNull' ];
                $this->loadMonitorConfig += [ 'lagWarnThreshold' => $this->maxLag ];
 
                foreach ( $params['servers'] as $i => $server ) {
@@ -222,22 +219,10 @@ class LoadBalancer implements ILoadBalancer {
                        }
                }
 
-               if ( isset( $params['srvCache'] ) ) {
-                       $this->srvCache = $params['srvCache'];
-               } else {
-                       $this->srvCache = new EmptyBagOStuff();
-               }
-               if ( isset( $params['wanCache'] ) ) {
-                       $this->wanCache = $params['wanCache'];
-               } else {
-                       $this->wanCache = WANObjectCache::newEmpty();
-               }
+               $this->srvCache = $params['srvCache'] ?? new EmptyBagOStuff();
+               $this->wanCache = $params['wanCache'] ?? WANObjectCache::newEmpty();
                $this->profiler = $params['profiler'] ?? null;
-               if ( isset( $params['trxProfiler'] ) ) {
-                       $this->trxProfiler = $params['trxProfiler'];
-               } else {
-                       $this->trxProfiler = new TransactionProfiler();
-               }
+               $this->trxProfiler = $params['trxProfiler'] ?? new TransactionProfiler();
 
                $this->errorLogger = $params['errorLogger'] ?? function ( Exception $e ) {
                        trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING );
@@ -952,6 +937,16 @@ class LoadBalancer implements ILoadBalancer {
                        }
                }
 
+               // Final sanity check to make sure the right domain is selected
+               if (
+                       $conn instanceof IDatabase &&
+                       !$this->localDomain->isCompatible( $conn->getDomainID() )
+               ) {
+                       throw new UnexpectedValueException(
+                               "Got connection to '{$conn->getDomainID()}', " .
+                               "but expected local domain ('{$this->localDomain}')." );
+               }
+
                return $conn;
        }
 
@@ -974,12 +969,11 @@ class LoadBalancer implements ILoadBalancer {
         * @param int $i Server index
         * @param string $domain Domain ID to open
         * @param int $flags Class CONN_* constant bitfield
-        * @return Database
+        * @return Database|bool Returns false on connection error
+        * @throws DBError When database selection fails
         */
        private function openForeignConnection( $i, $domain, $flags = 0 ) {
                $domainInstance = DatabaseDomain::newFromId( $domain );
-               $dbName = $domainInstance->getDatabase();
-               $prefix = $domainInstance->getTablePrefix();
                $autoCommit = ( ( $flags & self::CONN_TRX_AUTOCOMMIT ) == self::CONN_TRX_AUTOCOMMIT );
 
                if ( $autoCommit ) {
@@ -990,6 +984,7 @@ class LoadBalancer implements ILoadBalancer {
                        $connInUseKey = self::KEY_FOREIGN_INUSE;
                }
 
+               /** @var Database $conn */
                if ( isset( $this->conns[$connInUseKey][$i][$domain] ) ) {
                        // Reuse an in-use connection for the same domain
                        $conn = $this->conns[$connInUseKey][$i][$domain];
@@ -1004,19 +999,18 @@ class LoadBalancer implements ILoadBalancer {
                        // Reuse a free connection from another domain
                        $conn = reset( $this->conns[$connFreeKey][$i] );
                        $oldDomain = key( $this->conns[$connFreeKey][$i] );
-                       if ( strlen( $dbName ) && !$conn->selectDB( $dbName ) ) {
-                               $this->lastError = "Error selecting database '$dbName' on server " .
-                                       $conn->getServer() . " from client host {$this->hostname}";
-                               $this->errorConnection = $conn;
-                               $conn = false;
+                       if ( $domainInstance->getDatabase() !== null ) {
+                               $conn->selectDomain( $domainInstance );
                        } else {
-                               $conn->tablePrefix( $prefix );
-                               unset( $this->conns[$connFreeKey][$i][$oldDomain] );
-                               // Note that if $domain is an empty string, getDomainID() might not match it
-                               $this->conns[$connInUseKey][$i][$conn->getDomainId()] = $conn;
-                               $this->connLogger->debug( __METHOD__ .
-                                       ": reusing free connection from $oldDomain for $domain" );
+                               // Stay on the current database, but update the schema/prefix
+                               $conn->dbSchema( $domainInstance->getSchema() );
+                               $conn->tablePrefix( $domainInstance->getTablePrefix() );
                        }
+                       unset( $this->conns[$connFreeKey][$i][$oldDomain] );
+                       // Note that if $domain is an empty string, getDomainID() might not match it
+                       $this->conns[$connInUseKey][$i][$conn->getDomainId()] = $conn;
+                       $this->connLogger->debug( __METHOD__ .
+                               ": reusing free connection from $oldDomain for $domain" );
                } else {
                        if ( !isset( $this->servers[$i] ) || !is_array( $this->servers[$i] ) ) {
                                throw new InvalidArgumentException( "No server with index '$i'." );
@@ -1039,8 +1033,13 @@ class LoadBalancer implements ILoadBalancer {
                        }
                }
 
-               // Increment reference count
                if ( $conn instanceof IDatabase ) {
+                       // Final sanity check to make sure the right domain is selected
+                       if ( !$domainInstance->isCompatible( $conn->getDomainID() ) ) {
+                               throw new UnexpectedValueException(
+                                       "Got connection to '{$conn->getDomainID()}', but expected '$domain'." );
+                       }
+                       // Increment reference count
                        $refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
                        $conn->setLBInfo( 'foreignPoolRefCount', $refCount + 1 );
                }
@@ -1208,23 +1207,13 @@ class LoadBalancer implements ILoadBalancer {
        }
 
        public function getServerName( $i ) {
-               if ( isset( $this->servers[$i]['hostName'] ) ) {
-                       $name = $this->servers[$i]['hostName'];
-               } elseif ( isset( $this->servers[$i]['host'] ) ) {
-                       $name = $this->servers[$i]['host'];
-               } else {
-                       $name = '';
-               }
+               $name = $this->servers[$i]['hostName'] ?? $this->servers[$i]['host'] ?? '';
 
                return ( $name != '' ) ? $name : 'localhost';
        }
 
        public function getServerInfo( $i ) {
-               if ( isset( $this->servers[$i] ) ) {
-                       return $this->servers[$i];
-               } else {
-                       return false;
-               }
+               return $this->servers[$i] ?? false;
        }
 
        public function getServerType( $i ) {
@@ -1337,12 +1326,7 @@ class LoadBalancer implements ILoadBalancer {
                        // If atomic sections or explicit transactions are still open, some caller must have
                        // caught an exception but failed to properly rollback any changes. Detect that and
                        // throw and error (causing rollback).
-                       if ( $conn->explicitTrxActive() ) {
-                               throw new DBTransactionError(
-                                       $conn,
-                                       "Explicit transaction still active. A caller may have caught an error."
-                               );
-                       }
+                       $conn->assertNoOpenTransactions();
                        // Assert that the time to replicate the transaction will be sane.
                        // If this fails, then all DB transactions will be rollback back together.
                        $time = $conn->pendingWriteQueryDuration( $conn::ESTIMATE_DB_APPLY );