Rename LBFactory => LBFactoryMW and make LBFactory in /libs

The former extends the later with MW-specific logic.

Also removed a wf* method call from ChronologyProtector.

Change-Id: I325f59b7467ab9c2137731d1ce69816f5a020f03
This commit is contained in:
Aaron Schulz 2016-09-14 04:34:17 -07:00
parent aeedfb8526
commit 0e5cd18b74
11 changed files with 257 additions and 176 deletions

View file

@ -653,7 +653,8 @@ $wgAutoloadLocalClasses = [
'JsonContentHandler' => __DIR__ . '/includes/content/JsonContentHandler.php', 'JsonContentHandler' => __DIR__ . '/includes/content/JsonContentHandler.php',
'KkConverter' => __DIR__ . '/languages/classes/LanguageKk.php', 'KkConverter' => __DIR__ . '/languages/classes/LanguageKk.php',
'KuConverter' => __DIR__ . '/languages/classes/LanguageKu.php', 'KuConverter' => __DIR__ . '/languages/classes/LanguageKu.php',
'LBFactory' => __DIR__ . '/includes/db/loadbalancer/LBFactory.php', 'LBFactory' => __DIR__ . '/includes/libs/rdbms/lbfactory/LBFactory.php',
'LBFactoryMW' => __DIR__ . '/includes/db/loadbalancer/LBFactoryMW.php',
'LBFactoryMulti' => __DIR__ . '/includes/db/loadbalancer/LBFactoryMulti.php', 'LBFactoryMulti' => __DIR__ . '/includes/db/loadbalancer/LBFactoryMulti.php',
'LBFactorySimple' => __DIR__ . '/includes/db/loadbalancer/LBFactorySimple.php', 'LBFactorySimple' => __DIR__ . '/includes/db/loadbalancer/LBFactorySimple.php',
'LBFactorySingle' => __DIR__ . '/includes/db/loadbalancer/LBFactorySingle.php', 'LBFactorySingle' => __DIR__ . '/includes/db/loadbalancer/LBFactorySingle.php',

View file

@ -45,7 +45,7 @@ return [
'DBLoadBalancerFactory' => function( MediaWikiServices $services ) { 'DBLoadBalancerFactory' => function( MediaWikiServices $services ) {
$config = $services->getMainConfig()->get( 'LBFactoryConf' ); $config = $services->getMainConfig()->get( 'LBFactoryConf' );
$class = LBFactory::getLBFactoryClass( $config ); $class = LBFactoryMW::getLBFactoryClass( $config );
if ( !isset( $config['readOnlyReason'] ) ) { if ( !isset( $config['readOnlyReason'] ) ) {
// TODO: replace the global wfConfiguredReadOnlyReason() with a service. // TODO: replace the global wfConfiguredReadOnlyReason() with a service.
$config['readOnlyReason'] = wfConfiguredReadOnlyReason(); $config['readOnlyReason'] = wfConfiguredReadOnlyReason();

View file

@ -129,7 +129,10 @@ class CloneDatabase {
*/ */
public static function changePrefix( $prefix ) { public static function changePrefix( $prefix ) {
global $wgDBprefix; global $wgDBprefix;
wfGetLBFactory()->forEachLB( function( LoadBalancer $lb ) use ( $prefix ) {
$lbFactory = wfGetLBFactory();
$lbFactory->setDomainPrefix( $prefix );
$lbFactory->forEachLB( function( LoadBalancer $lb ) use ( $prefix ) {
$lb->setDomainPrefix( $prefix ); $lb->setDomainPrefix( $prefix );
$lb->forEachOpenConnection( function ( IDatabase $db ) use ( $prefix ) { $lb->forEachOpenConnection( function ( IDatabase $db ) use ( $prefix ) {
$db->tablePrefix( $prefix ); $db->tablePrefix( $prefix );

View file

@ -0,0 +1,149 @@
<?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
*/
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 {
/** @noinspection PhpMissingParentConstructorInspection */
/**
* Construct a factory based on a configuration array (typically from $wgLBFactoryConf)
* @param array $conf
* @TODO: inject objects via dependency framework
*/
public function __construct( array $conf ) {
$defaults = [
'domain' => wfWikiID(),
'hostname' => wfHostname(),
'trxProfiler' => Profiler::instance()->getTransactionProfiler(),
'replLogger' => LoggerFactory::getInstance( 'DBReplication' ),
'queryLogger' => LoggerFactory::getInstance( 'wfLogDBError' ),
'connLogger' => LoggerFactory::getInstance( 'wfLogDBError' ),
'perfLogger' => LoggerFactory::getInstance( 'DBPerformance' ),
'errorLogger' => [ MWExceptionHandler::class, 'logException' ]
];
// Use APC/memcached style caching, but avoids loops with CACHE_DB (T141804)
$sCache = ObjectCache::getLocalServerInstance();
if ( $sCache->getQoS( $sCache::ATTR_EMULATION ) > $sCache::QOS_EMULATION_SQL ) {
$defaults['srvCache'] = $sCache;
}
$cCache = ObjectCache::getLocalClusterInstance();
if ( $cCache->getQoS( $cCache::ATTR_EMULATION ) > $cCache::QOS_EMULATION_SQL ) {
$defaults['memCache'] = $cCache;
}
$wCache = ObjectCache::getMainWANInstance();
if ( $wCache->getQoS( $wCache::ATTR_EMULATION ) > $wCache::QOS_EMULATION_SQL ) {
$defaults['wanCache'] = $wCache;
}
parent::__construct( $conf + $defaults );
}
/**
* Returns the LBFactory class to use and the load balancer configuration.
*
* @todo instead of this, use a ServiceContainer for managing the different implementations.
*
* @param array $config (e.g. $wgLBFactoryConf)
* @return string Class name
*/
public static function getLBFactoryClass( array $config ) {
// For configuration backward compatibility after removing
// underscores from class names in MediaWiki 1.23.
$bcClasses = [
'LBFactory_Simple' => 'LBFactorySimple',
'LBFactory_Single' => 'LBFactorySingle',
'LBFactory_Multi' => 'LBFactoryMulti'
];
$class = $config['class'];
if ( isset( $bcClasses[$class] ) ) {
$class = $bcClasses[$class];
wfDeprecated(
'$wgLBFactoryConf must be updated. See RELEASE-NOTES for details',
'1.23'
);
}
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 ] );
}
}

View file

@ -83,7 +83,7 @@
* *
* @ingroup Database * @ingroup Database
*/ */
class LBFactoryMulti extends LBFactory { class LBFactoryMulti extends LBFactoryMW {
/** @var array A map of database names to section names */ /** @var array A map of database names to section names */
private $sectionsByDB; private $sectionsByDB;

View file

@ -24,7 +24,7 @@
/** /**
* A simple single-master LBFactory that gets its configuration from the b/c globals * A simple single-master LBFactory that gets its configuration from the b/c globals
*/ */
class LBFactorySimple extends LBFactory { class LBFactorySimple extends LBFactoryMW {
/** @var LoadBalancer */ /** @var LoadBalancer */
private $mainLB; private $mainLB;
/** @var LoadBalancer[] */ /** @var LoadBalancer[] */

View file

@ -96,17 +96,17 @@ class ChronologyProtector implements LoggerAwareInterface{
} }
/** /**
* Initialise a LoadBalancer to give it appropriate chronology protection. * Initialise a ILoadBalancer to give it appropriate chronology protection.
* *
* If the stash has a previous master position recorded, this will try to * If the stash has a previous master position recorded, this will try to
* make sure that the next query to a replica DB of that master will see changes up * make sure that the next query to a replica DB of that master will see changes up
* to that position by delaying execution. The delay may timeout and allow stale * to that position by delaying execution. The delay may timeout and allow stale
* data if no non-lagged replica DBs are available. * data if no non-lagged replica DBs are available.
* *
* @param LoadBalancer $lb * @param ILoadBalancer $lb
* @return void * @return void
*/ */
public function initLB( LoadBalancer $lb ) { public function initLB( ILoadBalancer $lb ) {
if ( !$this->enabled || $lb->getServerCount() <= 1 ) { if ( !$this->enabled || $lb->getServerCount() <= 1 ) {
return; // non-replicated setup or disabled return; // non-replicated setup or disabled
} }
@ -122,13 +122,13 @@ class ChronologyProtector implements LoggerAwareInterface{
} }
/** /**
* Notify the ChronologyProtector that the LoadBalancer is about to shut * Notify the ChronologyProtector that the ILoadBalancer is about to shut
* down. Saves replication positions. * down. Saves replication positions.
* *
* @param LoadBalancer $lb * @param ILoadBalancer $lb
* @return void * @return void
*/ */
public function shutdownLB( LoadBalancer $lb ) { public function shutdownLB( ILoadBalancer $lb ) {
if ( !$this->enabled ) { if ( !$this->enabled ) {
return; // not enabled return; // not enabled
} elseif ( !$lb->hasOrMadeRecentMasterChanges( INF ) ) { } elseif ( !$lb->hasOrMadeRecentMasterChanges( INF ) ) {

View file

@ -1,6 +1,6 @@
<?php <?php
/** /**
* Generator of database load balancing objects. * Generator and manager of database load balancing objects
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -22,23 +22,26 @@
*/ */
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use MediaWiki\MediaWikiServices;
use MediaWiki\Services\DestructibleService;
use MediaWiki\Logger\LoggerFactory;
/** /**
* An interface for generating database load balancers * An interface for generating database load balancers
* @ingroup Database * @ingroup Database
*/ */
abstract class LBFactory implements DestructibleService { abstract class LBFactory {
/** @var ChronologyProtector */ /** @var ChronologyProtector */
protected $chronProt; protected $chronProt;
/** @var TransactionProfiler */ /** @var TransactionProfiler */
protected $trxProfiler; protected $trxProfiler;
/** @var LoggerInterface */ /** @var LoggerInterface */
protected $trxLogger;
/** @var LoggerInterface */
protected $replLogger; protected $replLogger;
/** @var LoggerInterface */
protected $connLogger;
/** @var LoggerInterface */
protected $queryLogger;
/** @var LoggerInterface */
protected $perfLogger;
/** @var callable Error logger */
protected $errorLogger;
/** @var BagOStuff */ /** @var BagOStuff */
protected $srvCache; protected $srvCache;
/** @var BagOStuff */ /** @var BagOStuff */
@ -46,6 +49,10 @@ abstract class LBFactory implements DestructibleService {
/** @var WANObjectCache */ /** @var WANObjectCache */
protected $wanCache; protected $wanCache;
/** @var string Local domain */
protected $domain;
/** @var string Local hostname of the app server */
protected $hostname;
/** @var mixed */ /** @var mixed */
protected $ticket; protected $ticket;
/** @var string|bool String if a requested DBO_TRX transaction round is active */ /** @var string|bool String if a requested DBO_TRX transaction round is active */
@ -59,38 +66,44 @@ abstract class LBFactory implements DestructibleService {
const SHUTDOWN_CHRONPROT_ASYNC = 1; // save DB positions, but don't wait on remote DCs const SHUTDOWN_CHRONPROT_ASYNC = 1; // save DB positions, but don't wait on remote DCs
const SHUTDOWN_CHRONPROT_SYNC = 2; // save DB positions, waiting on all DCs const SHUTDOWN_CHRONPROT_SYNC = 2; // save DB positions, waiting on all DCs
private static $loggerFields =
[ 'replLogger', 'connLogger', 'queryLogger', 'perfLogger' ];
/** /**
* Construct a factory based on a configuration array (typically from $wgLBFactoryConf) * @TODO: document base params here
* @param array $conf * @param array $conf
* @TODO: inject objects via dependency framework
*/ */
public function __construct( array $conf ) { public function __construct( array $conf ) {
$this->domain = isset( $conf['domain'] ) ? $conf['domain'] : '';
if ( isset( $conf['readOnlyReason'] ) && is_string( $conf['readOnlyReason'] ) ) { if ( isset( $conf['readOnlyReason'] ) && is_string( $conf['readOnlyReason'] ) ) {
$this->readOnlyReason = $conf['readOnlyReason']; $this->readOnlyReason = $conf['readOnlyReason'];
} }
// Use APC/memcached style caching, but avoids loops with CACHE_DB (T141804)
$sCache = ObjectCache::getLocalServerInstance(); $this->srvCache = isset( $conf['srvCache'] ) ? $conf['srvCache'] : new EmptyBagOStuff();
if ( $sCache->getQoS( $sCache::ATTR_EMULATION ) > $sCache::QOS_EMULATION_SQL ) { $this->memCache = isset( $conf['memCache'] ) ? $conf['memCache'] : new EmptyBagOStuff();
$this->srvCache = $sCache; $this->wanCache = isset( $conf['wanCache'] )
} else { ? $conf['wanCache']
$this->srvCache = new EmptyBagOStuff(); : WANObjectCache::newEmpty();
foreach ( self::$loggerFields as $key ) {
$this->$key = isset( $conf[$key] ) ? $conf[$key] : new \Psr\Log\NullLogger();
} }
$cCache = ObjectCache::getLocalClusterInstance(); $this->errorLogger = isset( $conf['errorLogger'] )
if ( $cCache->getQoS( $cCache::ATTR_EMULATION ) > $cCache::QOS_EMULATION_SQL ) { ? $conf['errorLogger']
$this->memCache = $cCache; : function ( Exception $e ) {
} else { trigger_error( E_WARNING, $e->getMessage() );
$this->memCache = new EmptyBagOStuff(); };
} $this->hostname = isset( $conf['hostname'] )
$wCache = ObjectCache::getMainWANInstance(); ? $conf['hostname']
if ( $wCache->getQoS( $wCache::ATTR_EMULATION ) > $wCache::QOS_EMULATION_SQL ) { : gethostname();
$this->wanCache = $wCache;
} else { $this->chronProt = isset( $conf['chronProt'] )
$this->wanCache = WANObjectCache::newEmpty(); ? $conf['chronProt']
} : $this->newChronologyProtector();
$this->trxProfiler = Profiler::instance()->getTransactionProfiler(); $this->trxProfiler = isset( $conf['trxProfiler'] )
$this->trxLogger = LoggerFactory::getInstance( 'DBTransaction' ); ? $conf['trxProfiler']
$this->replLogger = LoggerFactory::getInstance( 'DBReplication' ); : new TransactionProfiler();
$this->chronProt = $this->newChronologyProtector();
$this->ticket = mt_rand(); $this->ticket = mt_rand();
} }
@ -104,80 +117,22 @@ abstract class LBFactory implements DestructibleService {
$this->forEachLBCallMethod( 'disable' ); $this->forEachLBCallMethod( 'disable' );
} }
/**
* Disables all access to the load balancer, will cause all database access
* to throw a DBAccessError
*/
public static function disableBackend() {
MediaWikiServices::disableStorageBackend();
}
/**
* Get an LBFactory instance
*
* @deprecated since 1.27, use MediaWikiServices::getDBLoadBalancerFactory() instead.
*
* @return LBFactory
*/
public static function singleton() {
return MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
}
/**
* Returns the LBFactory class to use and the load balancer configuration.
*
* @todo instead of this, use a ServiceContainer for managing the different implementations.
*
* @param array $config (e.g. $wgLBFactoryConf)
* @return string Class name
*/
public static function getLBFactoryClass( array $config ) {
// For configuration backward compatibility after removing
// underscores from class names in MediaWiki 1.23.
$bcClasses = [
'LBFactory_Simple' => 'LBFactorySimple',
'LBFactory_Single' => 'LBFactorySingle',
'LBFactory_Multi' => 'LBFactoryMulti',
];
$class = $config['class'];
if ( isset( $bcClasses[$class] ) ) {
$class = $bcClasses[$class];
wfDeprecated(
'$wgLBFactoryConf must be updated. See RELEASE-NOTES for details',
'1.23'
);
}
return $class;
}
/**
* Shut down, close connections and destroy the cached instance.
*
* @deprecated since 1.27, use LBFactory::destroy()
*/
public static function destroyInstance() {
MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->destroy();
}
/** /**
* Create a new load balancer object. The resulting object will be untracked, * Create a new load balancer object. The resulting object will be untracked,
* not chronology-protected, and the caller is responsible for cleaning it up. * not chronology-protected, and the caller is responsible for cleaning it up.
* *
* @param bool|string $wiki Wiki ID, or false for the current wiki * @param bool|string $domain Wiki ID, or false for the current wiki
* @return LoadBalancer * @return ILoadBalancer
*/ */
abstract public function newMainLB( $wiki = false ); abstract public function newMainLB( $domain = false );
/** /**
* Get a cached (tracked) load balancer object. * Get a cached (tracked) load balancer object.
* *
* @param bool|string $wiki Wiki ID, or false for the current wiki * @param bool|string $domain Wiki ID, or false for the current wiki
* @return LoadBalancer * @return ILoadBalancer
*/ */
abstract public function getMainLB( $wiki = false ); abstract public function getMainLB( $domain = false );
/** /**
* Create a new load balancer for external storage. The resulting object will be * Create a new load balancer for external storage. The resulting object will be
@ -185,19 +140,19 @@ abstract class LBFactory implements DestructibleService {
* cleaning it up. * cleaning it up.
* *
* @param string $cluster External storage cluster, or false for core * @param string $cluster External storage cluster, or false for core
* @param bool|string $wiki Wiki ID, or false for the current wiki * @param bool|string $domain Wiki ID, or false for the current wiki
* @return LoadBalancer * @return ILoadBalancer
*/ */
abstract protected function newExternalLB( $cluster, $wiki = false ); abstract protected function newExternalLB( $cluster, $domain = false );
/** /**
* Get a cached (tracked) load balancer for external storage * Get a cached (tracked) load balancer for external storage
* *
* @param string $cluster External storage cluster, or false for core * @param string $cluster External storage cluster, or false for core
* @param bool|string $wiki Wiki ID, or false for the current wiki * @param bool|string $domain Wiki ID, or false for the current wiki
* @return LoadBalancer * @return ILoadBalancer
*/ */
abstract public function getExternalLB( $cluster, $wiki = false ); abstract public function getExternalLB( $cluster, $domain = false );
/** /**
* Execute a function for each tracked load balancer * Execute a function for each tracked load balancer
@ -232,9 +187,9 @@ abstract class LBFactory implements DestructibleService {
* @param string $methodName * @param string $methodName
* @param array $args * @param array $args
*/ */
private function forEachLBCallMethod( $methodName, array $args = [] ) { protected function forEachLBCallMethod( $methodName, array $args = [] ) {
$this->forEachLB( $this->forEachLB(
function ( LoadBalancer $loadBalancer, $methodName, array $args ) { function ( ILoadBalancer $loadBalancer, $methodName, array $args ) {
call_user_func_array( [ $loadBalancer, $methodName ], $args ); call_user_func_array( [ $loadBalancer, $methodName ], $args );
}, },
[ $methodName, $args ] [ $methodName, $args ]
@ -316,7 +271,7 @@ abstract class LBFactory implements DestructibleService {
// Run all post-commit callbacks // Run all post-commit callbacks
/** @var Exception $e */ /** @var Exception $e */
$e = null; // first callback exception $e = null; // first callback exception
$this->forEachLB( function ( LoadBalancer $lb ) use ( &$e ) { $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$e ) {
$ex = $lb->runMasterPostTrxCallbacks( IDatabase::TRIGGER_COMMIT ); $ex = $lb->runMasterPostTrxCallbacks( IDatabase::TRIGGER_COMMIT );
$e = $e ?: $ex; $e = $e ?: $ex;
} ); } );
@ -338,7 +293,7 @@ abstract class LBFactory implements DestructibleService {
$this->forEachLBCallMethod( 'suppressTransactionEndCallbacks' ); $this->forEachLBCallMethod( 'suppressTransactionEndCallbacks' );
$this->forEachLBCallMethod( 'rollbackMasterChanges', [ $fname ] ); $this->forEachLBCallMethod( 'rollbackMasterChanges', [ $fname ] );
// Run all post-rollback callbacks // Run all post-rollback callbacks
$this->forEachLB( function ( LoadBalancer $lb ) { $this->forEachLB( function ( ILoadBalancer $lb ) {
$lb->runMasterPostTrxCallbacks( IDatabase::TRIGGER_ROLLBACK ); $lb->runMasterPostTrxCallbacks( IDatabase::TRIGGER_ROLLBACK );
} ); } );
} }
@ -348,7 +303,7 @@ abstract class LBFactory implements DestructibleService {
*/ */
private function logIfMultiDbTransaction() { private function logIfMultiDbTransaction() {
$callersByDB = []; $callersByDB = [];
$this->forEachLB( function ( LoadBalancer $lb ) use ( &$callersByDB ) { $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$callersByDB ) {
$masterName = $lb->getServerName( $lb->getWriterIndex() ); $masterName = $lb->getServerName( $lb->getWriterIndex() );
$callers = $lb->pendingMasterChangeCallers(); $callers = $lb->pendingMasterChangeCallers();
if ( $callers ) { if ( $callers ) {
@ -362,7 +317,7 @@ abstract class LBFactory implements DestructibleService {
foreach ( $callersByDB as $db => $callers ) { foreach ( $callersByDB as $db => $callers ) {
$msg .= "$db: " . implode( '; ', $callers ) . "\n"; $msg .= "$db: " . implode( '; ', $callers ) . "\n";
} }
$this->trxLogger->info( $msg ); $this->queryLogger->info( $msg );
} }
} }
@ -373,7 +328,7 @@ abstract class LBFactory implements DestructibleService {
*/ */
public function hasMasterChanges() { public function hasMasterChanges() {
$ret = false; $ret = false;
$this->forEachLB( function ( LoadBalancer $lb ) use ( &$ret ) { $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$ret ) {
$ret = $ret || $lb->hasMasterChanges(); $ret = $ret || $lb->hasMasterChanges();
} ); } );
@ -387,22 +342,13 @@ abstract class LBFactory implements DestructibleService {
*/ */
public function laggedReplicaUsed() { public function laggedReplicaUsed() {
$ret = false; $ret = false;
$this->forEachLB( function ( LoadBalancer $lb ) use ( &$ret ) { $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$ret ) {
$ret = $ret || $lb->laggedReplicaUsed(); $ret = $ret || $lb->laggedReplicaUsed();
} ); } );
return $ret; return $ret;
} }
/**
* @return bool
* @since 1.27
* @deprecated Since 1.28; use laggedReplicaUsed()
*/
public function laggedSlaveUsed() {
return $this->laggedReplicaUsed();
}
/** /**
* Determine if any master connection has pending/written changes from this request * Determine if any master connection has pending/written changes from this request
* @param float $age How many seconds ago is "recent" [defaults to LB lag wait timeout] * @param float $age How many seconds ago is "recent" [defaults to LB lag wait timeout]
@ -411,7 +357,7 @@ abstract class LBFactory implements DestructibleService {
*/ */
public function hasOrMadeRecentMasterChanges( $age = null ) { public function hasOrMadeRecentMasterChanges( $age = null ) {
$ret = false; $ret = false;
$this->forEachLB( function ( LoadBalancer $lb ) use ( $age, &$ret ) { $this->forEachLB( function ( ILoadBalancer $lb ) use ( $age, &$ret ) {
$ret = $ret || $lb->hasOrMadeRecentMasterChanges( $age ); $ret = $ret || $lb->hasOrMadeRecentMasterChanges( $age );
} ); } );
return $ret; return $ret;
@ -450,14 +396,14 @@ abstract class LBFactory implements DestructibleService {
]; ];
// Figure out which clusters need to be checked // Figure out which clusters need to be checked
/** @var LoadBalancer[] $lbs */ /** @var ILoadBalancer[] $lbs */
$lbs = []; $lbs = [];
if ( $opts['cluster'] !== false ) { if ( $opts['cluster'] !== false ) {
$lbs[] = $this->getExternalLB( $opts['cluster'] ); $lbs[] = $this->getExternalLB( $opts['cluster'] );
} elseif ( $opts['wiki'] !== false ) { } elseif ( $opts['wiki'] !== false ) {
$lbs[] = $this->getMainLB( $opts['wiki'] ); $lbs[] = $this->getMainLB( $opts['wiki'] );
} else { } else {
$this->forEachLB( function ( LoadBalancer $lb ) use ( &$lbs ) { $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$lbs ) {
$lbs[] = $lb; $lbs[] = $lb;
} ); } );
if ( !$lbs ) { if ( !$lbs ) {
@ -533,7 +479,7 @@ abstract class LBFactory implements DestructibleService {
*/ */
public function getEmptyTransactionTicket( $fname ) { public function getEmptyTransactionTicket( $fname ) {
if ( $this->hasMasterChanges() ) { if ( $this->hasMasterChanges() ) {
$this->trxLogger->error( __METHOD__ . ": $fname does not have outer scope." ); $this->queryLogger->error( __METHOD__ . ": $fname does not have outer scope." );
return null; return null;
} }
@ -553,15 +499,14 @@ abstract class LBFactory implements DestructibleService {
*/ */
public function commitAndWaitForReplication( $fname, $ticket, array $opts = [] ) { public function commitAndWaitForReplication( $fname, $ticket, array $opts = [] ) {
if ( $ticket !== $this->ticket ) { if ( $ticket !== $this->ticket ) {
$logger = LoggerFactory::getInstance( 'DBPerformance' ); $this->perfLogger->error( __METHOD__ . ": $fname does not have outer scope." );
$logger->error( __METHOD__ . ": cannot commit; $fname does not have outer scope." );
return; return;
} }
// The transaction owner and any caller with the empty transaction ticket can commit // The transaction owner and any caller with the empty transaction ticket can commit
// so that getEmptyTransactionTicket() callers don't risk seeing DBTransactionError. // so that getEmptyTransactionTicket() callers don't risk seeing DBTransactionError.
if ( $this->trxRoundId !== false && $fname !== $this->trxRoundId ) { if ( $this->trxRoundId !== false && $fname !== $this->trxRoundId ) {
$this->trxLogger->info( "$fname: committing on behalf of {$this->trxRoundId}." ); $this->queryLogger->info( "$fname: committing on behalf of {$this->trxRoundId}." );
$fnameEffective = $this->trxRoundId; $fnameEffective = $this->trxRoundId;
} else { } else {
$fnameEffective = $fname; $fnameEffective = $fname;
@ -600,22 +545,17 @@ abstract class LBFactory implements DestructibleService {
* @return ChronologyProtector * @return ChronologyProtector
*/ */
protected function newChronologyProtector() { protected function newChronologyProtector() {
$request = RequestContext::getMain()->getRequest();
$chronProt = new ChronologyProtector( $chronProt = new ChronologyProtector(
ObjectCache::getMainStashInstance(), $this->memCache,
[ [
'ip' => $request->getIP(), 'ip' => isset( $_SERVER[ 'REMOTE_ADDR' ] ) ? $_SERVER[ 'REMOTE_ADDR' ] : '',
'agent' => $request->getHeader( 'User-Agent' ), 'agent' => isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : ''
], ],
$request->getFloat( 'cpPosTime', $request->getCookie( 'cpPosTime', '' ) ) isset( $_GET['cpPosTime'] ) ? $_GET['cpPosTime'] : null
); );
$chronProt->setLogger( $this->replLogger ); $chronProt->setLogger( $this->replLogger );
if ( PHP_SAPI === 'cli' ) { if ( PHP_SAPI === 'cli' ) {
$chronProt->setEnabled( false ); $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; return $chronProt;
@ -632,7 +572,7 @@ abstract class LBFactory implements DestructibleService {
ChronologyProtector $cp, $workCallback, $mode ChronologyProtector $cp, $workCallback, $mode
) { ) {
// Record all the master positions needed // Record all the master positions needed
$this->forEachLB( function ( LoadBalancer $lb ) use ( $cp ) { $this->forEachLB( function ( ILoadBalancer $lb ) use ( $cp ) {
$cp->shutdownLB( $lb ); $cp->shutdownLB( $lb );
} ); } );
// Write them to the persistent stash. Try to do something useful by running $work // Write them to the persistent stash. Try to do something useful by running $work
@ -646,7 +586,7 @@ abstract class LBFactory implements DestructibleService {
// replica DBs to catch up before responding. Even if there are several DCs, this increases // replica DBs to catch up before responding. Even if there are several DCs, this increases
// the chance that the user will see their own changes immediately afterwards. As long // the chance that the user will see their own changes immediately afterwards. As long
// as the sticky DC cookie applies (same domain), this is not even an issue. // as the sticky DC cookie applies (same domain), this is not even an issue.
$this->forEachLB( function ( LoadBalancer $lb ) use ( $unsavedPositions ) { $this->forEachLB( function ( ILoadBalancer $lb ) use ( $unsavedPositions ) {
$masterName = $lb->getServerName( $lb->getWriterIndex() ); $masterName = $lb->getServerName( $lb->getWriterIndex() );
if ( isset( $unsavedPositions[$masterName] ) ) { if ( isset( $unsavedPositions[$masterName] ) ) {
$lb->waitForAll( $unsavedPositions[$masterName] ); $lb->waitForAll( $unsavedPositions[$masterName] );
@ -660,50 +600,38 @@ abstract class LBFactory implements DestructibleService {
*/ */
final protected function baseLoadBalancerParams() { final protected function baseLoadBalancerParams() {
return [ return [
'localDomain' => wfWikiID(), 'localDomain' => $this->domain,
'readOnlyReason' => $this->readOnlyReason, 'readOnlyReason' => $this->readOnlyReason,
'srvCache' => $this->srvCache, 'srvCache' => $this->srvCache,
'memCache' => $this->memCache,
'wanCache' => $this->wanCache, 'wanCache' => $this->wanCache,
'trxProfiler' => $this->trxProfiler, 'trxProfiler' => $this->trxProfiler,
'queryLogger' => LoggerFactory::getInstance( 'DBQuery' ), 'queryLogger' => $this->queryLogger,
'connLogger' => LoggerFactory::getInstance( 'DBConnection' ), 'connLogger' => $this->connLogger,
'replLogger' => LoggerFactory::getInstance( 'DBReplication' ), 'replLogger' => $this->replLogger,
'errorLogger' => [ MWExceptionHandler::class, 'logException' ], 'errorLogger' => $this->errorLogger,
'hostname' => wfHostname() 'hostname' => $this->hostname
]; ];
} }
/** /**
* @param LoadBalancer $lb * @param ILoadBalancer $lb
*/ */
protected function initLoadBalancer( LoadBalancer $lb ) { protected function initLoadBalancer( ILoadBalancer $lb ) {
if ( $this->trxRoundId !== false ) { if ( $this->trxRoundId !== false ) {
$lb->beginMasterChanges( $this->trxRoundId ); // set DBO_TRX $lb->beginMasterChanges( $this->trxRoundId ); // set DBO_TRX
} }
} }
/** /**
* Append ?cpPosTime parameter to a URL for ChronologyProtector purposes if needed * Define a new local domain (for testing)
* *
* Note that unlike cookies, this works accross domains * Caller should make sure no local connection are open to the old local domain
* *
* @param string $url * @param string $domain
* @param float $time UNIX timestamp just before shutdown() was called
* @return string
* @since 1.28 * @since 1.28
*/ */
public function appendPreShutdownTimeAsQuery( $url, $time ) { public function setDomainPrefix( $domain ) {
$usedCluster = 0; $this->domain = $domain;
$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 ] );
} }
/** /**

View file

@ -28,11 +28,11 @@ class LoadMonitorNull implements LoadMonitor {
public function setLogger( LoggerInterface $logger ) { public function setLogger( LoggerInterface $logger ) {
} }
public function scaleLoads( &$loads, $group = false, $wiki = false ) { public function scaleLoads( &$loads, $group = false, $domain = false ) {
} }
public function getLagTimes( $serverIndexes, $wiki ) { public function getLagTimes( $serverIndexes, $domain ) {
return array_fill_keys( $serverIndexes, 0 ); return array_fill_keys( $serverIndexes, 0 );
} }

View file

@ -1091,7 +1091,7 @@ abstract class Maintenance {
$wgLBFactoryConf['serverTemplate']['user'] = $wgDBuser; $wgLBFactoryConf['serverTemplate']['user'] = $wgDBuser;
$wgLBFactoryConf['serverTemplate']['password'] = $wgDBpassword; $wgLBFactoryConf['serverTemplate']['password'] = $wgDBpassword;
} }
LBFactory::destroyInstance(); MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->destroy();
} }
// Per-script profiling; useful for debugging // Per-script profiling; useful for debugging

View file

@ -43,7 +43,7 @@ class LBFactoryTest extends MediaWikiTestCase {
]; ];
$this->hideDeprecated( '$wgLBFactoryConf must be updated. See RELEASE-NOTES for details' ); $this->hideDeprecated( '$wgLBFactoryConf must be updated. See RELEASE-NOTES for details' );
$result = LBFactory::getLBFactoryClass( $config ); $result = LBFactoryMW::getLBFactoryClass( $config );
$this->assertEquals( $expected, $result ); $this->assertEquals( $expected, $result );
} }