* Refactored LBFactory a bit to make this possible.
* Move newChronologyProtector() up to LBFactory and
make a lazy-loading method instead.
* Move appendPreShutdownTimeAsQuery() up to LBFactory.
* Inject the web request values for LBFactory from Setup.php.
* Remove unused laggedSlaveUsed() method.
Change-Id: Ie8a38a6f4d6359680eb6a5be24a34e30b9816479
'LBFactory' => __DIR__ . '/includes/libs/rdbms/lbfactory/LBFactory.php',
'LBFactoryMW' => __DIR__ . '/includes/db/loadbalancer/LBFactoryMW.php',
'LBFactoryMulti' => __DIR__ . '/includes/db/loadbalancer/LBFactoryMulti.php',
- 'LBFactorySimple' => __DIR__ . '/includes/db/loadbalancer/LBFactorySimple.php',
+ 'LBFactorySimple' => __DIR__ . '/includes/libs/rdbms/lbfactory/LBFactorySimple.php',
'LBFactorySingle' => __DIR__ . '/includes/db/loadbalancer/LBFactorySingle.php',
'LCStore' => __DIR__ . '/includes/cache/localisation/LCStore.php',
'LCStoreCDB' => __DIR__ . '/includes/cache/localisation/LCStoreCDB.php',
return [
'DBLoadBalancerFactory' => function( MediaWikiServices $services ) {
- $config = $services->getMainConfig()->get( 'LBFactoryConf' );
+ $mainConfig = $services->getMainConfig();
- $class = LBFactoryMW::getLBFactoryClass( $config );
- if ( !isset( $config['readOnlyReason'] ) ) {
+ $lbConf = $mainConfig->get( 'LBFactoryConf' );
+ $lbConf += [
+ 'localDomain' => new DatabaseDomain(
+ $mainConfig->get( 'DBname' ), null, $mainConfig->get( 'DBprefix' ) ),
// TODO: replace the global wfConfiguredReadOnlyReason() with a service.
- $config['readOnlyReason'] = wfConfiguredReadOnlyReason();
+ 'readOnlyReason' => wfConfiguredReadOnlyReason(),
+ ];
+
+ $class = LBFactoryMW::getLBFactoryClass( $lbConf );
+ if ( $class === 'LBFactorySimple' ) {
+ if ( is_array( $mainConfig->get( 'DBservers' ) ) ) {
+ foreach ( $mainConfig->get( 'DBservers' ) as $i => $server ) {
+ $lbConf['servers'][$i] = $server + [
+ 'schema' => $mainConfig->get( 'DBmwschema' ),
+ 'tablePrefix' => $mainConfig->get( 'DBprefix' ),
+ 'flags' => DBO_DEFAULT,
+ 'sqlMode' => $mainConfig->get( 'SQLMode' ),
+ 'utf8Mode' => $mainConfig->get( 'DBmysql5' )
+ ];
+ }
+ } else {
+ $flags = DBO_DEFAULT;
+ $flags |= $mainConfig->get( 'DebugDumpSql' ) ? DBO_DEBUG : 0;
+ $flags |= $mainConfig->get( 'DBssl' ) ? DBO_SSL : 0;
+ $flags |= $mainConfig->get( 'DBcompress' ) ? DBO_COMPRESS : 0;
+ $lbConf['servers'] = [
+ [
+ 'host' => $mainConfig->get( 'DBserver' ),
+ 'user' => $mainConfig->get( 'DBuser' ),
+ 'password' => $mainConfig->get( 'DBpassword' ),
+ 'dbname' => $mainConfig->get( 'DBname' ),
+ 'schema' => $mainConfig->get( 'DBmwschema' ),
+ 'tablePrefix' => $mainConfig->get( 'DBprefix' ),
+ 'type' => $mainConfig->get( 'DBtype' ),
+ 'load' => 1,
+ 'flags' => $flags,
+ 'sqlMode' => $mainConfig->get( 'SQLMode' ),
+ 'utf8Mode' => $mainConfig->get( 'DBmysql5' )
+ ]
+ ];
+ }
+ $lbConf['externalServers'] = $mainConfig->get( 'ExternalServers' );
}
- return new $class( $config );
+ return new $class( LBFactoryMW::applyDefaultConfig( $lbConf ) );
},
'DBLoadBalancer' => function( MediaWikiServices $services ) {
// Reset the global service locator, so any services that have already been created will be
// re-created while taking into account any custom settings and extensions.
MediaWikiServices::resetGlobalInstance( new GlobalVarConfig(), 'quick' );
-// Apply $wgSharedDB table aliases for the local LB (all non-foreign DB connections)
+
if ( $wgSharedDB && $wgSharedTables ) {
+ // Apply $wgSharedDB table aliases for the local LB (all non-foreign DB connections)
MediaWikiServices::getInstance()->getDBLoadBalancer()->setTableAliases(
array_fill_keys(
$wgSharedTables,
// initialize the request object in $wgRequest
$wgRequest = RequestContext::getMain()->getRequest(); // BackCompat
+// Set user IP/agent information for causal consistency purposes
+MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->setRequestInfo( [
+ 'IPAddress' => $wgRequest->getIP(),
+ 'UserAgent' => $wgRequest->getHeader( 'User-Agent' ),
+ 'ChronologyProtection' => $wgRequest->getHeader( 'ChronologyProtection' )
+] );
// Useful debug output
if ( $wgCommandLineMode ) {
* @ingroup Database
*/
-use MediaWiki\MediaWikiServices;
-use MediaWiki\Services\DestructibleService;
use MediaWiki\Logger\LoggerFactory;
/**
* Legacy MediaWiki-specific class for generating database load balancers
* @ingroup Database
*/
-abstract class LBFactoryMW extends LBFactory implements DestructibleService {
+abstract class LBFactoryMW extends LBFactory {
/** @noinspection PhpMissingParentConstructorInspection */
/**
* Construct a factory based on a configuration array (typically from $wgLBFactoryConf)
* @TODO: inject objects via dependency framework
*/
public function __construct( array $conf ) {
+ parent::__construct( self::applyDefaultConfig( $conf ) );
+ }
+
+ /**
+ * @param array $conf
+ * @return array
+ * @TODO: inject objects via dependency framework
+ */
+ public static function applyDefaultConfig( array $conf ) {
global $wgCommandLineMode, $wgSQLMode, $wgDBmysql5, $wgDBname, $wgDBprefix;
$defaults = [
'queryLogger' => LoggerFactory::getInstance( 'wfLogDBError' ),
'connLogger' => LoggerFactory::getInstance( 'wfLogDBError' ),
'perfLogger' => LoggerFactory::getInstance( 'DBPerformance' ),
- 'errorLogger' => [ MWExceptionHandler::class, 'logException' ]
+ 'errorLogger' => [ MWExceptionHandler::class, 'logException' ],
+ 'cliMode' => $wgCommandLineMode,
+ 'agent' => ''
];
// Use APC/memcached style caching, but avoids loops with CACHE_DB (T141804)
$sCache = ObjectCache::getLocalServerInstance();
$defaults['wanCache'] = $wCache;
}
- $this->agent = isset( $params['agent'] ) ? $params['agent'] : '';
- $this->cliMode = isset( $params['cliMode'] ) ? $params['cliMode'] : $wgCommandLineMode;
-
if ( isset( $conf['serverTemplate'] ) ) { // LBFactoryMulti
$conf['serverTemplate']['sqlMode'] = $wgSQLMode;
$conf['serverTemplate']['utf8Mode'] = $wgDBmysql5;
}
- parent::__construct( $conf + $defaults );
+ return $conf + $defaults;
}
/**
return $class;
}
-
- /**
- * @return bool
- * @since 1.27
- * @deprecated Since 1.28; use laggedReplicaUsed()
- */
- public function laggedSlaveUsed() {
- return $this->laggedReplicaUsed();
- }
-
- protected function newChronologyProtector() {
- $request = RequestContext::getMain()->getRequest();
- $chronProt = new ChronologyProtector(
- ObjectCache::getMainStashInstance(),
- [
- 'ip' => $request->getIP(),
- 'agent' => $request->getHeader( 'User-Agent' ),
- ],
- $request->getFloat( 'cpPosTime', $request->getCookie( 'cpPosTime', '' ) )
- );
- if ( PHP_SAPI === 'cli' ) {
- $chronProt->setEnabled( false );
- } elseif ( $request->getHeader( 'ChronologyProtection' ) === 'false' ) {
- // Request opted out of using position wait logic. This is useful for requests
- // done by the job queue or background ETL that do not have a meaningful session.
- $chronProt->setWaitEnabled( false );
- }
-
- return $chronProt;
- }
-
- /**
- * Append ?cpPosTime parameter to a URL for ChronologyProtector purposes if needed
- *
- * Note that unlike cookies, this works accross domains
- *
- * @param string $url
- * @param float $time UNIX timestamp just before shutdown() was called
- * @return string
- * @since 1.28
- */
- public function appendPreShutdownTimeAsQuery( $url, $time ) {
- $usedCluster = 0;
- $this->forEachLB( function ( LoadBalancer $lb ) use ( &$usedCluster ) {
- $usedCluster |= ( $lb->getServerCount() > 1 );
- } );
-
- if ( !$usedCluster ) {
- return $url; // no master/replica clusters touched
- }
-
- return wfAppendQuery( $url, [ 'cpPosTime' => $time ] );
- }
}
$section = $this->getSectionForWiki( $wiki );
if ( !isset( $this->mainLBs[$section] ) ) {
$lb = $this->newMainLB( $wiki );
- $this->chronProt->initLB( $lb );
+ $this->getChronologyProtector()->initLB( $lb );
$this->mainLBs[$section] = $lb;
}
public function getExternalLB( $cluster, $wiki = false ) {
if ( !isset( $this->extLBs[$cluster] ) ) {
$this->extLBs[$cluster] = $this->newExternalLB( $cluster, $wiki );
- $this->chronProt->initLB( $this->extLBs[$cluster] );
+ $this->getChronologyProtector()->initLB( $this->extLBs[$cluster] );
}
return $this->extLBs[$cluster];
+++ /dev/null
-<?php
-/**
- * Generator of database load balancing objects.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Database
- */
-
-/**
- * A simple single-master LBFactory that gets its configuration from the b/c globals
- */
-class LBFactorySimple extends LBFactoryMW {
- /** @var LoadBalancer */
- private $mainLB;
- /** @var LoadBalancer[] */
- private $extLBs = [];
-
- /** @var string */
- private $loadMonitorClass;
-
- public function __construct( array $conf ) {
- parent::__construct( $conf );
-
- $this->loadMonitorClass = isset( $conf['loadMonitorClass'] )
- ? $conf['loadMonitorClass']
- : null;
- }
-
- /**
- * @param bool|string $wiki
- * @return LoadBalancer
- */
- public function newMainLB( $wiki = false ) {
- global $wgDBservers, $wgDBprefix, $wgDBmwschema, $wgSQLMode, $wgDBmysql5;
-
- if ( is_array( $wgDBservers ) ) {
- $servers = $wgDBservers;
- foreach ( $servers as $i => &$server ) {
- if ( $i == 0 ) {
- $server['master'] = true;
- } else {
- $server['replica'] = true;
- }
- $server += [
- 'schema' => $wgDBmwschema,
- 'tablePrefix' => $wgDBprefix,
- 'flags' => DBO_DEFAULT,
- 'sqlMode' => $wgSQLMode,
- 'utf8Mode' => $wgDBmysql5
- ];
- }
- } else {
- global $wgDBserver, $wgDBuser, $wgDBpassword, $wgDBname, $wgDBtype, $wgDebugDumpSql;
- global $wgDBssl, $wgDBcompress;
-
- $flags = DBO_DEFAULT;
- if ( $wgDebugDumpSql ) {
- $flags |= DBO_DEBUG;
- }
- if ( $wgDBssl ) {
- $flags |= DBO_SSL;
- }
- if ( $wgDBcompress ) {
- $flags |= DBO_COMPRESS;
- }
-
- $servers = [ [
- 'host' => $wgDBserver,
- 'user' => $wgDBuser,
- 'password' => $wgDBpassword,
- 'dbname' => $wgDBname,
- 'schema' => $wgDBmwschema,
- 'tablePrefix' => $wgDBprefix,
- 'type' => $wgDBtype,
- 'load' => 1,
- 'flags' => $flags,
- 'master' => true,
- 'sqlMode' => $wgSQLMode,
- 'utf8Mode' => $wgDBmysql5
- ] ];
- }
-
- return $this->newLoadBalancer( $servers );
- }
-
- /**
- * @param bool|string $wiki
- * @return LoadBalancer
- */
- public function getMainLB( $wiki = false ) {
- if ( !isset( $this->mainLB ) ) {
- $this->mainLB = $this->newMainLB( $wiki );
- $this->chronProt->initLB( $this->mainLB );
- }
-
- return $this->mainLB;
- }
-
- /**
- * @param string $cluster
- * @param bool|string $wiki
- * @return LoadBalancer
- * @throws InvalidArgumentException
- */
- protected function newExternalLB( $cluster, $wiki = false ) {
- global $wgExternalServers;
- if ( !isset( $wgExternalServers[$cluster] ) ) {
- throw new InvalidArgumentException( __METHOD__ . ": Unknown cluster \"$cluster\"" );
- }
-
- return $this->newLoadBalancer( $wgExternalServers[$cluster] );
- }
-
- /**
- * @param string $cluster
- * @param bool|string $wiki
- * @return array
- */
- public function getExternalLB( $cluster, $wiki = false ) {
- if ( !isset( $this->extLBs[$cluster] ) ) {
- $this->extLBs[$cluster] = $this->newExternalLB( $cluster, $wiki );
- $this->chronProt->initLB( $this->extLBs[$cluster] );
- }
-
- return $this->extLBs[$cluster];
- }
-
- private function newLoadBalancer( array $servers ) {
- $lb = new LoadBalancer( array_merge(
- $this->baseLoadBalancerParams(),
- [
- 'servers' => $servers,
- 'loadMonitor' => $this->loadMonitorClass,
- ]
- ) );
- $this->initLoadBalancer( $lb );
-
- return $lb;
- }
-
- /**
- * Execute a function for each tracked load balancer
- * The callback is called with the load balancer as the first parameter,
- * and $params passed as the subsequent parameters.
- *
- * @param callable $callback
- * @param array $params
- */
- public function forEachLB( $callback, array $params = [] ) {
- if ( isset( $this->mainLB ) ) {
- call_user_func_array( $callback, array_merge( [ $this->mainLB ], $params ) );
- }
- foreach ( $this->extLBs as $lb ) {
- call_user_func_array( $callback, array_merge( [ $lb ], $params ) );
- }
- }
-}
protected $localDomain;
/** @var string Local hostname of the app server */
protected $hostname;
+ /** @var array Web request information about the client */
+ protected $requestInfo;
+
/** @var mixed */
protected $ticket;
/** @var string|bool String if a requested DBO_TRX transaction round is active */
: function ( Exception $e ) {
trigger_error( E_WARNING, get_class( $e ) . ': ' . $e->getMessage() );
};
- $this->hostname = isset( $conf['hostname'] )
- ? $conf['hostname']
- : gethostname();
- $this->chronProt = isset( $conf['chronProt'] )
- ? $conf['chronProt']
- : $this->newChronologyProtector();
+ $this->chronProt = isset( $conf['chronProt'] ) ? $conf['chronProt'] : null;
$this->profiler = isset( $params['profiler'] ) ? $params['profiler'] : null;
$this->trxProfiler = isset( $conf['trxProfiler'] )
? $conf['trxProfiler']
: new TransactionProfiler();
- $this->ticket = mt_rand();
+ $this->requestInfo = [
+ 'IPAddress' => isset( $_SERVER[ 'REMOTE_ADDR' ] ) ? $_SERVER[ 'REMOTE_ADDR' ] : '',
+ 'UserAgent' => isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '',
+ 'ChronologyProtection' => 'true'
+ ];
+
$this->cliMode = isset( $params['cliMode'] ) ? $params['cliMode'] : PHP_SAPI === 'cli';
+ $this->hostname = isset( $conf['hostname'] ) ? $conf['hostname'] : gethostname();
$this->agent = isset( $params['agent'] ) ? $params['agent'] : '';
+
+ $this->ticket = mt_rand();
}
/**
public function shutdown(
$mode = self::SHUTDOWN_CHRONPROT_SYNC, callable $workCallback = null
) {
+ $chronProt = $this->getChronologyProtector();
if ( $mode === self::SHUTDOWN_CHRONPROT_SYNC ) {
- $this->shutdownChronologyProtector( $this->chronProt, $workCallback, 'sync' );
+ $this->shutdownChronologyProtector( $chronProt, $workCallback, 'sync' );
} elseif ( $mode === self::SHUTDOWN_CHRONPROT_ASYNC ) {
- $this->shutdownChronologyProtector( $this->chronProt, null, 'async' );
+ $this->shutdownChronologyProtector( $chronProt, null, 'async' );
}
$this->commitMasterChanges( __METHOD__ ); // sanity
* @since 1.28
*/
public function getChronologyProtectorTouched( $dbName ) {
- return $this->chronProt->getTouched( $dbName );
+ return $this->getChronologyProtector()->getTouched( $dbName );
}
/**
* @since 1.27
*/
public function disableChronologyProtection() {
- $this->chronProt->setEnabled( false );
+ $this->getChronologyProtector()->setEnabled( false );
}
/**
* @return ChronologyProtector
*/
- protected function newChronologyProtector() {
- $chronProt = new ChronologyProtector(
+ protected function getChronologyProtector() {
+ if ( $this->chronProt ) {
+ return $this->chronProt;
+ }
+
+ $this->chronProt = new ChronologyProtector(
$this->memCache,
[
- 'ip' => isset( $_SERVER[ 'REMOTE_ADDR' ] ) ? $_SERVER[ 'REMOTE_ADDR' ] : '',
- 'agent' => isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : ''
+ 'ip' => $this->requestInfo['IPAddress'],
+ 'agent' => $this->requestInfo['UserAgent'],
],
isset( $_GET['cpPosTime'] ) ? $_GET['cpPosTime'] : null
);
- $chronProt->setLogger( $this->replLogger );
+ $this->chronProt->setLogger( $this->replLogger );
+
if ( $this->cliMode ) {
- $chronProt->setEnabled( false );
+ $this->chronProt->setEnabled( false );
+ } elseif ( $this->requestInfo['ChronologyProtection'] === 'false' ) {
+ // Request opted out of using position wait logic. This is useful for requests
+ // done by the job queue or background ETL that do not have a meaningful session.
+ $this->chronProt->setWaitEnabled( false );
}
- return $chronProt;
+ $this->replLogger->debug( __METHOD__ . ': using request info ' .
+ json_encode( $this->requestInfo, JSON_PRETTY_PRINT ) );
+
+ return $this->chronProt;
}
/**
public function setAgentName( $agent ) {
$this->agent = $agent;
}
+
+ /**
+ * Append ?cpPosTime parameter to a URL for ChronologyProtector purposes if needed
+ *
+ * Note that unlike cookies, this works accross domains
+ *
+ * @param string $url
+ * @param float $time UNIX timestamp just before shutdown() was called
+ * @return string
+ * @since 1.28
+ */
+ public function appendPreShutdownTimeAsQuery( $url, $time ) {
+ $usedCluster = 0;
+ $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$usedCluster ) {
+ $usedCluster |= ( $lb->getServerCount() > 1 );
+ } );
+
+ if ( !$usedCluster ) {
+ return $url; // no master/replica clusters touched
+ }
+
+ return strpos( $url, '?' ) === false ? "$url?cpPosTime=$time" : "$url&cpPosTime=$time";
+ }
+
+ /**
+ * @param array $info Map of fields, including:
+ * - IPAddress : IP address
+ * - UserAgent : User-Agent HTTP header
+ * - ChronologyProtection : cookie/header value specifying ChronologyProtector usage
+ * @since 1.28
+ */
+ public function setRequestInfo( array $info ) {
+ $this->requestInfo = $info + $this->requestInfo;
+ }
+
+ function __destruct() {
+ $this->destroy();
+ }
}
--- /dev/null
+<?php
+/**
+ * Generator of database load balancing objects.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Database
+ */
+
+/**
+ * A simple single-master LBFactory that gets its configuration from the b/c globals
+ */
+class LBFactorySimple extends LBFactory {
+ /** @var LoadBalancer */
+ private $mainLB;
+ /** @var LoadBalancer[] */
+ private $extLBs = [];
+
+ /** @var array[] Map of (server index => server config) */
+ private $servers = [];
+ /** @var array[] Map of (cluster => (server index => server config)) */
+ private $externalClusters = [];
+
+ /** @var string */
+ private $loadMonitorClass;
+
+ public function __construct( array $conf ) {
+ parent::__construct( $conf );
+
+ $this->servers = isset( $conf['servers'] ) ? $conf['servers'] : [];
+ foreach ( $this->servers as $i => $server ) {
+ if ( $i == 0 ) {
+ $this->servers[$i]['master'] = true;
+ } else {
+ $this->servers[$i]['replica'] = true;
+ }
+ }
+
+ $this->externalClusters = isset( $conf['externalClusters'] )
+ ? $conf['externalClusters']
+ : [];
+ $this->loadMonitorClass = isset( $conf['loadMonitorClass'] )
+ ? $conf['loadMonitorClass']
+ : null;
+ }
+
+ /**
+ * @param bool|string $domain
+ * @return LoadBalancer
+ */
+ public function newMainLB( $domain = false ) {
+ return $this->newLoadBalancer( $this->servers );
+ }
+
+ /**
+ * @param bool|string $domain
+ * @return LoadBalancer
+ */
+ public function getMainLB( $domain = false ) {
+ if ( !isset( $this->mainLB ) ) {
+ $this->mainLB = $this->newMainLB( $domain );
+ $this->getChronologyProtector()->initLB( $this->mainLB );
+ }
+
+ return $this->mainLB;
+ }
+
+ /**
+ * @param string $cluster
+ * @param bool|string $domain
+ * @return LoadBalancer
+ * @throws InvalidArgumentException
+ */
+ protected function newExternalLB( $cluster, $domain = false ) {
+ if ( !isset( $this->externalClusters[$cluster] ) ) {
+ throw new InvalidArgumentException( __METHOD__ . ": Unknown cluster \"$cluster\"" );
+ }
+
+ return $this->newLoadBalancer( $this->externalClusters[$cluster] );
+ }
+
+ /**
+ * @param string $cluster
+ * @param bool|string $domain
+ * @return array
+ */
+ public function getExternalLB( $cluster, $domain = false ) {
+ if ( !isset( $this->extLBs[$cluster] ) ) {
+ $this->extLBs[$cluster] = $this->newExternalLB( $cluster, $domain );
+ $this->getChronologyProtector()->initLB( $this->extLBs[$cluster] );
+ }
+
+ return $this->extLBs[$cluster];
+ }
+
+ private function newLoadBalancer( array $servers ) {
+ $lb = new LoadBalancer( array_merge(
+ $this->baseLoadBalancerParams(),
+ [
+ 'servers' => $servers,
+ 'loadMonitor' => $this->loadMonitorClass,
+ ]
+ ) );
+ $this->initLoadBalancer( $lb );
+
+ return $lb;
+ }
+
+ /**
+ * Execute a function for each tracked load balancer
+ * The callback is called with the load balancer as the first parameter,
+ * and $params passed as the subsequent parameters.
+ *
+ * @param callable $callback
+ * @param array $params
+ */
+ public function forEachLB( $callback, array $params = [] ) {
+ if ( isset( $this->mainLB ) ) {
+ call_user_func_array( $callback, array_merge( [ $this->mainLB ], $params ) );
+ }
+ foreach ( $this->extLBs as $lb ) {
+ call_user_func_array( $callback, array_merge( [ $lb ], $params ) );
+ }
+ }
+}
->disableOriginalConstructor()
->getMock();
- $lbFactory->expects( $this->once() )
- ->method( 'destroy' );
-
$newServices->redefineService(
'DBLoadBalancerFactory',
function() use ( $lbFactory ) {
try {
MediaWikiServices::getInstance()->getService( 'DBLoadBalancerFactory' );
- $this->fail( 'DBLoadBalancerFactory shoudl have been disabled' );
+ $this->fail( 'DBLoadBalancerFactory should have been disabled' );
}
catch ( ServiceDisabledException $ex ) {
// ok, as expected
- }
- catch ( Throwable $ex ) {
+ } catch ( Throwable $ex ) {
$this->fail( 'ServiceDisabledException expected, caught ' . get_class( $ex ) );
}
}
public function testLBFactorySimpleServer() {
- $this->setMwGlobals( 'wgDBservers', false );
+ global $wgDBserver, $wgDBname, $wgDBuser, $wgDBpassword, $wgDBtype;
+
+ $servers = [
+ [
+ 'host' => $wgDBserver,
+ 'dbname' => $wgDBname,
+ 'user' => $wgDBuser,
+ 'password' => $wgDBpassword,
+ 'type' => $wgDBtype,
+ 'load' => 0,
+ 'flags' => DBO_TRX // REPEATABLE-READ for consistency
+ ],
+ ];
- $factory = new LBFactorySimple( [] );
+ $factory = new LBFactorySimple( [ 'servers' => $servers ] );
$lb = $factory->getMainLB();
$dbw = $lb->getConnection( DB_MASTER );
public function testLBFactorySimpleServers() {
global $wgDBserver, $wgDBname, $wgDBuser, $wgDBpassword, $wgDBtype;
- $this->setMwGlobals( 'wgDBservers', [
+ $servers = [
[ // master
- 'host' => $wgDBserver,
- 'dbname' => $wgDBname,
- 'user' => $wgDBuser,
- 'password' => $wgDBpassword,
- 'type' => $wgDBtype,
- 'load' => 0,
- 'flags' => DBO_TRX // REPEATABLE-READ for consistency
+ 'host' => $wgDBserver,
+ 'dbname' => $wgDBname,
+ 'user' => $wgDBuser,
+ 'password' => $wgDBpassword,
+ 'type' => $wgDBtype,
+ 'load' => 0,
+ 'flags' => DBO_TRX // REPEATABLE-READ for consistency
],
[ // emulated slave
- 'host' => $wgDBserver,
- 'dbname' => $wgDBname,
- 'user' => $wgDBuser,
- 'password' => $wgDBpassword,
- 'type' => $wgDBtype,
- 'load' => 100,
- 'flags' => DBO_TRX // REPEATABLE-READ for consistency
+ 'host' => $wgDBserver,
+ 'dbname' => $wgDBname,
+ 'user' => $wgDBuser,
+ 'password' => $wgDBpassword,
+ 'type' => $wgDBtype,
+ 'load' => 100,
+ 'flags' => DBO_TRX // REPEATABLE-READ for consistency
]
- ] );
+ ];
- $factory = new LBFactorySimple( [ 'loadMonitorClass' => 'LoadMonitorNull' ] );
+ $factory = new LBFactorySimple( [
+ 'servers' => $servers,
+ 'loadMonitorClass' => 'LoadMonitorNull'
+ ] );
$lb = $factory->getMainLB();
$dbw = $lb->getConnection( DB_MASTER );