* Inject wfWikiID() and MWExceptionHandler into LoadBalancer.
* Factor out LBFactory duplication into baseLoadBalancerParams().
* Remove $wgDBtype hack. Presumably, sites with others DBs would
not have multiple servers configured if does not work anyway.
* Make use of injected TransactionProfiler rather than calling
Profiler::instance()->getTransactionProfiler().
* Avoid use of trivial wfSplitWikiID() function.
* Make DBConnRef enforce its arguments more strongly and
optimize getWiki() to avoid causing a connection attempt.
* Avoid deprecated method call in LBFactory::destroyInstance().
Change-Id: If134b62d4f48cd68cb48ccbe149e72f12aa26819
*/
public static function changePrefix( $prefix ) {
global $wgDBprefix;
*/
public static function changePrefix( $prefix ) {
global $wgDBprefix;
- wfGetLBFactory()->forEachLB( function( $lb ) use ( $prefix ) {
- $lb->forEachOpenConnection( function ( $db ) use ( $prefix ) {
+ wfGetLBFactory()->forEachLB( function( LoadBalancer $lb ) use ( $prefix ) {
+ $lb->setDomainPrefix( $prefix );
+ $lb->forEachOpenConnection( function ( DatabaseBase $db ) use ( $prefix ) {
$db->tablePrefix( $prefix );
} );
} );
$db->tablePrefix( $prefix );
} );
} );
/** @var array|null */
private $params;
/** @var array|null */
private $params;
+ const FLD_INDEX = 0;
+ const FLD_GROUP = 1;
+ const FLD_WIKI = 2;
+
/**
* @param LoadBalancer $lb
/**
* @param LoadBalancer $lb
- * @param DatabaseBase|array $conn Connection or (server index, group, wiki ID) array
+ * @param DatabaseBase|array $conn Connection or (server index, group, wiki ID)
*/
public function __construct( LoadBalancer $lb, $conn ) {
$this->lb = $lb;
if ( $conn instanceof DatabaseBase ) {
$this->conn = $conn;
*/
public function __construct( LoadBalancer $lb, $conn ) {
$this->lb = $lb;
if ( $conn instanceof DatabaseBase ) {
$this->conn = $conn;
+ } elseif ( count( $conn ) >= 3 && $conn[self::FLD_WIKI] !== false ) {
+ } else {
+ throw new InvalidArgumentException( "Missing lazy connection arguments." );
}
public function getWikiID() {
}
public function getWikiID() {
+ if ( $this->conn === null ) {
+ // Avoid triggering a connection
+ return $this->params[self::FLD_WIKI];
+ }
+
return $this->__call( __FUNCTION__, func_get_args() );
}
return $this->__call( __FUNCTION__, func_get_args() );
}
* @deprecated since 1.27, use LBFactory::destroy()
*/
public static function destroyInstance() {
* @deprecated since 1.27, use LBFactory::destroy()
*/
public static function destroyInstance() {
- self::singleton()->destroy();
+ MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->destroy();
+ /**
+ * Base parameters to LoadBalancer::__construct()
+ */
+ final protected function baseLoadBalancerParams() {
+ return [
+ 'readOnlyReason' => $this->readOnlyReason,
+ 'trxProfiler' => $this->trxProfiler,
+ 'srvCache' => $this->srvCache,
+ 'wanCache' => $this->wanCache,
+ 'localDomain' => wfWikiID(),
+ 'errorLogger' => [ MWExceptionHandler::class, 'logException' ]
+ ];
+ }
+
/**
* @param LoadBalancer $lb
*/
/**
* @param LoadBalancer $lb
*/
* @return LoadBalancer
*/
private function newLoadBalancer( $template, $loads, $groupLoads, $readOnlyReason ) {
* @return LoadBalancer
*/
private function newLoadBalancer( $template, $loads, $groupLoads, $readOnlyReason ) {
- $lb = new LoadBalancer( [
- 'servers' => $this->makeServerArray( $template, $loads, $groupLoads ),
- 'loadMonitor' => $this->loadMonitorClass,
- 'readOnlyReason' => $readOnlyReason,
- 'trxProfiler' => $this->trxProfiler,
- 'srvCache' => $this->srvCache,
- 'wanCache' => $this->wanCache
- ] );
-
+ $lb = new LoadBalancer( array_merge(
+ $this->baseLoadBalancerParams(),
+ [
+ 'servers' => $this->makeServerArray( $template, $loads, $groupLoads ),
+ 'loadMonitor' => $this->loadMonitorClass,
+ 'readOnlyReason' => $readOnlyReason
+ ]
+ ) );
$this->initLoadBalancer( $lb );
return $lb;
$this->initLoadBalancer( $lb );
return $lb;
}
private function newLoadBalancer( array $servers ) {
}
private function newLoadBalancer( array $servers ) {
- $lb = new LoadBalancer( [
- 'servers' => $servers,
- 'loadMonitor' => $this->loadMonitorClass,
- 'readOnlyReason' => $this->readOnlyReason,
- 'trxProfiler' => $this->trxProfiler,
- 'srvCache' => $this->srvCache,
- 'wanCache' => $this->wanCache
- ] );
-
+ $lb = new LoadBalancer( array_merge(
+ $this->baseLoadBalancerParams(),
+ [
+ 'servers' => $servers,
+ 'loadMonitor' => $this->loadMonitorClass,
+ ]
+ ) );
$this->initLoadBalancer( $lb );
return $lb;
$this->initLoadBalancer( $lb );
return $lb;
public function __construct( array $conf ) {
parent::__construct( $conf );
public function __construct( array $conf ) {
parent::__construct( $conf );
- $this->lb = new LoadBalancerSingle( [
- 'readOnlyReason' => $this->readOnlyReason,
- 'trxProfiler' => $this->trxProfiler,
- 'srvCache' => $this->srvCache,
- 'wanCache' => $this->wanCache
- ] + $conf );
+ $this->lb = new LoadBalancerSingle( array_merge( $this->baseLoadBalancerParams(), $conf ) );
private $trxRoundId = false;
/** @var array[] Map of (name => callable) */
private $trxRecurringCallbacks = [];
private $trxRoundId = false;
/** @var array[] Map of (name => callable) */
private $trxRecurringCallbacks = [];
+ /** @var string Local Wiki ID and default for selectDB() calls */
+ private $localDomain;
+ /** @var callable Exception logger */
+ private $errorLogger;
/** @var integer Warn when this many connection are held */
const CONN_HELD_WARN_THRESHOLD = 10;
/** @var integer Warn when this many connection are held */
const CONN_HELD_WARN_THRESHOLD = 10;
* - waitTimeout : Maximum time to wait for replicas for consistency [optional]
* - srvCache : BagOStuff object [optional]
* - wanCache : WANObjectCache object [optional]
* - waitTimeout : Maximum time to wait for replicas for consistency [optional]
* - srvCache : BagOStuff object [optional]
* - wanCache : WANObjectCache object [optional]
+ * - localDomain: The wiki ID of the "local"/"current" wiki [optional]
+ * - errorLogger: Callback that takes an Exception and logs it [optional]
* @throws MWException
*/
public function __construct( array $params ) {
* @throws MWException
*/
public function __construct( array $params ) {
$this->mWaitTimeout = isset( $params['waitTimeout'] )
? $params['waitTimeout']
: self::POS_WAIT_TIMEOUT;
$this->mWaitTimeout = isset( $params['waitTimeout'] )
? $params['waitTimeout']
: self::POS_WAIT_TIMEOUT;
+ $this->localDomain = isset( $params['localDomain'] ) ? $params['localDomain'] : '';
$this->mReadIndex = -1;
$this->mWriteIndex = -1;
$this->mReadIndex = -1;
$this->mWriteIndex = -1;
} else {
$this->trxProfiler = new TransactionProfiler();
}
} else {
$this->trxProfiler = new TransactionProfiler();
}
+
+ $this->errorLogger = isset( $params['errorLogger'] )
+ ? $params['errorLogger']
+ : function ( Exception $e ) {
+ trigger_error( E_WARNING, $e->getMessage() );
+ };
* @return bool|int|string
*/
public function getReaderIndex( $group = false, $wiki = false ) {
* @return bool|int|string
*/
public function getReaderIndex( $group = false, $wiki = false ) {
- global $wgDBtype;
-
- # @todo FIXME: For now, only go through all this for mysql databases
- if ( $wgDBtype != 'mysql' ) {
- return $this->getWriterIndex();
- }
-
if ( count( $this->mServers ) == 1 ) {
# Skip the load balancing if there's only one server
if ( count( $this->mServers ) == 1 ) {
# Skip the load balancing if there's only one server
+ return $this->getWriterIndex();
} elseif ( $group === false && $this->mReadIndex >= 0 ) {
# Shortcut if generic reader exists already
return $this->mReadIndex;
} elseif ( $group === false && $this->mReadIndex >= 0 ) {
# Shortcut if generic reader exists already
return $this->mReadIndex;
throw new MWException( "Empty server array given to LoadBalancer" );
}
throw new MWException( "Empty server array given to LoadBalancer" );
}
- # Scale the configured load ratios according to the dynamic load (if the load monitor supports it)
+ # Scale the configured load ratios according to the dynamic load if supported
$this->getLoadMonitor()->scaleLoads( $nonErrorLoads, $group, $wiki );
$laggedReplicaMode = false;
$this->getLoadMonitor()->scaleLoads( $nonErrorLoads, $group, $wiki );
$laggedReplicaMode = false;
' with invalid server index' );
}
' with invalid server index' );
}
- if ( $wiki === wfWikiID() ) {
+ if ( $wiki === $this->localDomain ) {
if ( $this->connsOpened > $oldConnsOpened ) {
$host = $conn->getServer();
$dbname = $conn->getDBname();
if ( $this->connsOpened > $oldConnsOpened ) {
$host = $conn->getServer();
$dbname = $conn->getDBname();
- $trxProf = Profiler::instance()->getTransactionProfiler();
- $trxProf->recordConnection( $host, $dbname, $masterOnly );
+ $this->trxProfiler->recordConnection( $host, $dbname, $masterOnly );
* @return DBConnRef
*/
public function getLazyConnectionRef( $db, $groups = [], $wiki = false ) {
* @return DBConnRef
*/
public function getLazyConnectionRef( $db, $groups = [], $wiki = false ) {
+ $wiki = ( $wiki !== false ) ? $wiki : $this->localDomain;
+
return new DBConnRef( $this, [ $db, $groups, $wiki ] );
}
return new DBConnRef( $this, [ $db, $groups, $wiki ] );
}
* @return DatabaseBase
*/
private function openForeignConnection( $i, $wiki ) {
* @return DatabaseBase
*/
private function openForeignConnection( $i, $wiki ) {
- list( $dbName, $prefix ) = wfSplitWikiID( $wiki );
+ list( $dbName, $prefix ) = explode( '-', $wiki, 2 ) + [ '', '' ];
+
if ( isset( $this->mConns['foreignUsed'][$i][$wiki] ) ) {
// Reuse an already-used connection
$conn = $this->mConns['foreignUsed'][$i][$wiki];
if ( isset( $this->mConns['foreignUsed'][$i][$wiki] ) ) {
// Reuse an already-used connection
$conn = $this->mConns['foreignUsed'][$i][$wiki];
try {
$conn->commit( $fname, $conn::FLUSHING_ALL_PEERS );
} catch ( DBError $e ) {
try {
$conn->commit( $fname, $conn::FLUSHING_ALL_PEERS );
} catch ( DBError $e ) {
- MWExceptionHandler::logException( $e );
+ call_user_func( $this->errorLogger, $e );
$failures[] = "{$conn->getServer()}: {$e->getMessage()}";
}
if ( $restore && $conn->getLBInfo( 'master' ) ) {
$failures[] = "{$conn->getServer()}: {$e->getMessage()}";
}
if ( $restore && $conn->getLBInfo( 'master' ) ) {
try {
$conn->flushSnapshot( $fname );
} catch ( DBError $e ) {
try {
$conn->flushSnapshot( $fname );
} catch ( DBError $e ) {
- MWExceptionHandler::logException( $e );
+ call_user_func( $this->errorLogger, $e );
$failures[] = "{$conn->getServer()}: {$e->getMessage()}";
}
$conn->setTrxEndCallbackSuppression( false );
$failures[] = "{$conn->getServer()}: {$e->getMessage()}";
}
$conn->setTrxEndCallbackSuppression( false );
$conn->flushSnapshot( $fname );
}
} catch ( DBError $e ) {
$conn->flushSnapshot( $fname );
}
} catch ( DBError $e ) {
- MWExceptionHandler::logException( $e );
+ call_user_func( $this->errorLogger, $e );
$failures[] = "{$conn->getServer()}: {$e->getMessage()}";
}
if ( $restore ) {
$failures[] = "{$conn->getServer()}: {$e->getMessage()}";
}
if ( $restore ) {
+
+ /**
+ * Set a new table prefix for the existing local wiki ID for testing
+ *
+ * @param string $prefix
+ * @since 1.28
+ */
+ public function setDomainPrefix( $prefix ) {
+ list( $dbName, ) = explode( '-', $this->localDomain, 2 );
+
+ $this->localDomain = "{$dbName}-{$prefix}";
+ }