25 use Psr\Log\LoggerInterface;
26 use Psr\Log\NullLogger;
27 use Wikimedia\ScopedCallback;
32 use InvalidArgumentException;
120 const CONN_HELD_WARN_THRESHOLD = 10;
123 const MAX_LAG_DEFAULT = 10;
125 const TTL_CACHE_READONLY = 5;
136 if ( !isset(
$params[
'servers'] ) ) {
137 throw new InvalidArgumentException( __CLASS__ .
': missing servers parameter' );
139 $this->mServers =
$params[
'servers'];
140 foreach ( $this->mServers
as $i => $server ) {
142 $this->mServers[$i][
'master'] =
true;
144 $this->mServers[$i][
'replica'] =
true;
148 $this->localDomain = isset(
$params[
'localDomain'] )
153 if ( $this->localDomain->getTablePrefix() !=
'' ) {
154 $this->localDomainIdAlias =
155 $this->localDomain->getDatabase() .
'-' . $this->localDomain->getTablePrefix();
157 $this->localDomainIdAlias = $this->localDomain->getDatabase();
160 $this->mWaitTimeout = isset(
$params[
'waitTimeout'] ) ?
$params[
'waitTimeout'] : 10;
162 $this->mReadIndex = -1;
165 self::KEY_LOCAL => [],
166 self::KEY_FOREIGN_INUSE => [],
167 self::KEY_FOREIGN_FREE => [],
169 self::KEY_LOCAL_NOROUND => [],
170 self::KEY_FOREIGN_INUSE_NOROUND => [],
171 self::KEY_FOREIGN_FREE_NOROUND => []
174 $this->mWaitForPos =
false;
175 $this->mAllowLagged =
false;
177 if ( isset(
$params[
'readOnlyReason'] ) && is_string(
$params[
'readOnlyReason'] ) ) {
178 $this->readOnlyReason =
$params[
'readOnlyReason'];
181 if ( isset(
$params[
'loadMonitor'] ) ) {
182 $this->loadMonitorConfig =
$params[
'loadMonitor'];
184 $this->loadMonitorConfig = [
'class' =>
'LoadMonitorNull' ];
187 foreach (
$params[
'servers']
as $i => $server ) {
188 $this->mLoads[$i] = $server[
'load'];
189 if ( isset( $server[
'groupLoads'] ) ) {
190 foreach ( $server[
'groupLoads']
as $group => $ratio ) {
191 if ( !isset( $this->mGroupLoads[$group] ) ) {
192 $this->mGroupLoads[$group] = [];
194 $this->mGroupLoads[$group][$i] = $ratio;
199 if ( isset(
$params[
'srvCache'] ) ) {
200 $this->srvCache =
$params[
'srvCache'];
204 if ( isset(
$params[
'wanCache'] ) ) {
205 $this->wanCache =
$params[
'wanCache'];
209 $this->profiler = isset(
$params[
'profiler'] ) ?
$params[
'profiler'] :
null;
210 if ( isset(
$params[
'trxProfiler'] ) ) {
211 $this->trxProfiler =
$params[
'trxProfiler'];
216 $this->errorLogger = isset(
$params[
'errorLogger'] )
218 :
function ( Exception
$e ) {
219 trigger_error( get_class(
$e ) .
': ' .
$e->getMessage(), E_USER_WARNING );
222 foreach ( [
'replLogger',
'connLogger',
'queryLogger',
'perfLogger' ]
as $key ) {
223 $this->$key = isset(
$params[$key] ) ?
$params[$key] :
new NullLogger();
226 $this->host = isset(
$params[
'hostname'] )
228 : ( gethostname() ?:
'unknown' );
229 $this->cliMode = isset(
$params[
'cliMode'] ) ?
$params[
'cliMode'] : PHP_SAPI ===
'cli';
232 if ( isset(
$params[
'chronologyProtector'] ) ) {
233 $this->chronProt =
$params[
'chronologyProtector'];
243 if ( !isset( $this->loadMonitor ) ) {
250 $class = $this->loadMonitorConfig[
'class'];
251 if ( isset( $compat[$class] ) ) {
252 $class = $compat[$class];
255 $this->loadMonitor =
new $class(
256 $this, $this->srvCache, $this->wanCache, $this->loadMonitorConfig );
257 $this->loadMonitor->setLogger( $this->replLogger );
272 # Unset excessively lagged servers
273 foreach ( $lags
as $i => $lag ) {
275 # How much lag this server nominally is allowed to have
276 $maxServerLag = isset( $this->mServers[$i][
'max lag'] )
277 ? $this->mServers[$i][
'max lag']
278 : self::MAX_LAG_DEFAULT;
279 # Constrain that futher by $maxLag argument
280 $maxServerLag = min( $maxServerLag, $maxLag );
283 if ( $lag ===
false && !is_infinite( $maxServerLag ) ) {
284 $this->replLogger->error(
285 "Server {host} is not replicating?", [
'host' =>
$host ] );
287 } elseif ( $lag > $maxServerLag ) {
288 $this->replLogger->warning(
289 "Server {host} has {lag} seconds of lag (>= {maxlag})",
290 [
'host' =>
$host,
'lag' => $lag,
'maxlag' => $maxServerLag ]
297 # Find out if all the replica DBs with non-zero load are lagged
299 foreach ( $loads
as $load ) {
303 # No appropriate DB servers except maybe the master and some replica DBs with zero load
304 # Do NOT use the master
305 # Instead, this function will return false, triggering read-only mode,
306 # and a lagged replica DB will be used instead.
310 if (
count( $loads ) == 0 ) {
314 # Return a random representative of the remainder
319 if (
count( $this->mServers ) == 1 ) {
322 } elseif ( $group ===
false && $this->mReadIndex >= 0 ) {
327 if ( $group !==
false ) {
329 if ( isset( $this->mGroupLoads[$group] ) ) {
330 $loads = $this->mGroupLoads[$group];
333 $this->connLogger->info( __METHOD__ .
": no loads for group $group" );
347 if ( $i ===
false ) {
357 if ( !$this->
doWait( $i ) ) {
362 if ( $this->mReadIndex <= 0 && $this->mLoads[$i] > 0 && $group ===
false ) {
364 $this->mReadIndex = $i;
367 $this->laggedReplicaMode =
true;
372 $this->connLogger->debug( __METHOD__ .
": using server $serverName for group '$group'" );
383 if ( !
count( $loads ) ) {
384 throw new InvalidArgumentException(
"Empty server array given to LoadBalancer" );
393 $currentLoads = $loads;
394 while (
count( $currentLoads ) ) {
399 if ( $this->mWaitForPos && $this->mWaitForPos->asOfTime() ) {
403 $ago = microtime(
true ) - $this->mWaitForPos->asOfTime();
407 if ( $i ===
false ) {
411 if ( $i ===
false &&
count( $currentLoads ) != 0 ) {
413 $this->replLogger->error(
"All replica DBs lagged. Switch to read-only mode" );
419 if ( $i ===
false ) {
423 $this->connLogger->debug( __METHOD__ .
": pickRandom() returned false" );
425 return [
false,
false ];
429 $this->connLogger->debug( __METHOD__ .
": Using reader #$i: $serverName..." );
433 $this->connLogger->warning( __METHOD__ .
": Failed connecting to $i/$domain" );
434 unset( $currentLoads[$i] );
441 if ( $domain !==
false ) {
450 if ( !
count( $currentLoads ) ) {
451 $this->connLogger->error(
"All servers down" );
460 $this->mWaitForPos = $pos;
464 if ( !$this->
doWait( $i ) ) {
465 $this->laggedReplicaMode =
true;
477 $this->mWaitForPos = $pos;
484 $readLoads = array_filter( $readLoads );
489 $ok = $this->
doWait( $i,
true, $timeout );
494 # Restore the old position, as this is not used for lag-protection but for throttling
495 $this->mWaitForPos = $oldPos;
504 $this->mWaitForPos = $pos;
505 $serverCount =
count( $this->mServers );
508 for ( $i = 1; $i < $serverCount; $i++ ) {
509 if ( $this->mLoads[$i] > 0 ) {
510 $ok = $this->
doWait( $i,
true, $timeout ) && $ok;
514 # Restore the old position, as this is not used for lag-protection but for throttling
515 $this->mWaitForPos = $oldPos;
529 if ( !$this->mWaitForPos || $pos->hasReached( $this->mWaitForPos ) ) {
530 $this->mWaitForPos = $pos;
539 foreach ( $this->mConns
as $connsByServer ) {
540 if ( !empty( $connsByServer[$i] ) ) {
542 $serverConns = $connsByServer[$i];
544 return reset( $serverConns );
558 protected function doWait( $index, $open =
false, $timeout =
null ) {
563 $key = $this->srvCache->makeGlobalKey( __CLASS__,
'last-known-pos', $server,
'v1' );
565 $knownReachedPos = $this->srvCache->get( $key );
568 $knownReachedPos->
hasReached( $this->mWaitForPos )
570 $this->replLogger->debug( __METHOD__ .
571 ": replica DB $server known to be caught up (pos >= $knownReachedPos)." );
579 $this->replLogger->debug( __METHOD__ .
": no connection open for $server" );
585 $this->replLogger->warning( __METHOD__ .
": failed to connect to $server" );
595 $this->replLogger->info( __METHOD__ .
": Waiting for replica DB $server to catch up..." );
597 $result = $conn->masterPosWait( $this->mWaitForPos, $timeout );
601 $this->replLogger->warning(
602 __METHOD__ .
": Timed out waiting on {host} pos {$this->mWaitForPos}",
603 [
'host' => $server ]
607 $this->replLogger->info( __METHOD__ .
": Done" );
621 if ( $i ===
null || $i ===
false ) {
622 throw new InvalidArgumentException(
'Attempt to call ' . __METHOD__ .
623 ' with invalid server index' );
630 $groups = ( $groups ===
false || $groups === [] )
640 # Try to find an available server in any the query groups (in order)
641 foreach ( $groups
as $group ) {
643 if ( $groupIndex !==
false ) {
650 # Operation-based index
652 $this->mLastError =
'Unknown error';
653 # Try the general server pool if $groups are unavailable.
654 $i = ( $groups === [
false ] )
657 # Couldn't find a working server in getReaderIndex()?
658 if ( $i ===
false ) {
666 # Now we have an explicit index into the servers array
674 # Profile any new connections that happen
675 if ( $this->connsOpened > $oldConnsOpened ) {
676 $host = $conn->getServer();
677 $dbname = $conn->getDBname();
678 $this->trxProfiler->recordConnection(
$host, $dbname, $masterOnly );
682 # Make master-requested DB handles inherit any read-only mode setting
683 $conn->setLBInfo(
'readOnlyReason', $this->
getReadOnlyReason( $domain, $conn ) );
690 $serverIndex = $conn->getLBInfo(
'serverIndex' );
691 $refCount = $conn->getLBInfo(
'foreignPoolRefCount' );
692 if ( $serverIndex ===
null || $refCount ===
null ) {
704 } elseif ( $conn instanceof
DBConnRef ) {
707 $this->connLogger->error( __METHOD__ .
": got DBConnRef instance.\n" .
708 (
new RuntimeException() )->getTraceAsString() );
713 if ( $this->disabled ) {
717 if ( $conn->getLBInfo(
'autoCommitOnly' ) ) {
725 $domain = $conn->getDomainID();
726 if ( !isset( $this->mConns[$connInUseKey][$serverIndex][$domain] ) ) {
727 throw new InvalidArgumentException( __METHOD__ .
728 ": connection $serverIndex/$domain not found; it may have already been freed." );
729 } elseif ( $this->mConns[$connInUseKey][$serverIndex][$domain] !== $conn ) {
730 throw new InvalidArgumentException( __METHOD__ .
731 ": connection $serverIndex/$domain mismatched; it may have already been freed." );
734 $conn->setLBInfo(
'foreignPoolRefCount', --$refCount );
735 if ( $refCount <= 0 ) {
736 $this->mConns[$connFreeKey][$serverIndex][$domain] = $conn;
737 unset( $this->mConns[$connInUseKey][$serverIndex][$domain] );
738 if ( !$this->mConns[$connInUseKey][$serverIndex] ) {
739 unset( $this->mConns[$connInUseKey][$serverIndex] );
741 $this->connLogger->debug( __METHOD__ .
": freed connection $serverIndex/$domain" );
743 $this->connLogger->debug( __METHOD__ .
744 ": reference count for $serverIndex/$domain reduced to $refCount" );
749 $domain = ( $domain !==
false ) ? $domain : $this->localDomain;
755 $domain = ( $domain !==
false ) ? $domain : $this->localDomain;
761 $domain = ( $domain !==
false ) ? $domain : $this->localDomain;
772 if ( !$this->chronProtInitialized && $this->chronProt ) {
773 $this->connLogger->debug( __METHOD__ .
': calling initLB() before first connection.' );
775 $this->chronProtInitialized =
true;
776 $this->chronProt->initLB( $this );
783 $autoCommit = ( (
$flags & self::CONN_TRX_AUTO ) == self::CONN_TRX_AUTO );
785 if ( $domain !==
false ) {
791 if ( isset( $this->mConns[$connKey][$i][0] ) ) {
792 $conn = $this->mConns[$connKey][$i][0];
794 if ( !isset( $this->mServers[$i] ) || !is_array( $this->mServers[$i] ) ) {
795 throw new InvalidArgumentException(
"No server with index '$i'." );
798 $server = $this->mServers[$i];
799 $server[
'serverIndex'] = $i;
800 $server[
'autoCommitOnly'] = $autoCommit;
803 if ( $conn->isOpen() ) {
804 $this->connLogger->debug(
"Connected to database $i at '$host'." );
805 $this->mConns[$connKey][$i][0] = $conn;
807 $this->connLogger->warning(
"Failed to connect to database $i at '$host'." );
808 $this->errorConnection = $conn;
819 $this->errorConnection = $conn;
823 if ( $autoCommit && $conn instanceof
IDatabase ) {
853 $dbName = $domainInstance->getDatabase();
854 $prefix = $domainInstance->getTablePrefix();
855 $autoCommit = ( (
$flags & self::CONN_TRX_AUTO ) == self::CONN_TRX_AUTO );
865 if ( isset( $this->mConns[$connInUseKey][$i][$domain] ) ) {
867 $conn = $this->mConns[$connInUseKey][$i][$domain];
868 $this->connLogger->debug( __METHOD__ .
": reusing connection $i/$domain" );
869 } elseif ( isset( $this->mConns[$connFreeKey][$i][$domain] ) ) {
871 $conn = $this->mConns[$connFreeKey][$i][$domain];
872 unset( $this->mConns[$connFreeKey][$i][$domain] );
873 $this->mConns[$connInUseKey][$i][$domain] = $conn;
874 $this->connLogger->debug( __METHOD__ .
": reusing free connection $i/$domain" );
875 } elseif ( !empty( $this->mConns[$connFreeKey][$i] ) ) {
877 $conn = reset( $this->mConns[$connFreeKey][$i] );
878 $oldDomain =
key( $this->mConns[$connFreeKey][$i] );
881 if ( strlen( $dbName ) && !$conn->selectDB( $dbName ) ) {
882 $this->mLastError =
"Error selecting database '$dbName' on server " .
883 $conn->getServer() .
" from client host {$this->host}";
884 $this->errorConnection = $conn;
887 $conn->tablePrefix( $prefix );
888 unset( $this->mConns[$connFreeKey][$i][$oldDomain] );
889 $this->mConns[$connInUseKey][$i][$domain] = $conn;
890 $this->connLogger->debug( __METHOD__ .
891 ": reusing free connection from $oldDomain for $domain" );
894 if ( !isset( $this->mServers[$i] ) || !is_array( $this->mServers[$i] ) ) {
895 throw new InvalidArgumentException(
"No server with index '$i'." );
898 $server = $this->mServers[$i];
899 $server[
'serverIndex'] = $i;
900 $server[
'foreignPoolRefCount'] = 0;
901 $server[
'foreign'] =
true;
902 $server[
'autoCommitOnly'] = $autoCommit;
904 if ( !$conn->isOpen() ) {
905 $this->connLogger->warning( __METHOD__ .
": connection error for $i/$domain" );
906 $this->errorConnection = $conn;
909 $conn->tablePrefix( $prefix );
910 $this->mConns[$connInUseKey][$i][$domain] = $conn;
911 $this->connLogger->debug( __METHOD__ .
": opened new connection for $i/$domain" );
917 $refCount = $conn->getLBInfo(
'foreignPoolRefCount' );
918 $conn->setLBInfo(
'foreignPoolRefCount', $refCount + 1 );
932 if ( !is_integer( $index ) ) {
951 if ( $this->disabled ) {
955 if ( $dbNameOverride !==
false ) {
956 $server[
'dbname'] = $dbNameOverride;
961 $server[
'clusterMasterHost'] = $masterName;
964 if ( ++$this->connsOpened >= self::CONN_HELD_WARN_THRESHOLD ) {
965 $this->perfLogger->warning( __METHOD__ .
": " .
966 "{$this->connsOpened}+ connections made (master=$masterName)" );
992 $db->setLBInfo( $server );
993 $db->setLazyMasterHandle(
996 $db->setTableAliases( $this->tableAliases );
999 if ( $this->trxRoundId !==
false ) {
1002 foreach ( $this->trxRecurringCallbacks
as $name => $callback ) {
1003 $db->setTransactionListener(
$name, $callback );
1016 'method' => __METHOD__,
1021 $context[
'db_server'] = $conn->getServer();
1022 $this->connLogger->warning(
1023 "Connection error: {last_error} ({db_server})",
1028 $conn->reportConnectionError(
"{$this->mLastError} ({$context['db_server']})" );
1031 $this->connLogger->error(
1032 "LB failure with no last connection. Connection error: {last_error}",
1046 return array_key_exists( $i, $this->mServers );
1050 return array_key_exists( $i, $this->mServers ) && $this->mLoads[$i] != 0;
1054 return count( $this->mServers );
1058 if ( isset( $this->mServers[$i][
'hostName'] ) ) {
1059 $name = $this->mServers[$i][
'hostName'];
1060 } elseif ( isset( $this->mServers[$i][
'host'] ) ) {
1061 $name = $this->mServers[$i][
'host'];
1066 return (
$name !=
'' ) ?
$name :
'localhost';
1070 return isset( $this->mServers[$i][
'type'] ) ? $this->mServers[$i][
'type'] :
'unknown';
1078 if ( isset( $this->mServers[$i] ) ) {
1079 return $this->mServers[$i];
1090 $this->mServers[$i] = $serverInfo;
1094 # If this entire request was served from a replica DB without opening a connection to the
1095 # master (however unlikely that may be), then we can fetch the position from the replica DB.
1097 if ( !$masterConn ) {
1098 $serverCount =
count( $this->mServers );
1099 for ( $i = 1; $i < $serverCount; $i++ ) {
1102 return $conn->getReplicaPos();
1106 return $masterConn->getMasterPos();
1114 $this->disabled =
true;
1120 $this->connLogger->debug(
"Closing connection to database '$host'." );
1125 self::KEY_LOCAL => [],
1126 self::KEY_FOREIGN_INUSE => [],
1127 self::KEY_FOREIGN_FREE => [],
1128 self::KEY_LOCAL_NOROUND => [],
1129 self::KEY_FOREIGN_INUSE_NOROUND => [],
1130 self::KEY_FOREIGN_FREE_NOROUND => []
1132 $this->connsOpened = 0;
1136 $serverIndex = $conn->
getLBInfo(
'serverIndex' );
1137 foreach ( $this->mConns
as $type => $connsByServer ) {
1138 if ( !isset( $connsByServer[$serverIndex] ) ) {
1142 foreach ( $connsByServer[$serverIndex]
as $i => $trackedConn ) {
1143 if ( $conn === $trackedConn ) {
1145 $this->connLogger->debug(
"Closing connection to database $i at '$host'." );
1146 unset( $this->mConns[
$type][$serverIndex][$i] );
1159 $restore = ( $this->trxRoundId !==
false );
1160 $this->trxRoundId =
false;
1166 call_user_func( $this->errorLogger,
$e );
1167 $failures[] =
"{$conn->getServer()}: {$e->getMessage()}";
1169 if ( $restore && $conn->
getLBInfo(
'master' ) ) {
1178 "Commit failed on server(s) " . implode(
"\n", array_unique( $failures ) )
1194 $limit = isset(
$options[
'maxWriteDuration'] ) ?
$options[
'maxWriteDuration'] : 0;
1202 "Explicit transaction still active. A caller may have caught an error."
1208 if ( $limit > 0 &&
$time > $limit ) {
1211 "Transaction spent $time second(s) in writes, exceeding the limit of $limit.",
1220 "A connection to the {$conn->getDBname()} database was lost before commit."
1227 if ( $this->trxRoundId !==
false ) {
1230 "$fname: Transaction round '{$this->trxRoundId}' already started."
1233 $this->trxRoundId =
$fname;
1242 call_user_func( $this->errorLogger,
$e );
1243 $failures[] =
"{$conn->getServer()}: {$e->getMessage()}";
1253 "$fname: Flush failed on server(s) " . implode(
"\n", array_unique( $failures ) )
1264 $restore = ( $this->trxRoundId !==
false );
1265 $this->trxRoundId =
false;
1271 } elseif ( $restore ) {
1275 call_user_func( $this->errorLogger,
$e );
1276 $failures[] =
"{$conn->getServer()}: {$e->getMessage()}";
1287 "$fname: Commit failed on server(s) " . implode(
"\n", array_unique( $failures ) )
1300 $this->queryLogger->info( __METHOD__ .
": found writes/callbacks pending." );
1312 }
catch ( Exception $ex ) {
1317 }
catch ( Exception $ex ) {
1326 $restore = ( $this->trxRoundId !==
false );
1327 $this->trxRoundId =
false;
1350 if ( $conn->
getLBInfo(
'autoCommitOnly' ) ) {
1368 if ( $conn->
getLBInfo(
'autoCommitOnly' ) ) {
1393 return (
bool)$pending;
1406 $age = ( $age === null ) ? $this->mWaitTimeout : $age;
1423 if ( !$this->laggedReplicaMode && $this->
getServerCount() > 1 ) {
1430 $this->allReplicasDownMode =
true;
1431 $this->laggedReplicaMode =
true;
1461 if ( $this->readOnlyReason !==
false ) {
1464 if ( $this->allReplicasDownMode ) {
1465 return 'The database has been automatically locked ' .
1466 'until the replica database servers become available';
1468 return 'The database has been automatically locked ' .
1469 'while the replica database servers catch up to the master.';
1472 return 'The database master is running in read-only mode.';
1487 return (
bool)
$cache->getWithSetCallback(
1488 $cache->makeGlobalKey( __CLASS__,
'server-read-only', $masterServer ),
1489 self::TTL_CACHE_READONLY,
1490 function ()
use ( $domain, $conn ) {
1491 $old = $this->trxProfiler->setSilenced(
true );
1494 $readOnly = (int)$dbw->serverIsReadOnly();
1501 $this->trxProfiler->setSilenced( $old );
1504 [
'pcTTL' => $cache::TTL_PROC_LONG,
'busyValue' => 0 ]
1509 if ( $mode ===
null ) {
1512 $this->mAllowLagged = $mode;
1520 if ( !$conn->
ping() ) {
1529 foreach ( $this->mConns
as $connsByServer ) {
1530 foreach ( $connsByServer
as $serverConns ) {
1531 foreach ( $serverConns
as $conn ) {
1532 $mergedParams = array_merge( [ $conn ],
$params );
1533 call_user_func_array( $callback, $mergedParams );
1541 foreach ( $this->mConns
as $connsByServer ) {
1542 if ( isset( $connsByServer[$masterIndex] ) ) {
1544 foreach ( $connsByServer[$masterIndex]
as $conn ) {
1545 $mergedParams = array_merge( [ $conn ],
$params );
1546 call_user_func_array( $callback, $mergedParams );
1553 foreach ( $this->mConns
as $connsByServer ) {
1554 foreach ( $connsByServer
as $i => $serverConns ) {
1558 foreach ( $serverConns
as $conn ) {
1559 $mergedParams = array_merge( [ $conn ],
$params );
1560 call_user_func_array( $callback, $mergedParams );
1572 return [
$host, $maxLag, $maxIndex ];
1576 foreach ( $lagTimes
as $i => $lag ) {
1577 if ( $this->mLoads[$i] > 0 && $lag > $maxLag ) {
1579 $host = $this->mServers[$i][
'host'];
1584 return [
$host, $maxLag, $maxIndex ];
1592 $knownLagTimes = [];
1593 $indexesWithLag = [];
1594 foreach ( $this->mServers
as $i => $server ) {
1595 if ( empty( $server[
'is static'] ) ) {
1596 $indexesWithLag[] = $i;
1598 $knownLagTimes[$i] = 0;
1602 return $this->
getLoadMonitor()->getLagTimes( $indexesWithLag, $domain ) + $knownLagTimes;
1627 if ( $masterConn ) {
1628 $pos = $masterConn->getMasterPos();
1631 $pos = $masterConn->getMasterPos();
1639 $msg = __METHOD__ .
": Timed out waiting on {$conn->getServer()} pos {$pos}";
1640 $this->replLogger->warning(
"$msg" );
1643 $this->replLogger->info( __METHOD__ .
": Done" );
1648 $this->replLogger->error(
"Could not get master pos for {$conn->getServer()}." );
1656 $this->trxRecurringCallbacks[
$name] = $callback;
1658 unset( $this->trxRecurringCallbacks[
$name] );
1668 $this->tableAliases = $aliases;
1677 if ( $conn->
getLBInfo(
'foreignPoolRefCount' ) > 0 ) {
1683 if ( $domainsInUse ) {
1684 $domains = implode(
', ', $domainsInUse );
1686 "Foreign domain connections are still in use ($domains)." );
1690 $this->localDomain->getDatabase(),
1707 if ( PHP_SAPI !=
'cli' ) {
1708 $old = ignore_user_abort(
true );
1709 return new ScopedCallback(
function ()
use ( $old ) {
1710 ignore_user_abort( $old );