rdbms: reduce LoadBalancer replication log spam
[lhc/web/wiklou.git] / includes / libs / rdbms / loadbalancer / LoadBalancer.php
index f410882..d5e65cd 100644 (file)
@@ -39,26 +39,28 @@ use Exception;
  */
 class LoadBalancer implements ILoadBalancer {
        /** @var array[] Map of (server index => server config array) */
-       private $mServers;
+       private $servers;
        /** @var Database[][][] Map of (connection category => server index => IDatabase[]) */
-       private $mConns;
+       private $conns;
        /** @var float[] Map of (server index => weight) */
-       private $mLoads;
+       private $loads;
        /** @var array[] Map of (group => server index => weight) */
-       private $mGroupLoads;
+       private $groupLoads;
        /** @var bool Whether to disregard replica DB lag as a factor in replica DB selection */
-       private $mAllowLagged;
+       private $allowLagged;
        /** @var int Seconds to spend waiting on replica DB lag to resolve */
-       private $mWaitTimeout;
+       private $waitTimeout;
        /** @var array The LoadMonitor configuration */
        private $loadMonitorConfig;
        /** @var array[] $aliases Map of (table => (dbname, schema, prefix) map) */
        private $tableAliases = [];
+       /** @var string[] Map of (index alias => index) */
+       private $indexAliases = [];
 
        /** @var ILoadMonitor */
        private $loadMonitor;
-       /** @var ChronologyProtector|null */
-       private $chronProt;
+       /** @var callable|null Callback to run before the first connection attempt */
+       private $chronologyCallback;
        /** @var BagOStuff */
        private $srvCache;
        /** @var WANObjectCache */
@@ -79,15 +81,15 @@ class LoadBalancer implements ILoadBalancer {
        /** @var Database DB connection object that caused a problem */
        private $errorConnection;
        /** @var int The generic (not query grouped) replica DB index (of $mServers) */
-       private $mReadIndex;
+       private $readIndex;
        /** @var bool|DBMasterPos False if not set */
-       private $mWaitForPos;
+       private $waitForPos;
        /** @var bool Whether the generic reader fell back to a lagged replica DB */
        private $laggedReplicaMode = false;
        /** @var bool Whether the generic reader fell back to a lagged replica DB */
        private $allReplicasDownMode = false;
        /** @var string The last DB selection or connection error */
-       private $mLastError = 'Unknown error';
+       private $lastError = 'Unknown error';
        /** @var string|bool Reason the LB is read-only or false if not */
        private $readOnlyReason = false;
        /** @var int Total connections opened */
@@ -109,11 +111,13 @@ class LoadBalancer implements ILoadBalancer {
 
        /** @var callable Exception logger */
        private $errorLogger;
+       /** @var callable Deprecation logger */
+       private $deprecationLogger;
 
        /** @var bool */
        private $disabled = false;
-       /** @var bool */
-       private $chronProtInitialized = false;
+       /** @var bool Whether any connection has been attempted yet */
+       private $connectionAttempted = false;
        /** @var int */
        private $maxLag = self::MAX_LAG_DEFAULT;
 
@@ -139,12 +143,12 @@ class LoadBalancer implements ILoadBalancer {
                if ( !isset( $params['servers'] ) ) {
                        throw new InvalidArgumentException( __CLASS__ . ': missing servers parameter' );
                }
-               $this->mServers = $params['servers'];
-               foreach ( $this->mServers as $i => $server ) {
+               $this->servers = $params['servers'];
+               foreach ( $this->servers as $i => $server ) {
                        if ( $i == 0 ) {
-                               $this->mServers[$i]['master'] = true;
+                               $this->servers[$i]['master'] = true;
                        } else {
-                               $this->mServers[$i]['replica'] = true;
+                               $this->servers[$i]['replica'] = true;
                        }
                }
 
@@ -153,12 +157,12 @@ class LoadBalancer implements ILoadBalancer {
                        : DatabaseDomain::newUnspecified();
                $this->setLocalDomain( $localDomain );
 
-               $this->mWaitTimeout = isset( $params['waitTimeout'] )
+               $this->waitTimeout = isset( $params['waitTimeout'] )
                        ? $params['waitTimeout']
                        : self::MAX_WAIT_DEFAULT;
 
-               $this->mReadIndex = -1;
-               $this->mConns = [
+               $this->readIndex = -1;
+               $this->conns = [
                        // Connection were transaction rounds may be applied
                        self::KEY_LOCAL => [],
                        self::KEY_FOREIGN_INUSE => [],
@@ -168,9 +172,9 @@ class LoadBalancer implements ILoadBalancer {
                        self::KEY_FOREIGN_INUSE_NOROUND => [],
                        self::KEY_FOREIGN_FREE_NOROUND => []
                ];
-               $this->mLoads = [];
-               $this->mWaitForPos = false;
-               $this->mAllowLagged = false;
+               $this->loads = [];
+               $this->waitForPos = false;
+               $this->allowLagged = false;
 
                if ( isset( $params['readOnlyReason'] ) && is_string( $params['readOnlyReason'] ) ) {
                        $this->readOnlyReason = $params['readOnlyReason'];
@@ -188,13 +192,13 @@ class LoadBalancer implements ILoadBalancer {
                $this->loadMonitorConfig += [ 'lagWarnThreshold' => $this->maxLag ];
 
                foreach ( $params['servers'] as $i => $server ) {
-                       $this->mLoads[$i] = $server['load'];
+                       $this->loads[$i] = $server['load'];
                        if ( isset( $server['groupLoads'] ) ) {
                                foreach ( $server['groupLoads'] as $group => $ratio ) {
-                                       if ( !isset( $this->mGroupLoads[$group] ) ) {
-                                               $this->mGroupLoads[$group] = [];
+                                       if ( !isset( $this->groupLoads[$group] ) ) {
+                                               $this->groupLoads[$group] = [];
                                        }
-                                       $this->mGroupLoads[$group][$i] = $ratio;
+                                       $this->groupLoads[$group][$i] = $ratio;
                                }
                        }
                }
@@ -221,6 +225,11 @@ class LoadBalancer implements ILoadBalancer {
                        : function ( Exception $e ) {
                                trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING );
                        };
+               $this->deprecationLogger = isset( $params['deprecationLogger'] )
+                       ? $params['deprecationLogger']
+                       : function ( $msg ) {
+                               trigger_error( $msg, E_USER_DEPRECATED );
+                       };
 
                foreach ( [ 'replLogger', 'connLogger', 'queryLogger', 'perfLogger' ] as $key ) {
                        $this->$key = isset( $params[$key] ) ? $params[$key] : new NullLogger();
@@ -234,8 +243,8 @@ class LoadBalancer implements ILoadBalancer {
                        : ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' );
                $this->agent = isset( $params['agent'] ) ? $params['agent'] : '';
 
-               if ( isset( $params['chronologyProtector'] ) ) {
-                       $this->chronProt = $params['chronologyProtector'];
+               if ( isset( $params['chronologyCallback'] ) ) {
+                       $this->chronologyCallback = $params['chronologyCallback'];
                }
        }
 
@@ -289,8 +298,8 @@ class LoadBalancer implements ILoadBalancer {
                foreach ( $lags as $i => $lag ) {
                        if ( $i != 0 ) {
                                # How much lag this server nominally is allowed to have
-                               $maxServerLag = isset( $this->mServers[$i]['max lag'] )
-                                       ? $this->mServers[$i]['max lag']
+                               $maxServerLag = isset( $this->servers[$i]['max lag'] )
+                                       ? $this->servers[$i]['max lag']
                                        : $this->maxLag; // default
                                # Constrain that futher by $maxLag argument
                                $maxServerLag = min( $maxServerLag, $maxLag );
@@ -298,11 +307,13 @@ class LoadBalancer implements ILoadBalancer {
                                $host = $this->getServerName( $i );
                                if ( $lag === false && !is_infinite( $maxServerLag ) ) {
                                        $this->replLogger->error(
-                                               "Server {host} is not replicating?", [ 'host' => $host ] );
+                                               __METHOD__ .
+                                               ": server {host} is not replicating?", [ 'host' => $host ] );
                                        unset( $loads[$i] );
                                } elseif ( $lag > $maxServerLag ) {
-                                       $this->replLogger->info(
-                                               "Server {host} has {lag} seconds of lag (>= {maxlag})",
+                                       $this->replLogger->debug(
+                                               __METHOD__ .
+                                               ": server {host} has {lag} seconds of lag (>= {maxlag})",
                                                [ 'host' => $host, 'lag' => $lag, 'maxlag' => $maxServerLag ]
                                        );
                                        unset( $loads[$i] );
@@ -332,18 +343,18 @@ class LoadBalancer implements ILoadBalancer {
        }
 
        public function getReaderIndex( $group = false, $domain = false ) {
-               if ( count( $this->mServers ) == 1 ) {
+               if ( count( $this->servers ) == 1 ) {
                        // Skip the load balancing if there's only one server
                        return $this->getWriterIndex();
-               } elseif ( $group === false && $this->mReadIndex >= 0 ) {
+               } elseif ( $group === false && $this->readIndex >= 0 ) {
                        // Shortcut if the generic reader index was already cached
-                       return $this->mReadIndex;
+                       return $this->readIndex;
                }
 
                if ( $group !== false ) {
                        // Use the server weight array for this load group
-                       if ( isset( $this->mGroupLoads[$group] ) ) {
-                               $loads = $this->mGroupLoads[$group];
+                       if ( isset( $this->groupLoads[$group] ) ) {
+                               $loads = $this->groupLoads[$group];
                        } else {
                                // No loads for this group, return false and the caller can use some other group
                                $this->connLogger->info( __METHOD__ . ": no loads for group $group" );
@@ -352,32 +363,32 @@ class LoadBalancer implements ILoadBalancer {
                        }
                } else {
                        // Use the generic load group
-                       $loads = $this->mLoads;
+                       $loads = $this->loads;
                }
 
                // Scale the configured load ratios according to each server's load and state
                $this->getLoadMonitor()->scaleLoads( $loads, $domain );
 
-               // Pick a server to use, accounting for weights, load, lag, and mWaitForPos
+               // Pick a server to use, accounting for weights, load, lag, and "waitForPos"
                list( $i, $laggedReplicaMode ) = $this->pickReaderIndex( $loads, $domain );
                if ( $i === false ) {
                        // Replica DB connection unsuccessful
                        return false;
                }
 
-               if ( $this->mWaitForPos && $i != $this->getWriterIndex() ) {
+               if ( $this->waitForPos && $i != $this->getWriterIndex() ) {
                        // Before any data queries are run, wait for the server to catch up to the
                        // specified position. This is used to improve session consistency. Note that
-                       // when LoadBalancer::waitFor() sets mWaitForPos, the waiting triggers here,
+                       // when LoadBalancer::waitFor() sets "waitForPos", the waiting triggers here,
                        // so update laggedReplicaMode as needed for consistency.
                        if ( !$this->doWait( $i ) ) {
                                $laggedReplicaMode = true;
                        }
                }
 
-               if ( $this->mReadIndex <= 0 && $this->mLoads[$i] > 0 && $group === false ) {
+               if ( $this->readIndex <= 0 && $this->loads[$i] > 0 && $group === false ) {
                        // Cache the generic reader index for future ungrouped DB_REPLICA handles
-                       $this->mReadIndex = $i;
+                       $this->readIndex = $i;
                        // Record if the generic reader index is in "lagged replica DB" mode
                        if ( $laggedReplicaMode ) {
                                $this->laggedReplicaMode = true;
@@ -408,15 +419,15 @@ class LoadBalancer implements ILoadBalancer {
                // Quickly look through the available servers for a server that meets criteria...
                $currentLoads = $loads;
                while ( count( $currentLoads ) ) {
-                       if ( $this->mAllowLagged || $laggedReplicaMode ) {
+                       if ( $this->allowLagged || $laggedReplicaMode ) {
                                $i = ArrayUtils::pickRandom( $currentLoads );
                        } else {
                                $i = false;
-                               if ( $this->mWaitForPos && $this->mWaitForPos->asOfTime() ) {
-                                       // ChronologyProtecter sets mWaitForPos for session consistency.
+                               if ( $this->waitForPos && $this->waitForPos->asOfTime() ) {
+                                       // "chronologyCallback" sets "waitForPos" for session consistency.
                                        // This triggers doWait() after connect, so it's especially good to
                                        // avoid lagged servers so as to avoid excessive delay in that method.
-                                       $ago = microtime( true ) - $this->mWaitForPos->asOfTime();
+                                       $ago = microtime( true ) - $this->waitForPos->asOfTime();
                                        // Aim for <= 1 second of waiting (being too picky can backfire)
                                        $i = $this->getRandomNonLagged( $currentLoads, $domain, $ago + 1 );
                                }
@@ -426,7 +437,8 @@ class LoadBalancer implements ILoadBalancer {
                                }
                                if ( $i === false && count( $currentLoads ) != 0 ) {
                                        // All replica DBs lagged. Switch to read-only mode
-                                       $this->replLogger->error( "All replica DBs lagged. Switch to read-only mode" );
+                                       $this->replLogger->error(
+                                               __METHOD__ . ": all replica DBs lagged. Switch to read-only mode" );
                                        $i = ArrayUtils::pickRandom( $currentLoads );
                                        $laggedReplicaMode = true;
                                }
@@ -464,18 +476,18 @@ class LoadBalancer implements ILoadBalancer {
 
                // If all servers were down, quit now
                if ( !count( $currentLoads ) ) {
-                       $this->connLogger->error( "All servers down" );
+                       $this->connLogger->error( __METHOD__ . ": all servers down" );
                }
 
                return [ $i, $laggedReplicaMode ];
        }
 
        public function waitFor( $pos ) {
-               $oldPos = $this->mWaitForPos;
+               $oldPos = $this->waitForPos;
                try {
-                       $this->mWaitForPos = $pos;
+                       $this->waitForPos = $pos;
                        // If a generic reader connection was already established, then wait now
-                       $i = $this->mReadIndex;
+                       $i = $this->readIndex;
                        if ( $i > 0 ) {
                                if ( !$this->doWait( $i ) ) {
                                        $this->laggedReplicaMode = true;
@@ -488,14 +500,14 @@ class LoadBalancer implements ILoadBalancer {
        }
 
        public function waitForOne( $pos, $timeout = null ) {
-               $oldPos = $this->mWaitForPos;
+               $oldPos = $this->waitForPos;
                try {
-                       $this->mWaitForPos = $pos;
+                       $this->waitForPos = $pos;
 
-                       $i = $this->mReadIndex;
+                       $i = $this->readIndex;
                        if ( $i <= 0 ) {
                                // Pick a generic replica DB if there isn't one yet
-                               $readLoads = $this->mLoads;
+                               $readLoads = $this->loads;
                                unset( $readLoads[$this->getWriterIndex()] ); // replica DBs only
                                $readLoads = array_filter( $readLoads ); // with non-zero load
                                $i = ArrayUtils::pickRandom( $readLoads );
@@ -508,26 +520,26 @@ class LoadBalancer implements ILoadBalancer {
                        }
                } finally {
                        # Restore the old position, as this is not used for lag-protection but for throttling
-                       $this->mWaitForPos = $oldPos;
+                       $this->waitForPos = $oldPos;
                }
 
                return $ok;
        }
 
        public function waitForAll( $pos, $timeout = null ) {
-               $timeout = $timeout ?: $this->mWaitTimeout;
+               $timeout = $timeout ?: $this->waitTimeout;
 
-               $oldPos = $this->mWaitForPos;
+               $oldPos = $this->waitForPos;
                try {
-                       $this->mWaitForPos = $pos;
-                       $serverCount = count( $this->mServers );
+                       $this->waitForPos = $pos;
+                       $serverCount = count( $this->servers );
 
                        $ok = true;
                        for ( $i = 1; $i < $serverCount; $i++ ) {
-                               if ( $this->mLoads[$i] > 0 ) {
+                               if ( $this->loads[$i] > 0 ) {
                                        $start = microtime( true );
                                        $ok = $this->doWait( $i, true, $timeout ) && $ok;
-                                       $timeout -= ( microtime( true ) - $start );
+                                       $timeout -= intval( microtime( true ) - $start );
                                        if ( $timeout <= 0 ) {
                                                break; // timeout reached
                                        }
@@ -535,7 +547,7 @@ class LoadBalancer implements ILoadBalancer {
                        }
                } finally {
                        # Restore the old position, as this is not used for lag-protection but for throttling
-                       $this->mWaitForPos = $oldPos;
+                       $this->waitForPos = $oldPos;
                }
 
                return $ok;
@@ -549,22 +561,22 @@ class LoadBalancer implements ILoadBalancer {
                        return;
                }
 
-               if ( !$this->mWaitForPos || $pos->hasReached( $this->mWaitForPos ) ) {
-                       $this->mWaitForPos = $pos;
+               if ( !$this->waitForPos || $pos->hasReached( $this->waitForPos ) ) {
+                       $this->waitForPos = $pos;
                }
        }
 
-       /**
-        * @param int $i
-        * @return IDatabase|bool
-        */
-       public function getAnyOpenConnection( $i ) {
-               foreach ( $this->mConns as $connsByServer ) {
-                       if ( !empty( $connsByServer[$i] ) ) {
-                               /** @var IDatabase[] $serverConns */
-                               $serverConns = $connsByServer[$i];
+       public function getAnyOpenConnection( $i, $flags = 0 ) {
+               $autocommit = ( ( $flags & self::CONN_TRX_AUTOCOMMIT ) == self::CONN_TRX_AUTOCOMMIT );
+               foreach ( $this->conns as $connsByServer ) {
+                       if ( !isset( $connsByServer[$i] ) ) {
+                               continue;
+                       }
 
-                               return reset( $serverConns );
+                       foreach ( $connsByServer[$i] as $conn ) {
+                               if ( !$autocommit || $conn->getLBInfo( 'autoCommitOnly' ) ) {
+                                       return $conn;
+                               }
                        }
                }
 
@@ -575,11 +587,11 @@ class LoadBalancer implements ILoadBalancer {
         * Wait for a given replica DB to catch up to the master pos stored in $this
         * @param int $index Server index
         * @param bool $open Check the server even if a new connection has to be made
-        * @param int $timeout Max seconds to wait; default is mWaitTimeout
+        * @param int $timeout Max seconds to wait; default is "waitTimeout" given to __construct()
         * @return bool
         */
        protected function doWait( $index, $open = false, $timeout = null ) {
-               $timeout = max( 1, $timeout ?: $this->mWaitTimeout );
+               $timeout = max( 1, intval( $timeout ?: $this->waitTimeout ) );
 
                // Check if we already know that the DB has reached this point
                $server = $this->getServerName( $index );
@@ -588,7 +600,7 @@ class LoadBalancer implements ILoadBalancer {
                $knownReachedPos = $this->srvCache->get( $key );
                if (
                        $knownReachedPos instanceof DBMasterPos &&
-                       $knownReachedPos->hasReached( $this->mWaitForPos )
+                       $knownReachedPos->hasReached( $this->waitForPos )
                ) {
                        $this->replLogger->debug(
                                __METHOD__ .
@@ -627,18 +639,18 @@ class LoadBalancer implements ILoadBalancer {
 
                $this->replLogger->info(
                        __METHOD__ .
-                       ': Waiting for replica DB {dbserver} to catch up...',
+                       ': waiting for replica DB {dbserver} to catch up...',
                        [ 'dbserver' => $server ]
                );
 
-               $result = $conn->masterPosWait( $this->mWaitForPos, $timeout );
+               $result = $conn->masterPosWait( $this->waitForPos, $timeout );
 
                if ( $result === null ) {
                        $this->replLogger->warning(
                                __METHOD__ . ': Errored out waiting on {host} pos {pos}',
                                [
                                        'host' => $server,
-                                       'pos' => $this->mWaitForPos,
+                                       'pos' => $this->waitForPos,
                                        'trace' => ( new RuntimeException() )->getTraceAsString()
                                ]
                        );
@@ -648,16 +660,16 @@ class LoadBalancer implements ILoadBalancer {
                                __METHOD__ . ': Timed out waiting on {host} pos {pos}',
                                [
                                        'host' => $server,
-                                       'pos' => $this->mWaitForPos,
+                                       'pos' => $this->waitForPos,
                                        'trace' => ( new RuntimeException() )->getTraceAsString()
                                ]
                        );
                        $ok = false;
                } else {
-                       $this->replLogger->info( __METHOD__ . ": Done" );
+                       $this->replLogger->debug( __METHOD__ . ": done waiting" );
                        $ok = true;
                        // Remember that the DB reached this point
-                       $this->srvCache->set( $key, $this->mWaitForPos, BagOStuff::TTL_DAY );
+                       $this->srvCache->set( $key, $this->waitForPos, BagOStuff::TTL_DAY );
                }
 
                if ( $close ) {
@@ -677,6 +689,23 @@ class LoadBalancer implements ILoadBalancer {
                        $domain = false; // local connection requested
                }
 
+               if ( ( $flags & self::CONN_TRX_AUTOCOMMIT ) === self::CONN_TRX_AUTOCOMMIT ) {
+                       // Assuming all servers are of the same type (or similar), which is overwhelmingly
+                       // the case, use the master server information to get the attributes. The information
+                       // for $i cannot be used since it might be DB_REPLICA, which might require connection
+                       // attempts in order to be resolved into a real server index.
+                       $attributes = $this->getServerAttributes( $this->getWriterIndex() );
+                       if ( $attributes[Database::ATTR_DB_LEVEL_LOCKING] ) {
+                               // Callers sometimes want to (a) escape REPEATABLE-READ stateness without locking
+                               // rows (e.g. FOR UPDATE) or (b) make small commits during a larger transactions
+                               // to reduce lock contention. None of these apply for sqlite and using separate
+                               // connections just causes self-deadlocks.
+                               $flags &= ~self::CONN_TRX_AUTOCOMMIT;
+                               $this->connLogger->info( __METHOD__ .
+                                       ': ignoring CONN_TRX_AUTOCOMMIT to avoid deadlocks.' );
+                       }
+               }
+
                $groups = ( $groups === false || $groups === [] )
                        ? [ false ] // check one "group": the generic pool
                        : (array)$groups;
@@ -686,7 +715,7 @@ class LoadBalancer implements ILoadBalancer {
 
                if ( $i == self::DB_MASTER ) {
                        $i = $this->getWriterIndex();
-               } else {
+               } elseif ( $i == self::DB_REPLICA ) {
                        # Try to find an available server in any the query groups (in order)
                        foreach ( $groups as $group ) {
                                $groupIndex = $this->getReaderIndex( $group, $domain );
@@ -699,14 +728,14 @@ class LoadBalancer implements ILoadBalancer {
 
                # Operation-based index
                if ( $i == self::DB_REPLICA ) {
-                       $this->mLastError = 'Unknown error'; // reset error string
+                       $this->lastError = 'Unknown error'; // reset error string
                        # Try the general server pool if $groups are unavailable.
                        $i = ( $groups === [ false ] )
                                ? false // don't bother with this if that is what was tried above
                                : $this->getReaderIndex( false, $domain );
                        # Couldn't find a working server in getReaderIndex()?
                        if ( $i === false ) {
-                               $this->mLastError = 'No working replica DB server: ' . $this->mLastError;
+                               $this->lastError = 'No working replica DB server: ' . $this->lastError;
                                // Throw an exception
                                $this->reportConnectionError();
                                return null; // not reached
@@ -736,7 +765,7 @@ class LoadBalancer implements ILoadBalancer {
                return $conn;
        }
 
-       public function reuseConnection( $conn ) {
+       public function reuseConnection( IDatabase $conn ) {
                $serverIndex = $conn->getLBInfo( 'serverIndex' );
                $refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
                if ( $serverIndex === null || $refCount === null ) {
@@ -754,7 +783,8 @@ class LoadBalancer implements ILoadBalancer {
                } elseif ( $conn instanceof DBConnRef ) {
                        // DBConnRef already handles calling reuseConnection() and only passes the live
                        // Database instance to this method. Any caller passing in a DBConnRef is broken.
-                       $this->connLogger->error( __METHOD__ . ": got DBConnRef instance.\n" .
+                       $this->connLogger->error(
+                               __METHOD__ . ": got DBConnRef instance.\n" .
                                ( new RuntimeException() )->getTraceAsString() );
 
                        return;
@@ -773,20 +803,20 @@ class LoadBalancer implements ILoadBalancer {
                }
 
                $domain = $conn->getDomainID();
-               if ( !isset( $this->mConns[$connInUseKey][$serverIndex][$domain] ) ) {
+               if ( !isset( $this->conns[$connInUseKey][$serverIndex][$domain] ) ) {
                        throw new InvalidArgumentException( __METHOD__ .
                                ": connection $serverIndex/$domain not found; it may have already been freed." );
-               } elseif ( $this->mConns[$connInUseKey][$serverIndex][$domain] !== $conn ) {
+               } elseif ( $this->conns[$connInUseKey][$serverIndex][$domain] !== $conn ) {
                        throw new InvalidArgumentException( __METHOD__ .
                                ": connection $serverIndex/$domain mismatched; it may have already been freed." );
                }
 
                $conn->setLBInfo( 'foreignPoolRefCount', --$refCount );
                if ( $refCount <= 0 ) {
-                       $this->mConns[$connFreeKey][$serverIndex][$domain] = $conn;
-                       unset( $this->mConns[$connInUseKey][$serverIndex][$domain] );
-                       if ( !$this->mConns[$connInUseKey][$serverIndex] ) {
-                               unset( $this->mConns[$connInUseKey][$serverIndex] ); // clean up
+                       $this->conns[$connFreeKey][$serverIndex][$domain] = $conn;
+                       unset( $this->conns[$connInUseKey][$serverIndex][$domain] );
+                       if ( !$this->conns[$connInUseKey][$serverIndex] ) {
+                               unset( $this->conns[$connInUseKey][$serverIndex] ); // clean up
                        }
                        $this->connLogger->debug( __METHOD__ . ": freed connection $serverIndex/$domain" );
                } else {
@@ -819,18 +849,18 @@ class LoadBalancer implements ILoadBalancer {
                        $domain = false; // local connection requested
                }
 
-               if ( !$this->chronProtInitialized && $this->chronProt ) {
+               if ( !$this->connectionAttempted && $this->chronologyCallback ) {
                        $this->connLogger->debug( __METHOD__ . ': calling initLB() before first connection.' );
-                       // Load CP positions before connecting so that doWait() triggers later if needed
-                       $this->chronProtInitialized = true;
-                       $this->chronProt->initLB( $this );
+                       // Load any "waitFor" positions before connecting so that doWait() is triggered
+                       $this->connectionAttempted = true;
+                       call_user_func( $this->chronologyCallback, $this );
                }
 
                // Check if an auto-commit connection is being requested. If so, it will not reuse the
                // main set of DB connections but rather its own pool since:
                // a) those are usually set to implicitly use transaction rounds via DBO_TRX
                // b) those must support the use of explicit transaction rounds via beginMasterChanges()
-               $autoCommit = ( ( $flags & self::CONN_TRX_AUTO ) == self::CONN_TRX_AUTO );
+               $autoCommit = ( ( $flags & self::CONN_TRX_AUTOCOMMIT ) == self::CONN_TRX_AUTOCOMMIT );
 
                if ( $domain !== false ) {
                        // Connection is to a foreign domain
@@ -838,14 +868,14 @@ class LoadBalancer implements ILoadBalancer {
                } else {
                        // Connection is to the local domain
                        $connKey = $autoCommit ? self::KEY_LOCAL_NOROUND : self::KEY_LOCAL;
-                       if ( isset( $this->mConns[$connKey][$i][0] ) ) {
-                               $conn = $this->mConns[$connKey][$i][0];
+                       if ( isset( $this->conns[$connKey][$i][0] ) ) {
+                               $conn = $this->conns[$connKey][$i][0];
                        } else {
-                               if ( !isset( $this->mServers[$i] ) || !is_array( $this->mServers[$i] ) ) {
+                               if ( !isset( $this->servers[$i] ) || !is_array( $this->servers[$i] ) ) {
                                        throw new InvalidArgumentException( "No server with index '$i'." );
                                }
                                // Open a new connection
-                               $server = $this->mServers[$i];
+                               $server = $this->servers[$i];
                                $server['serverIndex'] = $i;
                                $server['autoCommitOnly'] = $autoCommit;
                                if ( $this->localDomain->getDatabase() !== null ) {
@@ -855,10 +885,12 @@ class LoadBalancer implements ILoadBalancer {
                                $conn = $this->reallyOpenConnection( $server, $this->localDomain );
                                $host = $this->getServerName( $i );
                                if ( $conn->isOpen() ) {
-                                       $this->connLogger->debug( "Connected to database $i at '$host'." );
-                                       $this->mConns[$connKey][$i][0] = $conn;
+                                       $this->connLogger->debug(
+                                               __METHOD__ . ": connected to database $i at '$host'." );
+                                       $this->conns[$connKey][$i][0] = $conn;
                                } else {
-                                       $this->connLogger->warning( "Failed to connect to database $i at '$host'." );
+                                       $this->connLogger->warning(
+                                               __METHOD__ . ": failed to connect to database $i at '$host'." );
                                        $this->errorConnection = $conn;
                                        $conn = false;
                                }
@@ -906,7 +938,7 @@ class LoadBalancer implements ILoadBalancer {
                $domainInstance = DatabaseDomain::newFromId( $domain );
                $dbName = $domainInstance->getDatabase();
                $prefix = $domainInstance->getTablePrefix();
-               $autoCommit = ( ( $flags & self::CONN_TRX_AUTO ) == self::CONN_TRX_AUTO );
+               $autoCommit = ( ( $flags & self::CONN_TRX_AUTOCOMMIT ) == self::CONN_TRX_AUTOCOMMIT );
 
                if ( $autoCommit ) {
                        $connFreeKey = self::KEY_FOREIGN_FREE_NOROUND;
@@ -916,39 +948,39 @@ class LoadBalancer implements ILoadBalancer {
                        $connInUseKey = self::KEY_FOREIGN_INUSE;
                }
 
-               if ( isset( $this->mConns[$connInUseKey][$i][$domain] ) ) {
+               if ( isset( $this->conns[$connInUseKey][$i][$domain] ) ) {
                        // Reuse an in-use connection for the same domain
-                       $conn = $this->mConns[$connInUseKey][$i][$domain];
+                       $conn = $this->conns[$connInUseKey][$i][$domain];
                        $this->connLogger->debug( __METHOD__ . ": reusing connection $i/$domain" );
-               } elseif ( isset( $this->mConns[$connFreeKey][$i][$domain] ) ) {
+               } elseif ( isset( $this->conns[$connFreeKey][$i][$domain] ) ) {
                        // Reuse a free connection for the same domain
-                       $conn = $this->mConns[$connFreeKey][$i][$domain];
-                       unset( $this->mConns[$connFreeKey][$i][$domain] );
-                       $this->mConns[$connInUseKey][$i][$domain] = $conn;
+                       $conn = $this->conns[$connFreeKey][$i][$domain];
+                       unset( $this->conns[$connFreeKey][$i][$domain] );
+                       $this->conns[$connInUseKey][$i][$domain] = $conn;
                        $this->connLogger->debug( __METHOD__ . ": reusing free connection $i/$domain" );
-               } elseif ( !empty( $this->mConns[$connFreeKey][$i] ) ) {
+               } elseif ( !empty( $this->conns[$connFreeKey][$i] ) ) {
                        // Reuse a free connection from another domain
-                       $conn = reset( $this->mConns[$connFreeKey][$i] );
-                       $oldDomain = key( $this->mConns[$connFreeKey][$i] );
+                       $conn = reset( $this->conns[$connFreeKey][$i] );
+                       $oldDomain = key( $this->conns[$connFreeKey][$i] );
                        if ( strlen( $dbName ) && !$conn->selectDB( $dbName ) ) {
-                               $this->mLastError = "Error selecting database '$dbName' on server " .
+                               $this->lastError = "Error selecting database '$dbName' on server " .
                                        $conn->getServer() . " from client host {$this->host}";
                                $this->errorConnection = $conn;
                                $conn = false;
                        } else {
                                $conn->tablePrefix( $prefix );
-                               unset( $this->mConns[$connFreeKey][$i][$oldDomain] );
+                               unset( $this->conns[$connFreeKey][$i][$oldDomain] );
                                // Note that if $domain is an empty string, getDomainID() might not match it
-                               $this->mConns[$connInUseKey][$i][$conn->getDomainId()] = $conn;
+                               $this->conns[$connInUseKey][$i][$conn->getDomainId()] = $conn;
                                $this->connLogger->debug( __METHOD__ .
                                        ": reusing free connection from $oldDomain for $domain" );
                        }
                } else {
-                       if ( !isset( $this->mServers[$i] ) || !is_array( $this->mServers[$i] ) ) {
+                       if ( !isset( $this->servers[$i] ) || !is_array( $this->servers[$i] ) ) {
                                throw new InvalidArgumentException( "No server with index '$i'." );
                        }
                        // Open a new connection
-                       $server = $this->mServers[$i];
+                       $server = $this->servers[$i];
                        $server['serverIndex'] = $i;
                        $server['foreignPoolRefCount'] = 0;
                        $server['foreign'] = true;
@@ -961,7 +993,7 @@ class LoadBalancer implements ILoadBalancer {
                        } else {
                                $conn->tablePrefix( $prefix ); // as specified
                                // Note that if $domain is an empty string, getDomainID() might not match it
-                               $this->mConns[$connInUseKey][$i][$conn->getDomainID()] = $conn;
+                               $this->conns[$connInUseKey][$i][$conn->getDomainID()] = $conn;
                                $this->connLogger->debug( __METHOD__ . ": opened new connection for $i/$domain" );
                        }
                }
@@ -975,6 +1007,13 @@ class LoadBalancer implements ILoadBalancer {
                return $conn;
        }
 
+       public function getServerAttributes( $i ) {
+               return Database::attributesFromType(
+                       $this->getServerType( $i ),
+                       isset( $this->servers[$i]['driver'] ) ? $this->servers[$i]['driver'] : null
+               );
+       }
+
        /**
         * Test if the specified index represents an open connection
         *
@@ -1006,7 +1045,17 @@ class LoadBalancer implements ILoadBalancer {
                        throw new DBAccessError();
                }
 
-               if ( $domainOverride->getDatabase() !== null ) {
+               // Handle $domainOverride being a specified or an unspecified domain
+               if ( $domainOverride->getDatabase() === null ) {
+                       // Normally, an RDBMS requires a DB name specified on connection and the $server
+                       // configuration array is assumed to already specify an appropriate DB name.
+                       if ( $server['type'] === 'mysql' ) {
+                               // For MySQL, DATABASE and SCHEMA are synonyms, connections need not specify a DB,
+                               // and the DB name in $server might not exist due to legacy reasons (the default
+                               // domain used to ignore the local LB domain, even when mismatched).
+                               $server['dbname'] = null;
+                       }
+               } else {
                        $server['dbname'] = $domainOverride->getDatabase();
                        $server['schema'] = $domainOverride->getSchema();
                }
@@ -1026,6 +1075,7 @@ class LoadBalancer implements ILoadBalancer {
                $server['connLogger'] = $this->connLogger;
                $server['queryLogger'] = $this->queryLogger;
                $server['errorLogger'] = $this->errorLogger;
+               $server['deprecationLogger'] = $this->deprecationLogger;
                $server['profiler'] = $this->profiler;
                $server['trxProfiler'] = $this->trxProfiler;
                // Use the same agent and PHP mode for all DB handles
@@ -1049,6 +1099,7 @@ class LoadBalancer implements ILoadBalancer {
                        $this->getLazyConnectionRef( self::DB_MASTER, [], $db->getDomainID() )
                );
                $db->setTableAliases( $this->tableAliases );
+               $db->setIndexAliases( $this->indexAliases );
 
                if ( $server['serverIndex'] === $this->getWriterIndex() ) {
                        if ( $this->trxRoundId !== false ) {
@@ -1069,27 +1120,28 @@ class LoadBalancer implements ILoadBalancer {
                $conn = $this->errorConnection; // the connection which caused the error
                $context = [
                        'method' => __METHOD__,
-                       'last_error' => $this->mLastError,
+                       'last_error' => $this->lastError,
                ];
 
                if ( $conn instanceof IDatabase ) {
                        $context['db_server'] = $conn->getServer();
                        $this->connLogger->warning(
-                               "Connection error: {last_error} ({db_server})",
+                               __METHOD__ . ": connection error: {last_error} ({db_server})",
                                $context
                        );
 
                        // throws DBConnectionError
-                       $conn->reportConnectionError( "{$this->mLastError} ({$context['db_server']})" );
+                       $conn->reportConnectionError( "{$this->lastError} ({$context['db_server']})" );
                } else {
                        // No last connection, probably due to all servers being too busy
                        $this->connLogger->error(
-                               "LB failure with no last connection. Connection error: {last_error}",
+                               __METHOD__ .
+                               ": LB failure with no last connection. Connection error: {last_error}",
                                $context
                        );
 
-                       // If all servers were busy, mLastError will contain something sensible
-                       throw new DBConnectionError( null, $this->mLastError );
+                       // If all servers were busy, "lastError" will contain something sensible
+                       throw new DBConnectionError( null, $this->lastError );
                }
        }
 
@@ -1098,22 +1150,22 @@ class LoadBalancer implements ILoadBalancer {
        }
 
        public function haveIndex( $i ) {
-               return array_key_exists( $i, $this->mServers );
+               return array_key_exists( $i, $this->servers );
        }
 
        public function isNonZeroLoad( $i ) {
-               return array_key_exists( $i, $this->mServers ) && $this->mLoads[$i] != 0;
+               return array_key_exists( $i, $this->servers ) && $this->loads[$i] != 0;
        }
 
        public function getServerCount() {
-               return count( $this->mServers );
+               return count( $this->servers );
        }
 
        public function getServerName( $i ) {
-               if ( isset( $this->mServers[$i]['hostName'] ) ) {
-                       $name = $this->mServers[$i]['hostName'];
-               } elseif ( isset( $this->mServers[$i]['host'] ) ) {
-                       $name = $this->mServers[$i]['host'];
+               if ( isset( $this->servers[$i]['hostName'] ) ) {
+                       $name = $this->servers[$i]['hostName'];
+               } elseif ( isset( $this->servers[$i]['host'] ) ) {
+                       $name = $this->servers[$i]['host'];
                } else {
                        $name = '';
                }
@@ -1121,8 +1173,16 @@ class LoadBalancer implements ILoadBalancer {
                return ( $name != '' ) ? $name : 'localhost';
        }
 
+       public function getServerInfo( $i ) {
+               if ( isset( $this->servers[$i] ) ) {
+                       return $this->servers[$i];
+               } else {
+                       return false;
+               }
+       }
+
        public function getServerType( $i ) {
-               return isset( $this->mServers[$i]['type'] ) ? $this->mServers[$i]['type'] : 'unknown';
+               return isset( $this->servers[$i]['type'] ) ? $this->servers[$i]['type'] : 'unknown';
        }
 
        public function getMasterPos() {
@@ -1130,7 +1190,7 @@ class LoadBalancer implements ILoadBalancer {
                # master (however unlikely that may be), then we can fetch the position from the replica DB.
                $masterConn = $this->getAnyOpenConnection( $this->getWriterIndex() );
                if ( !$masterConn ) {
-                       $serverCount = count( $this->mServers );
+                       $serverCount = count( $this->servers );
                        for ( $i = 1; $i < $serverCount; $i++ ) {
                                $conn = $this->getAnyOpenConnection( $i );
                                if ( $conn ) {
@@ -1152,11 +1212,12 @@ class LoadBalancer implements ILoadBalancer {
        public function closeAll() {
                $this->forEachOpenConnection( function ( IDatabase $conn ) {
                        $host = $conn->getServer();
-                       $this->connLogger->debug( "Closing connection to database '$host'." );
+                       $this->connLogger->debug(
+                               __METHOD__ . ": closing connection to database '$host'." );
                        $conn->close();
                } );
 
-               $this->mConns = [
+               $this->conns = [
                        self::KEY_LOCAL => [],
                        self::KEY_FOREIGN_INUSE => [],
                        self::KEY_FOREIGN_FREE => [],
@@ -1168,8 +1229,8 @@ class LoadBalancer implements ILoadBalancer {
        }
 
        public function closeConnection( IDatabase $conn ) {
-               $serverIndex = $conn->getLBInfo( 'serverIndex' ); // second index level of mConns
-               foreach ( $this->mConns as $type => $connsByServer ) {
+               $serverIndex = $conn->getLBInfo( 'serverIndex' );
+               foreach ( $this->conns as $type => $connsByServer ) {
                        if ( !isset( $connsByServer[$serverIndex] ) ) {
                                continue;
                        }
@@ -1177,8 +1238,9 @@ class LoadBalancer implements ILoadBalancer {
                        foreach ( $connsByServer[$serverIndex] as $i => $trackedConn ) {
                                if ( $conn === $trackedConn ) {
                                        $host = $this->getServerName( $i );
-                                       $this->connLogger->debug( "Closing connection to database $i at '$host'." );
-                                       unset( $this->mConns[$type][$serverIndex][$i] );
+                                       $this->connLogger->debug(
+                                               __METHOD__ . ": closing connection to database $i at '$host'." );
+                                       unset( $this->conns[$type][$serverIndex][$i] );
                                        --$this->connsOpened;
                                        break 2;
                                }
@@ -1328,11 +1390,12 @@ class LoadBalancer implements ILoadBalancer {
                $e = null; // first exception
                $this->forEachOpenMasterConnection( function ( Database $conn ) use ( $type, &$e ) {
                        $conn->setTrxEndCallbackSuppression( false );
-                       if ( $conn->writesOrCallbacksPending() ) {
-                               // This happens if onTransactionIdle() callbacks leave callbacks on *another* DB
-                               // (which finished its callbacks already). Warn and recover in this case. Let the
-                               // callbacks run in the final commitMasterChanges() in LBFactory::shutdown().
-                               $this->queryLogger->info( __METHOD__ . ": found writes/callbacks pending." );
+                       // Callbacks run in AUTO-COMMIT mode, so make sure no transactions are pending...
+                       if ( $conn->writesPending() ) {
+                               // This happens if onTransactionIdle() callbacks write to *other* handles
+                               // (which already finished their callbacks). Let any callbacks run in the final
+                               // commitMasterChanges() in LBFactory::shutdown(), when the transaction is gone.
+                               $this->queryLogger->warning( __METHOD__ . ": found writes pending." );
                                return;
                        } elseif ( $conn->trxLevel() ) {
                                // This happens for single-DB setups where DB_REPLICA uses the master DB,
@@ -1362,9 +1425,7 @@ class LoadBalancer implements ILoadBalancer {
                $this->trxRoundId = false;
                $this->forEachOpenMasterConnection(
                        function ( IDatabase $conn ) use ( $fname, $restore ) {
-                               if ( $conn->writesOrCallbacksPending() ) {
-                                       $conn->rollback( $fname, $conn::FLUSHING_ALL_PEERS );
-                               }
+                               $conn->rollback( $fname, $conn::FLUSHING_ALL_PEERS );
                                if ( $restore ) {
                                        $this->undoTransactionRoundFlags( $conn );
                                }
@@ -1379,6 +1440,12 @@ class LoadBalancer implements ILoadBalancer {
        }
 
        /**
+        * Make all DB servers with DBO_DEFAULT/DBO_TRX set join the transaction round
+        *
+        * Some servers may have neither flag enabled, meaning that they opt out of such
+        * transaction rounds and remain in auto-commit mode. Such behavior might be desired
+        * when a DB server is used for something like simple key/value storage.
+        *
         * @param IDatabase $conn
         */
        private function applyTransactionRoundFlags( IDatabase $conn ) {
@@ -1390,9 +1457,10 @@ class LoadBalancer implements ILoadBalancer {
                        // DBO_TRX is controlled entirely by CLI mode presence with DBO_DEFAULT.
                        // Force DBO_TRX even in CLI mode since a commit round is expected soon.
                        $conn->setFlag( $conn::DBO_TRX, $conn::REMEMBER_PRIOR );
-                       // If config has explicitly requested DBO_TRX be either on or off by not
-                       // setting DBO_DEFAULT, then respect that. Forcing no transactions is useful
-                       // for things like blob stores (ExternalStore) which want auto-commit mode.
+               }
+
+               if ( $conn->getFlag( $conn::DBO_TRX ) ) {
+                       $conn->setLBInfo( 'trxRoundId', $this->trxRoundId );
                }
        }
 
@@ -1404,6 +1472,10 @@ class LoadBalancer implements ILoadBalancer {
                        return; // transaction rounds do not apply to these connections
                }
 
+               if ( $conn->getFlag( $conn::DBO_TRX ) ) {
+                       $conn->setLBInfo( 'trxRoundId', false );
+               }
+
                if ( $conn->getFlag( $conn::DBO_DEFAULT ) ) {
                        $conn->restoreFlags( $conn::RESTORE_PRIOR );
                }
@@ -1438,7 +1510,7 @@ class LoadBalancer implements ILoadBalancer {
        }
 
        public function hasOrMadeRecentMasterChanges( $age = null ) {
-               $age = ( $age === null ) ? $this->mWaitTimeout : $age;
+               $age = ( $age === null ) ? $this->waitTimeout : $age;
 
                return ( $this->hasMasterChanges()
                        || $this->lastMasterChangeTimestamp() > microtime( true ) - $age );
@@ -1542,11 +1614,11 @@ class LoadBalancer implements ILoadBalancer {
 
        public function allowLagged( $mode = null ) {
                if ( $mode === null ) {
-                       return $this->mAllowLagged;
+                       return $this->allowLagged;
                }
-               $this->mAllowLagged = $mode;
+               $this->allowLagged = $mode;
 
-               return $this->mAllowLagged;
+               return $this->allowLagged;
        }
 
        public function pingAll() {
@@ -1561,7 +1633,7 @@ class LoadBalancer implements ILoadBalancer {
        }
 
        public function forEachOpenConnection( $callback, array $params = [] ) {
-               foreach ( $this->mConns as $connsByServer ) {
+               foreach ( $this->conns as $connsByServer ) {
                        foreach ( $connsByServer as $serverConns ) {
                                foreach ( $serverConns as $conn ) {
                                        $mergedParams = array_merge( [ $conn ], $params );
@@ -1573,7 +1645,7 @@ class LoadBalancer implements ILoadBalancer {
 
        public function forEachOpenMasterConnection( $callback, array $params = [] ) {
                $masterIndex = $this->getWriterIndex();
-               foreach ( $this->mConns as $connsByServer ) {
+               foreach ( $this->conns as $connsByServer ) {
                        if ( isset( $connsByServer[$masterIndex] ) ) {
                                /** @var IDatabase $conn */
                                foreach ( $connsByServer[$masterIndex] as $conn ) {
@@ -1585,7 +1657,7 @@ class LoadBalancer implements ILoadBalancer {
        }
 
        public function forEachOpenReplicaConnection( $callback, array $params = [] ) {
-               foreach ( $this->mConns as $connsByServer ) {
+               foreach ( $this->conns as $connsByServer ) {
                        foreach ( $connsByServer as $i => $serverConns ) {
                                if ( $i === $this->getWriterIndex() ) {
                                        continue; // skip master
@@ -1609,9 +1681,9 @@ class LoadBalancer implements ILoadBalancer {
 
                $lagTimes = $this->getLagTimes( $domain );
                foreach ( $lagTimes as $i => $lag ) {
-                       if ( $this->mLoads[$i] > 0 && $lag > $maxLag ) {
+                       if ( $this->loads[$i] > 0 && $lag > $maxLag ) {
                                $maxLag = $lag;
-                               $host = $this->mServers[$i]['host'];
+                               $host = $this->servers[$i]['host'];
                                $maxIndex = $i;
                        }
                }
@@ -1626,7 +1698,7 @@ class LoadBalancer implements ILoadBalancer {
 
                $knownLagTimes = []; // map of (server index => 0 seconds)
                $indexesWithLag = [];
-               foreach ( $this->mServers as $i => $server ) {
+               foreach ( $this->servers as $i => $server ) {
                        if ( empty( $server['is static'] ) ) {
                                $indexesWithLag[] = $i; // DB server might have replication lag
                        } else {
@@ -1648,10 +1720,12 @@ class LoadBalancer implements ILoadBalancer {
        /**
         * @param IDatabase $conn
         * @param DBMasterPos|bool $pos
-        * @param int $timeout
+        * @param int|null $timeout
         * @return bool
         */
-       public function safeWaitForMasterPos( IDatabase $conn, $pos = false, $timeout = 10 ) {
+       public function safeWaitForMasterPos( IDatabase $conn, $pos = false, $timeout = null ) {
+               $timeout = max( 1, $timeout ?: $this->waitTimeout );
+
                if ( $this->getServerCount() <= 1 || !$conn->getLBInfo( 'replica' ) ) {
                        return true; // server is not a replica DB
                }
@@ -1671,7 +1745,7 @@ class LoadBalancer implements ILoadBalancer {
                if ( $pos instanceof DBMasterPos ) {
                        $result = $conn->masterPosWait( $pos, $timeout );
                        if ( $result == -1 || is_null( $result ) ) {
-                               $msg = __METHOD__ . ': Timed out waiting on {host} pos {pos}';
+                               $msg = __METHOD__ . ': timed out waiting on {host} pos {pos}';
                                $this->replLogger->warning( $msg, [
                                        'host' => $conn->getServer(),
                                        'pos' => $pos,
@@ -1679,7 +1753,7 @@ class LoadBalancer implements ILoadBalancer {
                                ] );
                                $ok = false;
                        } else {
-                               $this->replLogger->info( __METHOD__ . ': Done' );
+                               $this->replLogger->debug( __METHOD__ . ': done waiting' );
                                $ok = true;
                        }
                } else {
@@ -1713,6 +1787,10 @@ class LoadBalancer implements ILoadBalancer {
                $this->tableAliases = $aliases;
        }
 
+       public function setIndexAliases( array $aliases ) {
+               $this->indexAliases = $aliases;
+       }
+
        public function setDomainPrefix( $prefix ) {
                // Find connections to explicit foreign domains still marked as in-use...
                $domainsInUse = [];