private $mServers;
/** @var array[] Map of (local/foreignUsed/foreignFree => server index => IDatabase array) */
private $mConns;
- /** @var array Map of (server index => weight) */
+ /** @var float[] Map of (server index => weight) */
private $mLoads;
/** @var array[] Map of (group => server index => weight) */
private $mGroupLoads;
private $mAllowLagged;
/** @var integer Seconds to spend waiting on replica DB lag to resolve */
private $mWaitTimeout;
- /** @var string The LoadMonitor subclass name */
- private $mLoadMonitorClass;
+ /** @var array The LoadMonitor configuration */
+ private $loadMonitorConfig;
/** @var array[] $aliases Map of (table => (dbname, schema, prefix) map) */
private $tableAliases = [];
/** @var ILoadMonitor */
- private $mLoadMonitor;
+ private $loadMonitor;
/** @var BagOStuff */
private $srvCache;
/** @var BagOStuff */
/** @var integer Warn when this many connection are held */
const CONN_HELD_WARN_THRESHOLD = 10;
+
/** @var integer Default 'max lag' when unspecified */
const MAX_LAG_DEFAULT = 10;
- /** @var integer Max time to wait for a replica DB to catch up (e.g. ChronologyProtector) */
- const POS_WAIT_TIMEOUT = 10;
/** @var integer Seconds to cache master server read-only status */
const TTL_CACHE_READONLY = 5;
$this->localDomainIdAlias = $this->localDomain->getDatabase();
}
- $this->mWaitTimeout = isset( $params['waitTimeout'] )
- ? $params['waitTimeout']
- : self::POS_WAIT_TIMEOUT;
+ $this->mWaitTimeout = isset( $params['waitTimeout'] ) ? $params['waitTimeout'] : 10;
$this->mReadIndex = -1;
$this->mConns = [
}
if ( isset( $params['loadMonitor'] ) ) {
- $this->mLoadMonitorClass = $params['loadMonitor'];
+ $this->loadMonitorConfig = $params['loadMonitor'];
} else {
- $master = reset( $params['servers'] );
- if ( isset( $master['type'] ) && $master['type'] === 'mysql' ) {
- $this->mLoadMonitorClass = 'LoadMonitorMySQL';
- } else {
- $this->mLoadMonitorClass = 'LoadMonitorNull';
- }
+ $this->loadMonitorConfig = [ 'class' => 'LoadMonitorNull' ];
}
foreach ( $params['servers'] as $i => $server ) {
* @return ILoadMonitor
*/
private function getLoadMonitor() {
- if ( !isset( $this->mLoadMonitor ) ) {
- $class = $this->mLoadMonitorClass;
- $this->mLoadMonitor = new $class( $this, $this->srvCache, $this->memCache );
- $this->mLoadMonitor->setLogger( $this->replLogger );
+ if ( !isset( $this->loadMonitor ) ) {
+ $class = $this->loadMonitorConfig['class'];
+ $this->loadMonitor = new $class(
+ $this, $this->srvCache, $this->memCache, $this->loadMonitorConfig );
+ $this->loadMonitor->setLogger( $this->replLogger );
}
- return $this->mLoadMonitor;
+ return $this->loadMonitor;
}
/**
}
# Scale the configured load ratios according to the dynamic load if supported
- $this->getLoadMonitor()->scaleLoads( $nonErrorLoads, $group, $domain );
+ $this->getLoadMonitor()->scaleLoads( $nonErrorLoads, $domain );
$laggedReplicaMode = false;
return false;
} else {
- $conn = $this->openConnection( $index, '' );
+ $conn = $this->openConnection( $index, self::DOMAIN_ANY );
if ( !$conn ) {
$this->replLogger->warning( __METHOD__ . ": failed to connect to $server" );
return;
}
+ if ( $this->disabled ) {
+ return; // DBConnRef handle probably survived longer than the LoadBalancer
+ }
+
$domain = $conn->getDomainID();
- if ( $this->mConns['foreignUsed'][$serverIndex][$domain] !== $conn ) {
+ if ( !isset( $this->mConns['foreignUsed'][$serverIndex][$domain] ) ) {
+ throw new InvalidArgumentException( __METHOD__ .
+ ": connection $serverIndex/$domain not found; it may have already been freed." );
+ } elseif ( $this->mConns['foreignUsed'][$serverIndex][$domain] !== $conn ) {
throw new InvalidArgumentException( __METHOD__ .
- ": connection not found, has the connection been freed already?" );
+ ": connection $serverIndex/$domain mismatched; it may have already been freed." );
}
$conn->setLBInfo( 'foreignPoolRefCount', --$refCount );
if ( $refCount <= 0 ) {
} elseif ( isset( $this->mConns['local'][$i][0] ) ) {
$conn = $this->mConns['local'][$i][0];
} else {
+ if ( !isset( $this->mServers[$i] ) || !is_array( $this->mServers[$i] ) ) {
+ throw new InvalidArgumentException( "No server with index '$i'." );
+ }
+ // Open a new connection
$server = $this->mServers[$i];
$server['serverIndex'] = $i;
$conn = $this->reallyOpenConnection( $server, false );
": reusing free connection from $oldDomain for $domain" );
}
} else {
+ if ( !isset( $this->mServers[$i] ) || !is_array( $this->mServers[$i] ) ) {
+ throw new InvalidArgumentException( "No server with index '$i'." );
+ }
// Open a new connection
$server = $this->mServers[$i];
$server['serverIndex'] = $i;
* @throws DBAccessError
* @throws InvalidArgumentException
*/
- protected function reallyOpenConnection( $server, $dbNameOverride = false ) {
+ protected function reallyOpenConnection( array $server, $dbNameOverride = false ) {
if ( $this->disabled ) {
throw new DBAccessError();
}
- if ( !is_array( $server ) ) {
- throw new InvalidArgumentException(
- 'You must update your load-balancing configuration. ' .
- 'See DefaultSettings.php entry for $wgDBservers.' );
- }
-
if ( $dbNameOverride !== false ) {
$server['dbname'] = $dbNameOverride;
}
$cache->makeGlobalKey( __CLASS__, 'server-read-only', $masterServer ),
self::TTL_CACHE_READONLY,
function () use ( $domain, $conn ) {
- $this->trxProfiler->setSilenced( true );
+ $old = $this->trxProfiler->setSilenced( true );
try {
$dbw = $conn ?: $this->getConnection( self::DB_MASTER, [], $domain );
$readOnly = (int)$dbw->serverIsReadOnly();
} catch ( DBError $e ) {
$readOnly = 0;
}
- $this->trxProfiler->setSilenced( false );
+ $this->trxProfiler->setSilenced( $old );
return $readOnly;
},
[ 'pcTTL' => $cache::TTL_PROC_LONG, 'busyValue' => 0 ]
}
if ( !$pos ) {
- // Get the current master position
- $dbw = $this->getConnection( self::DB_MASTER );
- $pos = $dbw->getMasterPos();
- $this->reuseConnection( $dbw );
+ // Get the current master position, opening a connection if needed
+ $masterConn = $this->getAnyOpenConnection( $this->getWriterIndex() );
+ if ( $masterConn ) {
+ $pos = $masterConn->getMasterPos();
+ } else {
+ $masterConn = $this->openConnection( $this->getWriterIndex(), self::DOMAIN_ANY );
+ $pos = $masterConn->getMasterPos();
+ $this->closeConnection( $masterConn );
+ }
}
if ( $pos instanceof DBMasterPos ) {
return $ok;
}
- public function clearLagTimeCache() {
- $this->getLoadMonitor()->clearCaches();
- }
-
public function setTransactionListener( $name, callable $callback = null ) {
if ( $callback ) {
$this->trxRecurringCallbacks[$name] = $callback;
function __destruct() {
// Avoid connection leaks for sanity
- $this->closeAll();
+ $this->disable();
}
}