2017-07-20 03:51:54 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Holds tests for LoadBalancer MediaWiki class.
|
|
|
|
|
*
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
2019-10-06 04:54:59 +00:00
|
|
|
use PHPUnit\Framework\Constraint\StringContains;
|
2018-02-28 00:00:05 +00:00
|
|
|
use Wikimedia\Rdbms\Database;
|
2020-01-10 00:00:51 +00:00
|
|
|
use Wikimedia\Rdbms\DatabaseDomain;
|
2022-08-24 00:57:22 +00:00
|
|
|
use Wikimedia\Rdbms\DBConnRef;
|
2020-01-10 00:00:51 +00:00
|
|
|
use Wikimedia\Rdbms\DBError;
|
2019-10-11 22:22:26 +00:00
|
|
|
use Wikimedia\Rdbms\DBReadOnlyRoleError;
|
2022-05-27 20:38:32 +00:00
|
|
|
use Wikimedia\Rdbms\IDatabase;
|
|
|
|
|
use Wikimedia\Rdbms\IMaintainableDatabase;
|
2018-02-28 00:00:05 +00:00
|
|
|
use Wikimedia\Rdbms\LoadBalancer;
|
|
|
|
|
use Wikimedia\Rdbms\LoadMonitorNull;
|
2022-01-28 22:04:40 +00:00
|
|
|
use Wikimedia\Rdbms\TransactionManager;
|
2019-05-08 19:18:01 +00:00
|
|
|
use Wikimedia\TestingAccessWrapper;
|
2018-02-28 00:00:05 +00:00
|
|
|
|
2018-03-09 21:56:41 +00:00
|
|
|
/**
|
|
|
|
|
* @group Database
|
2019-06-18 21:12:06 +00:00
|
|
|
* @group medium
|
2018-03-09 21:56:41 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer
|
|
|
|
|
*/
|
2020-06-30 15:09:24 +00:00
|
|
|
class LoadBalancerTest extends MediaWikiIntegrationTestCase {
|
2019-04-05 04:18:37 +00:00
|
|
|
private function makeServerConfig( $flags = DBO_DEFAULT ) {
|
2017-07-20 03:51:54 +00:00
|
|
|
global $wgDBserver, $wgDBname, $wgDBuser, $wgDBpassword, $wgDBtype, $wgSQLiteDataDir;
|
|
|
|
|
|
2018-04-05 02:44:21 +00:00
|
|
|
return [
|
|
|
|
|
'host' => $wgDBserver,
|
2021-03-10 01:38:23 +00:00
|
|
|
'serverName' => 'testhost',
|
2018-04-05 02:44:21 +00:00
|
|
|
'dbname' => $wgDBname,
|
|
|
|
|
'tablePrefix' => $this->dbPrefix(),
|
|
|
|
|
'user' => $wgDBuser,
|
|
|
|
|
'password' => $wgDBpassword,
|
|
|
|
|
'type' => $wgDBtype,
|
|
|
|
|
'dbDirectory' => $wgSQLiteDataDir,
|
|
|
|
|
'load' => 0,
|
2019-04-05 04:18:37 +00:00
|
|
|
'flags' => $flags
|
2017-07-20 03:51:54 +00:00
|
|
|
];
|
2018-04-05 02:44:21 +00:00
|
|
|
}
|
|
|
|
|
|
2018-07-05 12:50:57 +00:00
|
|
|
/**
|
2019-06-18 23:57:32 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getConnection()
|
|
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getLocalDomainID()
|
|
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::resolveDomainID()
|
2019-07-03 22:02:41 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::setDomainAliases()
|
2021-02-22 21:06:23 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getClusterName()
|
2018-07-05 12:50:57 +00:00
|
|
|
*/
|
2018-04-05 02:44:21 +00:00
|
|
|
public function testWithoutReplica() {
|
|
|
|
|
global $wgDBname;
|
2017-07-20 03:51:54 +00:00
|
|
|
|
2018-04-20 20:45:49 +00:00
|
|
|
$called = false;
|
2017-07-20 03:51:54 +00:00
|
|
|
$lb = new LoadBalancer( [
|
2019-04-05 04:18:37 +00:00
|
|
|
// Simulate web request with DBO_TRX
|
|
|
|
|
'servers' => [ $this->makeServerConfig( DBO_TRX ) ],
|
rdbms: Consolidate logger channels into one
Notable changes:
* In SqlBagOStuff::getConnectionFromServerInfo, only two loggers were
injected. The rest implicitly got a NullLogger due to being absent.
These are now effectively unsilenced.
* Database::__construct() required almost all parameters, even the
loggers. I've wanted to move some of DatabaseFactory into the ctor
here for a while. In order to make this change not a breaking
change, the new 'logger' parameter is optional with NullLogger as
default. This allowed some of the test cases, which were simply
passing NullLogger, to be fixed by passing nothing instead of
passing the new option name.
The Database class is behind a dozen layers of indirection for
real use, so this will still be injected just fine (DBF, LB, LBF,
MWLBF, etc.).
* In LegacyLogger, the handling for $wgDBerrorLog was previously
limited to DBConnection and DBQuery. This now includes errors
from other (generally, newer) parts of Rdbms as well, which were
previously missing.
This only affects sites (typically CI and dev setup) where
$wgDBerrorLog is used, as opposed to the more common
$wgDebugLogGroups by-channel configuration.
* TransactionProfiler gets its logger injected in a rather odd way,
via entrypoints (MediaWiki.php, ApiMain.php, and MaintenanceRunner)
as opposed to service wiring. This is kept as-is for now.
* In LBFactoryTest, in particular testInvalidSelectDBIndependent2,
there are cases that intentionally produce failures of which
the result is then observed. In CI we assert that dberror.log is
empty so instead of adding the missing logger fields to that
LBFactory instance, the only one set (replLogger) is removed.
The alternative is to set 'logger' now, which would naturally
cause CI failures due to unexpected entries coming through to
non-mocked error log.
Bug: T320873
Change-Id: I7ca996618e41b93f488cb5c4de82000bb36e0dd3
2022-10-15 20:16:07 +00:00
|
|
|
'logger' => MediaWiki\Logger\LoggerFactory::getInstance( 'rdbms' ),
|
2018-04-20 20:45:49 +00:00
|
|
|
'localDomain' => new DatabaseDomain( $wgDBname, null, $this->dbPrefix() ),
|
2021-02-07 13:10:36 +00:00
|
|
|
'chronologyCallback' => static function () use ( &$called ) {
|
2018-04-20 20:45:49 +00:00
|
|
|
$called = true;
|
2021-02-22 21:06:23 +00:00
|
|
|
},
|
|
|
|
|
'clusterName' => 'xyz'
|
2017-07-20 03:51:54 +00:00
|
|
|
] );
|
|
|
|
|
|
2020-05-30 10:36:42 +00:00
|
|
|
$this->assertSame( 1, $lb->getServerCount() );
|
2019-06-18 23:57:32 +00:00
|
|
|
$this->assertFalse( $lb->hasReplicaServers() );
|
|
|
|
|
$this->assertFalse( $lb->hasStreamingReplicaServers() );
|
2021-02-22 21:06:23 +00:00
|
|
|
$this->assertSame( 'xyz', $lb->getClusterName() );
|
2019-06-18 23:57:32 +00:00
|
|
|
|
2018-01-12 23:57:23 +00:00
|
|
|
$ld = DatabaseDomain::newFromId( $lb->getLocalDomainID() );
|
2022-08-24 00:57:22 +00:00
|
|
|
$this->assertSame( $wgDBname, $ld->getDatabase(), 'local domain DB set' );
|
|
|
|
|
$this->assertSame( $this->dbPrefix(), $ld->getTablePrefix(), 'local domain prefix set' );
|
2018-07-05 12:50:57 +00:00
|
|
|
$this->assertSame( 'my_test_wiki', $lb->resolveDomainID( 'my_test_wiki' ) );
|
|
|
|
|
$this->assertSame( $ld->getId(), $lb->resolveDomainID( false ) );
|
|
|
|
|
$this->assertSame( $ld->getId(), $lb->resolveDomainID( $ld ) );
|
2018-04-20 20:45:49 +00:00
|
|
|
$this->assertFalse( $called );
|
2019-04-05 04:18:37 +00:00
|
|
|
|
2021-04-29 02:37:11 +00:00
|
|
|
$dbw = $lb->getConnection( DB_PRIMARY );
|
2022-03-03 12:33:24 +00:00
|
|
|
$dbw->getServerName();
|
2022-08-24 00:57:22 +00:00
|
|
|
$this->assertFalse( $called, "getServerName() optimized for DB_PRIMARY" );
|
|
|
|
|
|
|
|
|
|
$dbw->ensureConnection();
|
2022-11-23 21:36:49 +00:00
|
|
|
$this->assertFalse( $called, "Session replication pos not used with single server" );
|
2022-08-24 00:57:22 +00:00
|
|
|
$this->assertSame(
|
2019-07-14 22:43:26 +00:00
|
|
|
$dbw::ROLE_STREAMING_MASTER, $dbw->getTopologyRole(), 'master shows as master'
|
|
|
|
|
);
|
2017-07-20 03:51:54 +00:00
|
|
|
$this->assertTrue( $dbw->getFlag( $dbw::DBO_TRX ), "DBO_TRX set on master" );
|
2017-12-20 18:37:13 +00:00
|
|
|
$this->assertWriteAllowed( $dbw );
|
2017-07-20 03:51:54 +00:00
|
|
|
|
|
|
|
|
$dbr = $lb->getConnection( DB_REPLICA );
|
2022-08-24 00:57:22 +00:00
|
|
|
$this->assertSame(
|
2019-07-14 22:43:26 +00:00
|
|
|
$dbr::ROLE_STREAMING_MASTER, $dbr->getTopologyRole(), 'DB_REPLICA also gets the master' );
|
2017-12-20 18:37:13 +00:00
|
|
|
$this->assertTrue( $dbr->getFlag( $dbw::DBO_TRX ), "DBO_TRX set on replica" );
|
2017-07-20 03:51:54 +00:00
|
|
|
|
2022-03-03 12:33:24 +00:00
|
|
|
if ( !$lb->getServerAttributes( $lb->getWriterIndex() )[Database::ATTR_DB_LEVEL_LOCKING] ) {
|
2022-08-24 00:57:22 +00:00
|
|
|
$dbwAC1 = $lb->getConnection( DB_PRIMARY, [], false, $lb::CONN_TRX_AUTOCOMMIT );
|
2018-04-13 08:39:22 +00:00
|
|
|
$this->assertFalse(
|
2022-08-24 00:57:22 +00:00
|
|
|
$dbwAC1->getFlag( $dbw::DBO_TRX ),
|
|
|
|
|
"No DBO_TRX with CONN_TRX_AUTOCOMMIT"
|
|
|
|
|
);
|
2018-04-13 08:39:22 +00:00
|
|
|
$this->assertTrue( $dbw->getFlag( $dbw::DBO_TRX ), "DBO_TRX still set on master" );
|
2022-08-24 00:57:22 +00:00
|
|
|
$this->assertUnsharedHandle( $dbw, $dbwAC1, "CONN_TRX_AUTOCOMMIT separate connection" );
|
2018-04-13 08:39:22 +00:00
|
|
|
|
2022-08-24 00:57:22 +00:00
|
|
|
$dbrAC1 = $lb->getConnection( DB_REPLICA, [], false, $lb::CONN_TRX_AUTOCOMMIT );
|
2018-04-13 08:39:22 +00:00
|
|
|
$this->assertFalse(
|
2022-08-24 00:57:22 +00:00
|
|
|
$dbrAC1->getFlag( $dbw::DBO_TRX ),
|
|
|
|
|
"No DBO_TRX with CONN_TRX_AUTOCOMMIT"
|
|
|
|
|
);
|
2018-04-13 08:39:22 +00:00
|
|
|
$this->assertTrue( $dbr->getFlag( $dbw::DBO_TRX ), "DBO_TRX still set on replica" );
|
2022-08-24 00:57:22 +00:00
|
|
|
$this->assertUnsharedHandle( $dbr, $dbrAC1, "CONN_TRX_AUTOCOMMIT separate connection" );
|
2018-04-13 08:39:22 +00:00
|
|
|
|
2022-08-24 00:57:22 +00:00
|
|
|
$dbwAC2 = $lb->getConnection( DB_PRIMARY, [], false, $lb::CONN_TRX_AUTOCOMMIT );
|
|
|
|
|
$dbwAC2->ensureConnection();
|
|
|
|
|
$this->assertSharedHandle( $dbwAC2, $dbwAC1, "CONN_TRX_AUTOCOMMIT reuses connections" );
|
|
|
|
|
|
|
|
|
|
$dbrAC2 = $lb->getConnection( DB_REPLICA, [], false, $lb::CONN_TRX_AUTOCOMMIT );
|
|
|
|
|
$dbrAC2->ensureConnection();
|
|
|
|
|
$this->assertSharedHandle( $dbrAC2, $dbrAC1, "CONN_TRX_AUTOCOMMIT reuses connections" );
|
2018-04-13 08:39:22 +00:00
|
|
|
}
|
2017-07-20 03:51:54 +00:00
|
|
|
|
2022-03-16 19:35:31 +00:00
|
|
|
$lb->closeAll( __METHOD__ );
|
2017-07-20 03:51:54 +00:00
|
|
|
}
|
|
|
|
|
|
2019-06-18 23:57:32 +00:00
|
|
|
/**
|
|
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getConnection()
|
|
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getReaderIndex()
|
|
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getWriterIndex()
|
|
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getServerName()
|
|
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getServerInfo()
|
|
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getServerType()
|
|
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getServerAttributes()
|
2021-02-22 21:06:23 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getClusterName()
|
2019-06-18 23:57:32 +00:00
|
|
|
*/
|
2017-12-20 18:37:13 +00:00
|
|
|
public function testWithReplica() {
|
2019-04-05 04:18:37 +00:00
|
|
|
// Simulate web request with DBO_TRX
|
2019-06-24 23:36:42 +00:00
|
|
|
$lb = $this->newMultiServerLocalLoadBalancer( [], [ 'flags' => DBO_TRX ] );
|
2017-07-20 03:51:54 +00:00
|
|
|
|
2022-08-24 00:57:22 +00:00
|
|
|
$this->assertSame( 8, $lb->getServerCount() );
|
2019-06-18 21:12:06 +00:00
|
|
|
$this->assertTrue( $lb->hasReplicaServers() );
|
|
|
|
|
$this->assertTrue( $lb->hasStreamingReplicaServers() );
|
2021-02-22 21:06:23 +00:00
|
|
|
$this->assertSame( 'main-test-cluster', $lb->getClusterName() );
|
2019-06-18 21:12:06 +00:00
|
|
|
|
2019-06-18 23:57:32 +00:00
|
|
|
for ( $i = 0; $i < $lb->getServerCount(); ++$i ) {
|
2019-12-13 17:44:39 +00:00
|
|
|
$this->assertIsString( $lb->getServerName( $i ) );
|
|
|
|
|
$this->assertIsArray( $lb->getServerInfo( $i ) );
|
|
|
|
|
$this->assertIsString( $lb->getServerType( $i ) );
|
|
|
|
|
$this->assertIsArray( $lb->getServerAttributes( $i ) );
|
2019-06-18 23:57:32 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-29 02:37:11 +00:00
|
|
|
$dbw = $lb->getConnection( DB_PRIMARY );
|
2022-08-24 00:57:22 +00:00
|
|
|
$dbw->ensureConnection();
|
|
|
|
|
$wConn = TestingAccessWrapper::newFromObject( $dbw )->conn;
|
|
|
|
|
$wConnWrap = TestingAccessWrapper::newFromObject( $wConn );
|
|
|
|
|
|
|
|
|
|
$this->assertSame(
|
2021-09-02 18:52:20 +00:00
|
|
|
$dbw::ROLE_STREAMING_MASTER, $dbw->getTopologyRole(), 'primary shows as primary' );
|
|
|
|
|
$this->assertTrue( $dbw->getFlag( $dbw::DBO_TRX ), "DBO_TRX set on primary" );
|
2017-12-20 18:37:13 +00:00
|
|
|
$this->assertWriteAllowed( $dbw );
|
2017-07-20 03:51:54 +00:00
|
|
|
|
|
|
|
|
$dbr = $lb->getConnection( DB_REPLICA );
|
2022-08-24 00:57:22 +00:00
|
|
|
$dbr->ensureConnection();
|
|
|
|
|
$rConn = TestingAccessWrapper::newFromObject( $dbr )->conn;
|
|
|
|
|
$rConnWrap = TestingAccessWrapper::newFromObject( $rConn );
|
|
|
|
|
|
|
|
|
|
$this->assertSame(
|
2019-07-14 22:43:26 +00:00
|
|
|
$dbr::ROLE_STREAMING_REPLICA, $dbr->getTopologyRole(), 'replica shows as replica' );
|
2019-07-10 06:51:24 +00:00
|
|
|
$this->assertTrue( $dbr->isReadOnly(), 'replica shows as replica' );
|
2017-12-20 18:37:13 +00:00
|
|
|
$this->assertTrue( $dbr->getFlag( $dbw::DBO_TRX ), "DBO_TRX set on replica" );
|
2022-08-24 00:57:22 +00:00
|
|
|
$this->assertSame( $dbr->getLBInfo( 'serverIndex' ), $lb->getReaderIndex() );
|
2017-07-20 03:51:54 +00:00
|
|
|
|
2022-03-03 12:33:24 +00:00
|
|
|
if ( !$lb->getServerAttributes( $lb->getWriterIndex() )[Database::ATTR_DB_LEVEL_LOCKING] ) {
|
2022-08-24 00:57:22 +00:00
|
|
|
$dbwAC1 = $lb->getConnection( DB_PRIMARY, [], false, $lb::CONN_TRX_AUTOCOMMIT );
|
2018-04-13 08:39:22 +00:00
|
|
|
$this->assertFalse(
|
2022-08-24 00:57:22 +00:00
|
|
|
$dbwAC1->getFlag( $dbw::DBO_TRX ),
|
|
|
|
|
"No DBO_TRX with CONN_TRX_AUTOCOMMIT"
|
|
|
|
|
);
|
2018-04-13 08:39:22 +00:00
|
|
|
$this->assertTrue( $dbw->getFlag( $dbw::DBO_TRX ), "DBO_TRX still set on master" );
|
2022-08-24 00:57:22 +00:00
|
|
|
$this->assertUnsharedHandle( $dbw, $dbwAC1, "CONN_TRX_AUTOCOMMIT separate connection" );
|
2018-04-13 08:39:22 +00:00
|
|
|
|
2022-08-24 00:57:22 +00:00
|
|
|
$dbrAC1 = $lb->getConnection( DB_REPLICA, [], false, $lb::CONN_TRX_AUTOCOMMIT );
|
2018-04-13 08:39:22 +00:00
|
|
|
$this->assertFalse(
|
2022-08-24 00:57:22 +00:00
|
|
|
$dbrAC1->getFlag( $dbw::DBO_TRX ),
|
|
|
|
|
"No DBO_TRX with CONN_TRX_AUTOCOMMIT"
|
|
|
|
|
);
|
2018-04-13 08:39:22 +00:00
|
|
|
$this->assertTrue( $dbr->getFlag( $dbw::DBO_TRX ), "DBO_TRX still set on replica" );
|
2022-08-24 00:57:22 +00:00
|
|
|
$this->assertUnsharedHandle( $dbr, $dbrAC1, "CONN_TRX_AUTOCOMMIT separate connection" );
|
|
|
|
|
|
|
|
|
|
$dbwAC2 = $lb->getConnection( DB_PRIMARY, [], false, $lb::CONN_TRX_AUTOCOMMIT );
|
|
|
|
|
$dbwAC2->ensureConnection();
|
|
|
|
|
$this->assertSharedHandle( $dbwAC2, $dbwAC1, "CONN_TRX_AUTOCOMMIT reuses connections" );
|
2018-04-13 08:39:22 +00:00
|
|
|
|
2022-08-24 00:57:22 +00:00
|
|
|
$dbrAC2 = $lb->getConnection( DB_REPLICA, [], false, $lb::CONN_TRX_AUTOCOMMIT );
|
|
|
|
|
$dbrAC2->ensureConnection();
|
|
|
|
|
$this->assertSharedHandle( $dbrAC2, $dbrAC1, "CONN_TRX_AUTOCOMMIT reuses connections" );
|
2018-04-13 08:39:22 +00:00
|
|
|
}
|
2017-07-20 03:51:54 +00:00
|
|
|
|
2022-03-16 19:35:31 +00:00
|
|
|
$lb->closeAll( __METHOD__ );
|
2017-07-20 03:51:54 +00:00
|
|
|
}
|
2017-12-20 18:37:13 +00:00
|
|
|
|
2022-08-24 00:57:22 +00:00
|
|
|
private function assertSharedHandle( DBConnRef $connRef1, DBConnRef $connRef2, $msg ) {
|
|
|
|
|
$connRef1Wrap = TestingAccessWrapper::newFromObject( $connRef1 );
|
|
|
|
|
$connRef2Wrap = TestingAccessWrapper::newFromObject( $connRef2 );
|
|
|
|
|
$this->assertSame( $connRef1Wrap->conn, $connRef2Wrap->conn, $msg );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function assertUnsharedHandle( DBConnRef $connRef1, DBConnRef $connRef2, $msg ) {
|
|
|
|
|
$connRef1Wrap = TestingAccessWrapper::newFromObject( $connRef1 );
|
|
|
|
|
$connRef2Wrap = TestingAccessWrapper::newFromObject( $connRef2 );
|
|
|
|
|
$this->assertNotSame( $connRef1Wrap->conn, $connRef2Wrap->conn, $msg );
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-05 04:18:37 +00:00
|
|
|
private function newSingleServerLocalLoadBalancer() {
|
|
|
|
|
global $wgDBname;
|
|
|
|
|
|
|
|
|
|
return new LoadBalancer( [
|
|
|
|
|
'servers' => [ $this->makeServerConfig() ],
|
2023-01-07 00:33:02 +00:00
|
|
|
'localDomain' => new DatabaseDomain( $wgDBname, null, $this->dbPrefix() ),
|
|
|
|
|
'cliMode' => false
|
2019-04-05 04:18:37 +00:00
|
|
|
] );
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-27 18:52:04 +00:00
|
|
|
private function newMultiServerLocalLoadBalancer(
|
|
|
|
|
$lbExtra = [], $srvExtra = [], $masterOnly = false
|
|
|
|
|
) {
|
2019-04-05 04:18:37 +00:00
|
|
|
global $wgDBserver, $wgDBname, $wgDBuser, $wgDBpassword, $wgDBtype, $wgSQLiteDataDir;
|
|
|
|
|
|
|
|
|
|
$servers = [
|
2021-05-14 20:04:02 +00:00
|
|
|
// Primary DB
|
2019-06-24 23:36:42 +00:00
|
|
|
0 => $srvExtra + [
|
2023-01-03 20:52:10 +00:00
|
|
|
'serverName' => 'db0',
|
2021-02-22 21:06:23 +00:00
|
|
|
'host' => $wgDBserver,
|
|
|
|
|
'dbname' => $wgDBname,
|
|
|
|
|
'tablePrefix' => $this->dbPrefix(),
|
|
|
|
|
'user' => $wgDBuser,
|
|
|
|
|
'password' => $wgDBpassword,
|
|
|
|
|
'type' => $wgDBtype,
|
|
|
|
|
'dbDirectory' => $wgSQLiteDataDir,
|
|
|
|
|
'load' => $masterOnly ? 100 : 0,
|
|
|
|
|
],
|
2019-05-08 19:18:01 +00:00
|
|
|
// Main replica DBs
|
2019-06-24 23:36:42 +00:00
|
|
|
1 => $srvExtra + [
|
2023-01-03 20:52:10 +00:00
|
|
|
'serverName' => 'db1',
|
2021-02-22 21:06:23 +00:00
|
|
|
'host' => $wgDBserver,
|
|
|
|
|
'dbname' => $wgDBname,
|
|
|
|
|
'tablePrefix' => $this->dbPrefix(),
|
|
|
|
|
'user' => $wgDBuser,
|
|
|
|
|
'password' => $wgDBpassword,
|
|
|
|
|
'type' => $wgDBtype,
|
|
|
|
|
'dbDirectory' => $wgSQLiteDataDir,
|
|
|
|
|
'load' => $masterOnly ? 0 : 100,
|
|
|
|
|
],
|
2019-06-24 23:36:42 +00:00
|
|
|
2 => $srvExtra + [
|
2023-01-03 20:52:10 +00:00
|
|
|
'serverName' => 'db2',
|
2021-02-22 21:06:23 +00:00
|
|
|
'host' => $wgDBserver,
|
|
|
|
|
'dbname' => $wgDBname,
|
|
|
|
|
'tablePrefix' => $this->dbPrefix(),
|
|
|
|
|
'user' => $wgDBuser,
|
|
|
|
|
'password' => $wgDBpassword,
|
|
|
|
|
'type' => $wgDBtype,
|
|
|
|
|
'dbDirectory' => $wgSQLiteDataDir,
|
|
|
|
|
'load' => $masterOnly ? 0 : 100,
|
|
|
|
|
],
|
2019-05-08 19:18:01 +00:00
|
|
|
// RC replica DBs
|
2019-06-24 23:36:42 +00:00
|
|
|
3 => $srvExtra + [
|
2023-01-03 20:52:10 +00:00
|
|
|
'serverName' => 'db3',
|
2021-02-22 21:06:23 +00:00
|
|
|
'host' => $wgDBserver,
|
|
|
|
|
'dbname' => $wgDBname,
|
|
|
|
|
'tablePrefix' => $this->dbPrefix(),
|
|
|
|
|
'user' => $wgDBuser,
|
|
|
|
|
'password' => $wgDBpassword,
|
|
|
|
|
'type' => $wgDBtype,
|
|
|
|
|
'dbDirectory' => $wgSQLiteDataDir,
|
|
|
|
|
'load' => 0,
|
|
|
|
|
'groupLoads' => [
|
2022-08-16 18:33:43 +00:00
|
|
|
'foo' => 100,
|
|
|
|
|
'bar' => 100
|
2021-02-22 21:06:23 +00:00
|
|
|
],
|
2019-05-08 19:18:01 +00:00
|
|
|
],
|
|
|
|
|
// Logging replica DBs
|
2019-06-24 23:36:42 +00:00
|
|
|
4 => $srvExtra + [
|
2023-01-03 20:52:10 +00:00
|
|
|
'serverName' => 'db4',
|
2021-02-22 21:06:23 +00:00
|
|
|
'host' => $wgDBserver,
|
|
|
|
|
'dbname' => $wgDBname,
|
|
|
|
|
'tablePrefix' => $this->dbPrefix(),
|
|
|
|
|
'user' => $wgDBuser,
|
|
|
|
|
'password' => $wgDBpassword,
|
|
|
|
|
'type' => $wgDBtype,
|
|
|
|
|
'dbDirectory' => $wgSQLiteDataDir,
|
|
|
|
|
'load' => 0,
|
|
|
|
|
'groupLoads' => [
|
2022-08-16 18:33:43 +00:00
|
|
|
'baz' => 100
|
2021-02-22 21:06:23 +00:00
|
|
|
],
|
2019-05-08 19:18:01 +00:00
|
|
|
],
|
2019-06-24 23:36:42 +00:00
|
|
|
5 => $srvExtra + [
|
2023-01-03 20:52:10 +00:00
|
|
|
'serverName' => 'db5',
|
2021-02-22 21:06:23 +00:00
|
|
|
'host' => $wgDBserver,
|
|
|
|
|
'dbname' => $wgDBname,
|
|
|
|
|
'tablePrefix' => $this->dbPrefix(),
|
|
|
|
|
'user' => $wgDBuser,
|
|
|
|
|
'password' => $wgDBpassword,
|
|
|
|
|
'type' => $wgDBtype,
|
|
|
|
|
'dbDirectory' => $wgSQLiteDataDir,
|
|
|
|
|
'load' => 0,
|
|
|
|
|
'groupLoads' => [
|
2022-08-16 18:33:43 +00:00
|
|
|
'baz' => 100
|
2021-02-22 21:06:23 +00:00
|
|
|
],
|
2019-05-08 19:18:01 +00:00
|
|
|
],
|
|
|
|
|
// Maintenance query replica DBs
|
2019-06-24 23:36:42 +00:00
|
|
|
6 => $srvExtra + [
|
2023-01-03 20:52:10 +00:00
|
|
|
'serverName' => 'db6',
|
2021-02-22 21:06:23 +00:00
|
|
|
'host' => $wgDBserver,
|
|
|
|
|
'dbname' => $wgDBname,
|
|
|
|
|
'tablePrefix' => $this->dbPrefix(),
|
|
|
|
|
'user' => $wgDBuser,
|
|
|
|
|
'password' => $wgDBpassword,
|
|
|
|
|
'type' => $wgDBtype,
|
|
|
|
|
'dbDirectory' => $wgSQLiteDataDir,
|
|
|
|
|
'load' => 0,
|
|
|
|
|
'groupLoads' => [
|
|
|
|
|
'vslow' => 100
|
|
|
|
|
],
|
2019-05-08 19:18:01 +00:00
|
|
|
],
|
2019-06-18 21:12:06 +00:00
|
|
|
// Replica DB that only has a copy of some static tables
|
2019-06-24 23:36:42 +00:00
|
|
|
7 => $srvExtra + [
|
2023-01-03 20:52:10 +00:00
|
|
|
'serverName' => 'db7',
|
2021-02-22 21:06:23 +00:00
|
|
|
'host' => $wgDBserver,
|
|
|
|
|
'dbname' => $wgDBname,
|
|
|
|
|
'tablePrefix' => $this->dbPrefix(),
|
|
|
|
|
'user' => $wgDBuser,
|
|
|
|
|
'password' => $wgDBpassword,
|
|
|
|
|
'type' => $wgDBtype,
|
|
|
|
|
'dbDirectory' => $wgSQLiteDataDir,
|
|
|
|
|
'load' => 0,
|
|
|
|
|
'groupLoads' => [
|
|
|
|
|
'archive' => 100
|
|
|
|
|
],
|
|
|
|
|
'is static' => true
|
|
|
|
|
]
|
2019-04-05 04:18:37 +00:00
|
|
|
];
|
|
|
|
|
|
2019-06-24 23:36:42 +00:00
|
|
|
return new LoadBalancer( $lbExtra + [
|
2019-04-05 04:18:37 +00:00
|
|
|
'servers' => $servers,
|
|
|
|
|
'localDomain' => new DatabaseDomain( $wgDBname, null, $this->dbPrefix() ),
|
rdbms: Consolidate logger channels into one
Notable changes:
* In SqlBagOStuff::getConnectionFromServerInfo, only two loggers were
injected. The rest implicitly got a NullLogger due to being absent.
These are now effectively unsilenced.
* Database::__construct() required almost all parameters, even the
loggers. I've wanted to move some of DatabaseFactory into the ctor
here for a while. In order to make this change not a breaking
change, the new 'logger' parameter is optional with NullLogger as
default. This allowed some of the test cases, which were simply
passing NullLogger, to be fixed by passing nothing instead of
passing the new option name.
The Database class is behind a dozen layers of indirection for
real use, so this will still be injected just fine (DBF, LB, LBF,
MWLBF, etc.).
* In LegacyLogger, the handling for $wgDBerrorLog was previously
limited to DBConnection and DBQuery. This now includes errors
from other (generally, newer) parts of Rdbms as well, which were
previously missing.
This only affects sites (typically CI and dev setup) where
$wgDBerrorLog is used, as opposed to the more common
$wgDebugLogGroups by-channel configuration.
* TransactionProfiler gets its logger injected in a rather odd way,
via entrypoints (MediaWiki.php, ApiMain.php, and MaintenanceRunner)
as opposed to service wiring. This is kept as-is for now.
* In LBFactoryTest, in particular testInvalidSelectDBIndependent2,
there are cases that intentionally produce failures of which
the result is then observed. In CI we assert that dberror.log is
empty so instead of adding the missing logger fields to that
LBFactory instance, the only one set (replLogger) is removed.
The alternative is to set 'logger' now, which would naturally
cause CI failures due to unexpected entries coming through to
non-mocked error log.
Bug: T320873
Change-Id: I7ca996618e41b93f488cb5c4de82000bb36e0dd3
2022-10-15 20:16:07 +00:00
|
|
|
'logger' => MediaWiki\Logger\LoggerFactory::getInstance( 'rdbms' ),
|
2021-02-22 21:06:23 +00:00
|
|
|
'loadMonitor' => [ 'class' => LoadMonitorNull::class ],
|
|
|
|
|
'clusterName' => 'main-test-cluster'
|
2019-04-05 04:18:37 +00:00
|
|
|
] );
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-12 23:57:23 +00:00
|
|
|
private function assertWriteForbidden( Database $db ) {
|
2017-12-20 18:37:13 +00:00
|
|
|
try {
|
2018-01-12 23:57:23 +00:00
|
|
|
$db->delete( 'some_table', [ 'id' => 57634126 ], __METHOD__ );
|
2017-12-20 18:37:13 +00:00
|
|
|
$this->fail( 'Write operation should have failed!' );
|
|
|
|
|
} catch ( DBError $ex ) {
|
|
|
|
|
// check that the exception message contains "Write operation"
|
2019-10-06 04:54:59 +00:00
|
|
|
$constraint = new StringContains( 'Write operation' );
|
2017-12-20 18:37:13 +00:00
|
|
|
|
2017-12-21 11:59:53 +00:00
|
|
|
if ( !$constraint->evaluate( $ex->getMessage(), '', true ) ) {
|
2017-12-20 18:37:13 +00:00
|
|
|
// re-throw original error, to preserve stack trace
|
|
|
|
|
throw $ex;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-03 12:33:24 +00:00
|
|
|
private function assertWriteAllowed( IMaintainableDatabase $db ) {
|
2018-01-12 23:57:23 +00:00
|
|
|
$table = $db->tableName( 'some_table' );
|
2018-06-06 21:38:47 +00:00
|
|
|
// Trigger a transaction so that rollback() will remove all the tables.
|
2019-08-14 19:47:06 +00:00
|
|
|
// Don't do this for MySQL as it auto-commits transactions for DDL
|
2018-06-06 21:38:47 +00:00
|
|
|
// statements such as CREATE TABLE.
|
2019-08-14 15:04:50 +00:00
|
|
|
$useAtomicSection = in_array( $db->getType(), [ 'sqlite', 'postgres' ], true );
|
2022-03-03 12:33:24 +00:00
|
|
|
/** @var Database $db */
|
2017-12-20 18:37:13 +00:00
|
|
|
try {
|
2021-11-21 16:23:11 +00:00
|
|
|
$db->dropTable( 'some_table' );
|
2022-01-28 22:04:40 +00:00
|
|
|
$this->assertNotEquals( TransactionManager::STATUS_TRX_ERROR, $db->trxStatus() );
|
2018-04-13 17:32:48 +00:00
|
|
|
|
2018-06-06 21:38:47 +00:00
|
|
|
if ( $useAtomicSection ) {
|
|
|
|
|
$db->startAtomic( __METHOD__ );
|
|
|
|
|
}
|
2018-01-12 23:57:23 +00:00
|
|
|
// Use only basic SQL and trivial types for these queries for compatibility
|
2020-10-28 19:57:14 +00:00
|
|
|
$this->assertNotFalse(
|
2018-01-12 23:57:23 +00:00
|
|
|
$db->query( "CREATE TABLE $table (id INT, time INT)", __METHOD__ ),
|
|
|
|
|
"table created"
|
|
|
|
|
);
|
2022-01-28 22:04:40 +00:00
|
|
|
$this->assertNotEquals( TransactionManager::STATUS_TRX_ERROR, $db->trxStatus() );
|
2020-10-28 19:57:14 +00:00
|
|
|
$this->assertNotFalse(
|
2018-01-12 23:57:23 +00:00
|
|
|
$db->query( "DELETE FROM $table WHERE id=57634126", __METHOD__ ),
|
|
|
|
|
"delete query"
|
|
|
|
|
);
|
2022-01-28 22:04:40 +00:00
|
|
|
$this->assertNotEquals( TransactionManager::STATUS_TRX_ERROR, $db->trxStatus() );
|
2017-12-20 18:37:13 +00:00
|
|
|
} finally {
|
2018-06-06 21:38:47 +00:00
|
|
|
if ( !$useAtomicSection ) {
|
|
|
|
|
// Drop the table to clean up, ignoring any error.
|
|
|
|
|
$db->dropTable( 'some_table' );
|
|
|
|
|
}
|
|
|
|
|
// Rollback the atomic section for sqlite's benefit.
|
2017-12-20 18:37:13 +00:00
|
|
|
$db->rollback( __METHOD__, 'flush' );
|
2022-01-28 22:04:40 +00:00
|
|
|
$this->assertNotEquals( TransactionManager::STATUS_TRX_ERROR, $db->trxStatus() );
|
2017-12-20 18:37:13 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-03 22:02:41 +00:00
|
|
|
/**
|
|
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getServerAttributes
|
|
|
|
|
*/
|
2018-02-28 00:00:05 +00:00
|
|
|
public function testServerAttributes() {
|
|
|
|
|
$servers = [
|
|
|
|
|
[ // master
|
2021-02-22 21:06:23 +00:00
|
|
|
'dbname' => 'my_unittest_wiki',
|
2022-03-17 05:14:08 +00:00
|
|
|
'tablePrefix' => self::DB_PREFIX,
|
2021-02-22 21:06:23 +00:00
|
|
|
'type' => 'sqlite',
|
2018-02-28 00:00:05 +00:00
|
|
|
'dbDirectory' => "some_directory",
|
2021-02-22 21:06:23 +00:00
|
|
|
'load' => 0
|
2018-02-28 00:00:05 +00:00
|
|
|
]
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$lb = new LoadBalancer( [
|
|
|
|
|
'servers' => $servers,
|
2022-03-17 05:14:08 +00:00
|
|
|
'localDomain' => new DatabaseDomain( 'my_unittest_wiki', null, self::DB_PREFIX ),
|
2020-03-28 03:18:05 +00:00
|
|
|
'loadMonitor' => [ 'class' => LoadMonitorNull::class ]
|
2018-02-28 00:00:05 +00:00
|
|
|
] );
|
|
|
|
|
|
|
|
|
|
$this->assertTrue( $lb->getServerAttributes( 0 )[Database::ATTR_DB_LEVEL_LOCKING] );
|
|
|
|
|
|
|
|
|
|
$servers = [
|
|
|
|
|
[ // master
|
2021-02-22 21:06:23 +00:00
|
|
|
'host' => 'db1001',
|
|
|
|
|
'user' => 'wikiuser',
|
|
|
|
|
'password' => 'none',
|
|
|
|
|
'dbname' => 'my_unittest_wiki',
|
2022-03-17 05:14:08 +00:00
|
|
|
'tablePrefix' => self::DB_PREFIX,
|
2021-02-22 21:06:23 +00:00
|
|
|
'type' => 'mysql',
|
|
|
|
|
'load' => 100
|
2018-02-28 00:00:05 +00:00
|
|
|
],
|
|
|
|
|
[ // emulated replica
|
2021-02-22 21:06:23 +00:00
|
|
|
'host' => 'db1002',
|
|
|
|
|
'user' => 'wikiuser',
|
|
|
|
|
'password' => 'none',
|
|
|
|
|
'dbname' => 'my_unittest_wiki',
|
2022-03-17 05:14:08 +00:00
|
|
|
'tablePrefix' => self::DB_PREFIX,
|
2021-02-22 21:06:23 +00:00
|
|
|
'type' => 'mysql',
|
|
|
|
|
'load' => 100
|
2018-02-28 00:00:05 +00:00
|
|
|
]
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$lb = new LoadBalancer( [
|
|
|
|
|
'servers' => $servers,
|
2022-03-17 05:14:08 +00:00
|
|
|
'localDomain' => new DatabaseDomain( 'my_unittest_wiki', null, self::DB_PREFIX ),
|
2020-03-28 03:18:05 +00:00
|
|
|
'loadMonitor' => [ 'class' => LoadMonitorNull::class ]
|
2018-02-28 00:00:05 +00:00
|
|
|
] );
|
|
|
|
|
|
|
|
|
|
$this->assertFalse( $lb->getServerAttributes( 1 )[Database::ATTR_DB_LEVEL_LOCKING] );
|
|
|
|
|
}
|
2018-04-05 02:44:21 +00:00
|
|
|
|
|
|
|
|
/**
|
2022-03-03 12:33:24 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getConnectionInternal()
|
2019-06-18 23:57:32 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getWriterIndex()
|
2018-04-05 02:44:21 +00:00
|
|
|
*/
|
2019-10-09 18:24:07 +00:00
|
|
|
public function testOpenConnection() {
|
2019-04-05 04:18:37 +00:00
|
|
|
$lb = $this->newSingleServerLocalLoadBalancer();
|
2018-04-05 02:44:21 +00:00
|
|
|
$i = $lb->getWriterIndex();
|
2019-03-21 16:10:23 +00:00
|
|
|
|
2023-01-07 00:33:02 +00:00
|
|
|
$this->assertFalse( $lb->getAnyOpenConnection( $i ) );
|
|
|
|
|
$this->assertFalse( $lb->getAnyOpenConnection( $i, $lb::CONN_TRX_AUTOCOMMIT ) );
|
|
|
|
|
|
|
|
|
|
// Get two live round-aware handles
|
|
|
|
|
$raConnRef1 = $lb->getConnection( $i );
|
|
|
|
|
$raConnRef1->ensureConnection();
|
|
|
|
|
$raConnRef1Wrapper = TestingAccessWrapper::newFromObject( $raConnRef1 );
|
|
|
|
|
$raConnRef2 = $lb->getConnection( $i );
|
|
|
|
|
$raConnRef2->ensureConnection();
|
|
|
|
|
$raConnRef2Wrapper = TestingAccessWrapper::newFromObject( $raConnRef2 );
|
|
|
|
|
|
|
|
|
|
$this->assertNotNull( $raConnRef1Wrapper->conn );
|
|
|
|
|
$this->assertSame( $raConnRef1Wrapper->conn, $raConnRef2Wrapper->conn );
|
|
|
|
|
$this->assertTrue( $raConnRef1Wrapper->conn->getFlag( DBO_TRX ) );
|
|
|
|
|
|
|
|
|
|
// Get two live autocommit handles
|
|
|
|
|
$acConnRef1 = $lb->getConnection( $i, [], false, $lb::CONN_TRX_AUTOCOMMIT );
|
|
|
|
|
$acConnRef1->ensureConnection();
|
|
|
|
|
$acConnRef1Wrapper = TestingAccessWrapper::newFromObject( $acConnRef1 );
|
|
|
|
|
$acConnRef2 = $lb->getConnection( $i, [], false, $lb::CONN_TRX_AUTOCOMMIT );
|
|
|
|
|
$acConnRef2->ensureConnection();
|
|
|
|
|
$acConnRef2Wrapper = TestingAccessWrapper::newFromObject( $acConnRef2 );
|
|
|
|
|
|
|
|
|
|
$this->assertNotNull( $acConnRef1Wrapper->conn );
|
|
|
|
|
$this->assertSame( $acConnRef1Wrapper->conn, $acConnRef2Wrapper->conn );
|
|
|
|
|
|
|
|
|
|
// Vary expected behaviour by dbtype, SQLite uses ATTR_DB_LEVEL_LOCKING.
|
|
|
|
|
// getConnection() ignores CONN_TRX_AUTOCOMMIT if ATTR_DB_LEVEL_LOCKING if set,
|
|
|
|
|
// meaning that external callers don't have to check. This is not the case for
|
|
|
|
|
// the internal getServerConnection()/getAnyOpenConnection() methods.
|
2018-04-05 02:44:21 +00:00
|
|
|
if ( $lb->getServerAttributes( $i )[Database::ATTR_DB_LEVEL_LOCKING] ) {
|
2023-01-07 00:33:02 +00:00
|
|
|
$this->assertSame( $raConnRef1Wrapper->conn, $acConnRef1Wrapper->conn );
|
|
|
|
|
$this->assertFalse( $lb->getAnyOpenConnection( $i, $lb::CONN_TRX_AUTOCOMMIT ) );
|
|
|
|
|
} else {
|
|
|
|
|
$this->assertFalse( $acConnRef1Wrapper->conn->getFlag( DBO_TRX ) );
|
|
|
|
|
$this->assertNotFalse( $lb->getAnyOpenConnection( $i, $lb::CONN_TRX_AUTOCOMMIT ) );
|
2018-04-05 02:44:21 +00:00
|
|
|
}
|
|
|
|
|
|
2023-01-07 00:33:02 +00:00
|
|
|
$this->assertNotFalse( $lb->getAnyOpenConnection( $i ) );
|
|
|
|
|
|
2022-03-16 19:35:31 +00:00
|
|
|
$lb->closeAll( __METHOD__ );
|
2018-04-05 02:44:21 +00:00
|
|
|
}
|
2018-03-28 20:01:32 +00:00
|
|
|
|
2022-05-24 13:50:02 +00:00
|
|
|
public function testReconfigure() {
|
2022-08-31 16:21:40 +00:00
|
|
|
$serverA = $this->makeServerConfig();
|
|
|
|
|
$serverA['serverName'] = 'test_one';
|
|
|
|
|
|
|
|
|
|
$serverB = $this->makeServerConfig();
|
|
|
|
|
$serverB['serverName'] = 'test_two';
|
2022-05-24 13:50:02 +00:00
|
|
|
$conf = [
|
2022-08-31 16:21:40 +00:00
|
|
|
'servers' => [ $serverA, $serverB ],
|
2022-05-24 13:50:02 +00:00
|
|
|
'clusterName' => 'A',
|
|
|
|
|
'localDomain' => $this->db->getDomainID()
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$lb = new LoadBalancer( $conf );
|
2022-08-31 16:21:40 +00:00
|
|
|
$this->assertSame( 2, $lb->getServerCount() );
|
2022-05-24 13:50:02 +00:00
|
|
|
|
|
|
|
|
$con = $lb->getConnectionInternal( DB_PRIMARY );
|
|
|
|
|
$ref = $lb->getConnection( DB_PRIMARY );
|
|
|
|
|
|
|
|
|
|
$this->assertTrue( $con->isOpen() );
|
|
|
|
|
$this->assertTrue( $ref->isOpen() );
|
|
|
|
|
|
2022-08-31 16:21:40 +00:00
|
|
|
// Depool the second server
|
|
|
|
|
$conf['servers'] = [ $serverA ];
|
2022-05-24 13:50:02 +00:00
|
|
|
$lb->reconfigure( $conf );
|
2022-08-31 16:21:40 +00:00
|
|
|
$this->assertSame( 1, $lb->getServerCount() );
|
2022-05-24 13:50:02 +00:00
|
|
|
|
|
|
|
|
// Reconfiguring should not close connections immediately.
|
|
|
|
|
$this->assertTrue( $con->isOpen() );
|
|
|
|
|
|
|
|
|
|
// Connection refs should detect the config change, close the old connection,
|
|
|
|
|
// and get a new connection.
|
|
|
|
|
$this->assertTrue( $ref->isOpen() );
|
|
|
|
|
|
|
|
|
|
// The old connection should have been called by DBConnRef.
|
|
|
|
|
$this->assertFalse( $con->isOpen() );
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-18 23:57:32 +00:00
|
|
|
/**
|
|
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getWriterIndex()
|
2022-04-26 03:54:59 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getOpenPrimaryConnections()
|
2019-06-18 23:57:32 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::setTransactionListener()
|
2021-05-14 22:16:39 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::beginPrimaryChanges()
|
2021-05-14 22:05:55 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::finalizePrimaryChanges()
|
2021-09-02 22:45:52 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::approvePrimaryChanges()
|
2021-05-14 22:22:15 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::commitPrimaryChanges()
|
2022-03-18 00:08:39 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::flushPrimarySessions()
|
2021-09-02 19:04:20 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::runPrimaryTransactionIdleCallbacks()
|
2021-09-02 19:07:38 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::runPrimaryTransactionListenerCallbacks()
|
2019-06-18 23:57:32 +00:00
|
|
|
*/
|
2018-03-28 20:01:32 +00:00
|
|
|
public function testTransactionCallbackChains() {
|
|
|
|
|
global $wgDBserver, $wgDBname, $wgDBuser, $wgDBpassword, $wgDBtype, $wgSQLiteDataDir;
|
|
|
|
|
|
|
|
|
|
$servers = [
|
|
|
|
|
[
|
|
|
|
|
'host' => $wgDBserver,
|
|
|
|
|
'dbname' => $wgDBname,
|
|
|
|
|
'tablePrefix' => $this->dbPrefix(),
|
|
|
|
|
'user' => $wgDBuser,
|
|
|
|
|
'password' => $wgDBpassword,
|
|
|
|
|
'type' => $wgDBtype,
|
|
|
|
|
'dbDirectory' => $wgSQLiteDataDir,
|
|
|
|
|
'load' => 0,
|
2019-03-21 16:10:23 +00:00
|
|
|
'flags' => DBO_TRX // simulate a web request with DBO_TRX
|
2018-03-28 20:01:32 +00:00
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$lb = new LoadBalancer( [
|
|
|
|
|
'servers' => $servers,
|
|
|
|
|
'localDomain' => new DatabaseDomain( $wgDBname, null, $this->dbPrefix() )
|
|
|
|
|
] );
|
2022-08-24 00:57:22 +00:00
|
|
|
/** @var LoadBalancer $lbWrapper */
|
|
|
|
|
$lbWrapper = TestingAccessWrapper::newFromObject( $lb );
|
2018-03-28 20:01:32 +00:00
|
|
|
|
2022-02-09 23:12:06 +00:00
|
|
|
$conn1 = $lb->getConnection( $lb->getWriterIndex(), [], false );
|
2022-08-24 00:57:22 +00:00
|
|
|
$count = iterator_count( $lbWrapper->getOpenPrimaryConnections() );
|
|
|
|
|
$this->assertSame( 0, $count, 'Connection handle count' );
|
2022-03-03 12:33:24 +00:00
|
|
|
$conn1->getServerName();
|
2022-08-24 00:57:22 +00:00
|
|
|
$count = iterator_count( $lbWrapper->getOpenPrimaryConnections() );
|
|
|
|
|
$this->assertSame( 0, $count, 'Connection handle count' );
|
|
|
|
|
$conn1->ensureConnection();
|
|
|
|
|
|
2022-02-09 23:12:06 +00:00
|
|
|
$conn2 = $lb->getConnection( $lb->getWriterIndex(), [], '' );
|
2022-08-24 00:57:22 +00:00
|
|
|
$count = iterator_count( $lbWrapper->getOpenPrimaryConnections() );
|
|
|
|
|
$this->assertSame( 1, $count, 'Connection handle count' );
|
2022-03-03 12:33:24 +00:00
|
|
|
$conn2->getServerName();
|
2022-08-24 00:57:22 +00:00
|
|
|
$count = iterator_count( $lbWrapper->getOpenPrimaryConnections() );
|
|
|
|
|
$this->assertSame( 1, $count, 'Connection handle count' );
|
|
|
|
|
$conn2->ensureConnection();
|
2018-03-28 20:01:32 +00:00
|
|
|
|
2022-04-26 03:54:59 +00:00
|
|
|
$count = iterator_count( $lbWrapper->getOpenPrimaryConnections() );
|
2019-06-25 19:15:07 +00:00
|
|
|
$this->assertSame( 1, $count, 'Connection handle count' );
|
2018-03-28 20:01:32 +00:00
|
|
|
|
|
|
|
|
$tlCalls = 0;
|
2021-02-07 13:10:36 +00:00
|
|
|
$lb->setTransactionListener( 'test-listener', static function () use ( &$tlCalls ) {
|
2018-03-28 20:01:32 +00:00
|
|
|
++$tlCalls;
|
|
|
|
|
} );
|
|
|
|
|
|
2021-05-14 22:16:39 +00:00
|
|
|
$lb->beginPrimaryChanges( __METHOD__ );
|
2018-03-28 20:01:32 +00:00
|
|
|
$bc = array_fill_keys( [ 'a', 'b', 'c', 'd' ], 0 );
|
2021-02-07 13:10:36 +00:00
|
|
|
$conn1->onTransactionPreCommitOrIdle( static function () use ( &$bc, $conn1, $conn2 ) {
|
2018-03-28 20:01:32 +00:00
|
|
|
$bc['a'] = 1;
|
2021-07-13 19:43:07 +00:00
|
|
|
$conn2->onTransactionPreCommitOrIdle( static function () use ( &$bc, $conn1 ) {
|
2018-03-28 20:01:32 +00:00
|
|
|
$bc['b'] = 1;
|
2021-07-13 19:43:07 +00:00
|
|
|
$conn1->onTransactionPreCommitOrIdle( static function () use ( &$bc, $conn1 ) {
|
2018-03-28 20:01:32 +00:00
|
|
|
$bc['c'] = 1;
|
2021-07-13 19:43:07 +00:00
|
|
|
$conn1->onTransactionPreCommitOrIdle( static function () use ( &$bc ) {
|
2018-03-28 20:01:32 +00:00
|
|
|
$bc['d'] = 1;
|
|
|
|
|
} );
|
|
|
|
|
} );
|
|
|
|
|
} );
|
|
|
|
|
} );
|
2021-05-14 22:05:55 +00:00
|
|
|
$lb->finalizePrimaryChanges();
|
2021-09-02 22:45:52 +00:00
|
|
|
$lb->approvePrimaryChanges( [] );
|
2021-05-14 22:22:15 +00:00
|
|
|
$lb->commitPrimaryChanges( __METHOD__ );
|
2021-09-02 19:04:20 +00:00
|
|
|
$lb->runPrimaryTransactionIdleCallbacks();
|
2021-09-02 19:07:38 +00:00
|
|
|
$lb->runPrimaryTransactionListenerCallbacks();
|
2018-03-28 20:01:32 +00:00
|
|
|
|
2022-08-24 00:57:22 +00:00
|
|
|
$this->assertSame( array_fill_keys( [ 'a', 'b', 'c', 'd' ], 1 ), $bc );
|
2019-06-25 19:15:07 +00:00
|
|
|
$this->assertSame( 1, $tlCalls );
|
2018-03-28 20:01:32 +00:00
|
|
|
|
|
|
|
|
$tlCalls = 0;
|
2021-05-14 22:16:39 +00:00
|
|
|
$lb->beginPrimaryChanges( __METHOD__ );
|
2018-03-28 20:01:32 +00:00
|
|
|
$ac = array_fill_keys( [ 'a', 'b', 'c', 'd' ], 0 );
|
2021-02-07 13:10:36 +00:00
|
|
|
$conn1->onTransactionCommitOrIdle( static function () use ( &$ac, $conn1, $conn2 ) {
|
2018-03-28 20:01:32 +00:00
|
|
|
$ac['a'] = 1;
|
2021-07-13 19:43:07 +00:00
|
|
|
$conn2->onTransactionCommitOrIdle( static function () use ( &$ac, $conn1 ) {
|
2018-03-28 20:01:32 +00:00
|
|
|
$ac['b'] = 1;
|
2021-07-13 19:43:07 +00:00
|
|
|
$conn1->onTransactionCommitOrIdle( static function () use ( &$ac, $conn1 ) {
|
2018-03-28 20:01:32 +00:00
|
|
|
$ac['c'] = 1;
|
2021-07-13 19:43:07 +00:00
|
|
|
$conn1->onTransactionCommitOrIdle( static function () use ( &$ac ) {
|
2018-03-28 20:01:32 +00:00
|
|
|
$ac['d'] = 1;
|
|
|
|
|
} );
|
|
|
|
|
} );
|
|
|
|
|
} );
|
|
|
|
|
} );
|
2021-05-14 22:05:55 +00:00
|
|
|
$lb->finalizePrimaryChanges();
|
2021-09-02 22:45:52 +00:00
|
|
|
$lb->approvePrimaryChanges( [] );
|
2021-05-14 22:22:15 +00:00
|
|
|
$lb->commitPrimaryChanges( __METHOD__ );
|
2021-09-02 19:04:20 +00:00
|
|
|
$lb->runPrimaryTransactionIdleCallbacks();
|
2021-09-02 19:07:38 +00:00
|
|
|
$lb->runPrimaryTransactionListenerCallbacks();
|
2018-03-28 20:01:32 +00:00
|
|
|
|
2022-08-24 00:57:22 +00:00
|
|
|
$this->assertSame( array_fill_keys( [ 'a', 'b', 'c', 'd' ], 1 ), $ac );
|
2019-06-25 19:15:07 +00:00
|
|
|
$this->assertSame( 1, $tlCalls );
|
2018-03-28 20:01:32 +00:00
|
|
|
|
2022-03-18 00:08:39 +00:00
|
|
|
$conn1->lock( 'test_lock_' . mt_rand(), __METHOD__, 0 );
|
|
|
|
|
$lb->flushPrimarySessions( __METHOD__ );
|
|
|
|
|
$this->assertSame( TransactionManager::STATUS_TRX_NONE, $conn1->trxStatus() );
|
|
|
|
|
$this->assertSame( TransactionManager::STATUS_TRX_NONE, $conn2->trxStatus() );
|
2018-03-28 20:01:32 +00:00
|
|
|
}
|
2019-03-21 16:10:23 +00:00
|
|
|
|
2019-06-18 23:57:32 +00:00
|
|
|
/**
|
2019-07-14 22:43:26 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getConnectionRef()
|
|
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getConnection()
|
|
|
|
|
*/
|
|
|
|
|
public function testForbiddenWritesNoRef() {
|
|
|
|
|
// Simulate web request with DBO_TRX
|
|
|
|
|
$lb = $this->newMultiServerLocalLoadBalancer( [], [ 'flags' => DBO_TRX ] );
|
|
|
|
|
|
|
|
|
|
$dbr = $lb->getConnection( DB_REPLICA );
|
|
|
|
|
$this->assertTrue( $dbr->isReadOnly(), 'replica shows as replica' );
|
2019-10-11 22:22:26 +00:00
|
|
|
$this->expectException( DBReadOnlyRoleError::class );
|
2019-07-14 22:43:26 +00:00
|
|
|
$dbr->delete( 'some_table', [ 'id' => 57634126 ], __METHOD__ );
|
|
|
|
|
|
2019-10-11 22:22:26 +00:00
|
|
|
// FIXME: not needed?
|
2022-03-16 19:35:31 +00:00
|
|
|
$lb->closeAll( __METHOD__ );
|
2019-07-14 22:43:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getConnectionRef()
|
2019-06-18 23:57:32 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getConnection()
|
2019-07-03 22:02:41 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getConnectionRef()
|
2019-06-18 23:57:32 +00:00
|
|
|
*/
|
2019-03-21 16:10:23 +00:00
|
|
|
public function testDBConnRefReadsMasterAndReplicaRoles() {
|
|
|
|
|
$lb = $this->newSingleServerLocalLoadBalancer();
|
|
|
|
|
|
|
|
|
|
$rConn = $lb->getConnectionRef( DB_REPLICA );
|
2021-04-29 02:37:11 +00:00
|
|
|
$wConn = $lb->getConnectionRef( DB_PRIMARY );
|
2019-03-21 16:10:23 +00:00
|
|
|
$wConn2 = $lb->getConnectionRef( 0 );
|
|
|
|
|
|
|
|
|
|
$v = [ 'value' => '1', '1' ];
|
|
|
|
|
$sql = 'SELECT MAX(1) AS value';
|
|
|
|
|
foreach ( [ $rConn, $wConn, $wConn2 ] as $conn ) {
|
|
|
|
|
$conn->clearFlag( $conn::DBO_TRX );
|
|
|
|
|
|
|
|
|
|
$res = $conn->query( $sql, __METHOD__ );
|
2022-01-06 05:33:26 +00:00
|
|
|
$this->assertEquals( $v, $res->fetchRow() );
|
2019-03-21 16:10:23 +00:00
|
|
|
|
|
|
|
|
$res = $conn->query( $sql, __METHOD__, $conn::QUERY_REPLICA_ROLE );
|
2022-01-06 05:33:26 +00:00
|
|
|
$this->assertEquals( $v, $res->fetchRow() );
|
2019-03-21 16:10:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$wConn->getScopedLockAndFlush( 'key', __METHOD__, 1 );
|
|
|
|
|
$wConn2->getScopedLockAndFlush( 'key2', __METHOD__, 1 );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2019-07-14 22:43:26 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getConnectionRef()
|
2019-03-21 16:10:23 +00:00
|
|
|
*/
|
|
|
|
|
public function testDBConnRefWritesReplicaRole() {
|
|
|
|
|
$lb = $this->newSingleServerLocalLoadBalancer();
|
|
|
|
|
|
|
|
|
|
$rConn = $lb->getConnectionRef( DB_REPLICA );
|
|
|
|
|
|
2019-10-11 22:22:26 +00:00
|
|
|
$this->expectException( DBReadOnlyRoleError::class );
|
2019-03-21 16:10:23 +00:00
|
|
|
$rConn->query( 'DELETE FROM sometesttable WHERE 1=0' );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2019-07-14 22:43:26 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getConnectionRef()
|
2019-03-21 16:10:23 +00:00
|
|
|
*/
|
|
|
|
|
public function testDBConnRefWritesReplicaRoleIndex() {
|
|
|
|
|
$lb = $this->newMultiServerLocalLoadBalancer();
|
|
|
|
|
|
|
|
|
|
$rConn = $lb->getConnectionRef( 1 );
|
|
|
|
|
|
2019-10-11 22:22:26 +00:00
|
|
|
$this->expectException( DBReadOnlyRoleError::class );
|
2019-03-21 16:10:23 +00:00
|
|
|
$rConn->query( 'DELETE FROM sometesttable WHERE 1=0' );
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-14 22:43:26 +00:00
|
|
|
/**
|
|
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getConnectionRef()
|
2019-03-21 16:10:23 +00:00
|
|
|
*/
|
|
|
|
|
public function testDBConnRefWritesReplicaRoleInsert() {
|
|
|
|
|
$lb = $this->newMultiServerLocalLoadBalancer();
|
|
|
|
|
|
|
|
|
|
$rConn = $lb->getConnectionRef( DB_REPLICA );
|
|
|
|
|
|
2019-10-11 22:22:26 +00:00
|
|
|
$this->expectException( DBReadOnlyRoleError::class );
|
2019-03-21 16:10:23 +00:00
|
|
|
$rConn->insert( 'test', [ 't' => 1 ], __METHOD__ );
|
|
|
|
|
}
|
2019-05-08 19:18:01 +00:00
|
|
|
|
2019-07-15 23:59:21 +00:00
|
|
|
/**
|
|
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getConnection()
|
|
|
|
|
*/
|
|
|
|
|
public function testGetConnectionRefDefaultGroup() {
|
|
|
|
|
$lb = $this->newMultiServerLocalLoadBalancer( [ 'defaultGroup' => 'vslow' ] );
|
|
|
|
|
$lbWrapper = TestingAccessWrapper::newFromObject( $lb );
|
|
|
|
|
|
|
|
|
|
$rVslow = $lb->getConnectionRef( DB_REPLICA );
|
|
|
|
|
$vslowIndexPicked = $rVslow->getLBInfo( 'serverIndex' );
|
|
|
|
|
|
|
|
|
|
$this->assertSame( $vslowIndexPicked, $lbWrapper->getExistingReaderIndex( 'vslow' ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getConnection()
|
|
|
|
|
*/
|
|
|
|
|
public function testGetConnectionRefUnknownDefaultGroup() {
|
|
|
|
|
$lb = $this->newMultiServerLocalLoadBalancer( [ 'defaultGroup' => 'invalid' ] );
|
|
|
|
|
|
|
|
|
|
$this->assertInstanceOf(
|
|
|
|
|
IDatabase::class,
|
|
|
|
|
$lb->getConnectionRef( DB_REPLICA )
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-18 23:57:32 +00:00
|
|
|
/**
|
|
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getConnection()
|
|
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getMaintenanceConnectionRef()
|
|
|
|
|
*/
|
2019-05-08 19:18:01 +00:00
|
|
|
public function testQueryGroupIndex() {
|
2019-06-24 23:36:42 +00:00
|
|
|
$lb = $this->newMultiServerLocalLoadBalancer( [ 'defaultGroup' => false ] );
|
2019-05-08 19:18:01 +00:00
|
|
|
/** @var LoadBalancer $lbWrapper */
|
|
|
|
|
$lbWrapper = TestingAccessWrapper::newFromObject( $lb );
|
|
|
|
|
|
|
|
|
|
$rGeneric = $lb->getConnectionRef( DB_REPLICA );
|
|
|
|
|
$mainIndexPicked = $rGeneric->getLBInfo( 'serverIndex' );
|
|
|
|
|
|
2022-08-24 00:57:22 +00:00
|
|
|
$this->assertSame(
|
2019-07-15 23:59:21 +00:00
|
|
|
$mainIndexPicked,
|
|
|
|
|
$lbWrapper->getExistingReaderIndex( $lb::GROUP_GENERIC )
|
|
|
|
|
);
|
2021-01-30 12:51:38 +00:00
|
|
|
$this->assertContains( $mainIndexPicked, [ 1, 2 ] );
|
2019-05-08 19:18:01 +00:00
|
|
|
for ( $i = 0; $i < 300; ++$i ) {
|
|
|
|
|
$rLog = $lb->getConnectionRef( DB_REPLICA, [] );
|
2022-08-24 00:57:22 +00:00
|
|
|
$this->assertSame(
|
2019-05-08 19:18:01 +00:00
|
|
|
$mainIndexPicked,
|
|
|
|
|
$rLog->getLBInfo( 'serverIndex' ),
|
|
|
|
|
"Main index unchanged" );
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-16 18:33:43 +00:00
|
|
|
$rRC = $lb->getConnectionRef( DB_REPLICA, [ 'foo' ] );
|
|
|
|
|
$rWL = $lb->getConnectionRef( DB_REPLICA, [ 'bar' ] );
|
|
|
|
|
$rRCMaint = $lb->getMaintenanceConnectionRef( DB_REPLICA, [ 'foo' ] );
|
|
|
|
|
$rWLMaint = $lb->getMaintenanceConnectionRef( DB_REPLICA, [ 'bar' ] );
|
2019-05-08 19:18:01 +00:00
|
|
|
|
2022-08-24 00:57:22 +00:00
|
|
|
$this->assertSame( 3, $rRC->getLBInfo( 'serverIndex' ) );
|
|
|
|
|
$this->assertSame( 3, $rWL->getLBInfo( 'serverIndex' ) );
|
|
|
|
|
$this->assertSame( 3, $rRCMaint->getLBInfo( 'serverIndex' ) );
|
|
|
|
|
$this->assertSame( 3, $rWLMaint->getLBInfo( 'serverIndex' ) );
|
2019-05-08 19:18:01 +00:00
|
|
|
|
2022-08-16 18:33:43 +00:00
|
|
|
$rLog = $lb->getConnectionRef( DB_REPLICA, [ 'baz', 'bar' ] );
|
2019-05-08 19:18:01 +00:00
|
|
|
$logIndexPicked = $rLog->getLBInfo( 'serverIndex' );
|
|
|
|
|
|
2022-08-24 00:57:22 +00:00
|
|
|
$this->assertSame( $logIndexPicked, $lbWrapper->getExistingReaderIndex( 'baz' ) );
|
2021-01-30 12:51:38 +00:00
|
|
|
$this->assertContains( $logIndexPicked, [ 4, 5 ] );
|
2019-05-08 19:18:01 +00:00
|
|
|
|
|
|
|
|
for ( $i = 0; $i < 300; ++$i ) {
|
2022-08-16 18:33:43 +00:00
|
|
|
$rLog = $lb->getConnectionRef( DB_REPLICA, [ 'baz', 'bar' ] );
|
2022-08-24 00:57:22 +00:00
|
|
|
$this->assertSame(
|
2019-05-08 19:18:01 +00:00
|
|
|
$logIndexPicked, $rLog->getLBInfo( 'serverIndex' ), "Index unchanged" );
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-16 18:33:43 +00:00
|
|
|
$rVslow = $lb->getConnectionRef( DB_REPLICA, [ 'vslow', 'baz' ] );
|
2019-05-08 19:18:01 +00:00
|
|
|
$vslowIndexPicked = $rVslow->getLBInfo( 'serverIndex' );
|
|
|
|
|
|
2022-08-24 00:57:22 +00:00
|
|
|
$this->assertSame( $vslowIndexPicked, $lbWrapper->getExistingReaderIndex( 'vslow' ) );
|
|
|
|
|
$this->assertSame( 6, $vslowIndexPicked );
|
2019-05-08 19:18:01 +00:00
|
|
|
}
|
2019-06-27 18:52:04 +00:00
|
|
|
|
|
|
|
|
public function testNonZeroMasterLoad() {
|
|
|
|
|
$lb = $this->newMultiServerLocalLoadBalancer( [], [ 'flags' => DBO_DEFAULT ], true );
|
|
|
|
|
// Make sure that no infinite loop occurs (T226678)
|
|
|
|
|
$rGeneric = $lb->getConnectionRef( DB_REPLICA );
|
2022-08-24 00:57:22 +00:00
|
|
|
$this->assertSame( $lb->getWriterIndex(), $rGeneric->getLBInfo( 'serverIndex' ) );
|
2019-06-27 18:52:04 +00:00
|
|
|
}
|
2019-06-18 23:57:32 +00:00
|
|
|
|
2019-07-03 22:02:41 +00:00
|
|
|
/**
|
2022-05-27 22:27:16 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::setDomainAliases()
|
|
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::resolveDomainID()
|
2019-07-03 22:02:41 +00:00
|
|
|
*/
|
|
|
|
|
public function testSetDomainAliases() {
|
|
|
|
|
$lb = $this->newMultiServerLocalLoadBalancer();
|
|
|
|
|
$origDomain = $lb->getLocalDomainID();
|
|
|
|
|
|
2022-08-24 00:57:22 +00:00
|
|
|
$this->assertSame( $origDomain, $lb->resolveDomainID( false ) );
|
|
|
|
|
$this->assertSame( "db-prefix_", $lb->resolveDomainID( "db-prefix_" ) );
|
2019-07-03 22:02:41 +00:00
|
|
|
|
|
|
|
|
$lb->setDomainAliases( [
|
|
|
|
|
'alias-db' => 'realdb',
|
|
|
|
|
'alias-db-prefix_' => 'realdb-realprefix_'
|
|
|
|
|
] );
|
|
|
|
|
|
2022-08-24 00:57:22 +00:00
|
|
|
$this->assertSame( 'realdb', $lb->resolveDomainID( 'alias-db' ) );
|
|
|
|
|
$this->assertSame( "realdb-realprefix_", $lb->resolveDomainID( "alias-db-prefix_" ) );
|
2019-07-03 22:02:41 +00:00
|
|
|
}
|
2021-02-22 21:06:23 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer::getClusterName()
|
|
|
|
|
*/
|
|
|
|
|
public function testClusterName() {
|
|
|
|
|
global $wgDBname;
|
|
|
|
|
|
|
|
|
|
$lb1 = new LoadBalancer( [
|
|
|
|
|
'servers' => [ $this->makeServerConfig() ],
|
rdbms: Consolidate logger channels into one
Notable changes:
* In SqlBagOStuff::getConnectionFromServerInfo, only two loggers were
injected. The rest implicitly got a NullLogger due to being absent.
These are now effectively unsilenced.
* Database::__construct() required almost all parameters, even the
loggers. I've wanted to move some of DatabaseFactory into the ctor
here for a while. In order to make this change not a breaking
change, the new 'logger' parameter is optional with NullLogger as
default. This allowed some of the test cases, which were simply
passing NullLogger, to be fixed by passing nothing instead of
passing the new option name.
The Database class is behind a dozen layers of indirection for
real use, so this will still be injected just fine (DBF, LB, LBF,
MWLBF, etc.).
* In LegacyLogger, the handling for $wgDBerrorLog was previously
limited to DBConnection and DBQuery. This now includes errors
from other (generally, newer) parts of Rdbms as well, which were
previously missing.
This only affects sites (typically CI and dev setup) where
$wgDBerrorLog is used, as opposed to the more common
$wgDebugLogGroups by-channel configuration.
* TransactionProfiler gets its logger injected in a rather odd way,
via entrypoints (MediaWiki.php, ApiMain.php, and MaintenanceRunner)
as opposed to service wiring. This is kept as-is for now.
* In LBFactoryTest, in particular testInvalidSelectDBIndependent2,
there are cases that intentionally produce failures of which
the result is then observed. In CI we assert that dberror.log is
empty so instead of adding the missing logger fields to that
LBFactory instance, the only one set (replLogger) is removed.
The alternative is to set 'logger' now, which would naturally
cause CI failures due to unexpected entries coming through to
non-mocked error log.
Bug: T320873
Change-Id: I7ca996618e41b93f488cb5c4de82000bb36e0dd3
2022-10-15 20:16:07 +00:00
|
|
|
'logger' => MediaWiki\Logger\LoggerFactory::getInstance( 'rdbms' ),
|
2021-02-22 21:06:23 +00:00
|
|
|
'localDomain' => new DatabaseDomain( $wgDBname, null, $this->dbPrefix() ),
|
|
|
|
|
'chronologyCallback' => static function () use ( &$called ) {
|
|
|
|
|
$called = true;
|
|
|
|
|
},
|
|
|
|
|
'clusterName' => 'xx'
|
|
|
|
|
] );
|
|
|
|
|
$this->assertSame( 'xx', $lb1->getClusterName() );
|
|
|
|
|
|
|
|
|
|
$lb2 = new LoadBalancer( [
|
|
|
|
|
'servers' => [ $this->makeServerConfig() ],
|
rdbms: Consolidate logger channels into one
Notable changes:
* In SqlBagOStuff::getConnectionFromServerInfo, only two loggers were
injected. The rest implicitly got a NullLogger due to being absent.
These are now effectively unsilenced.
* Database::__construct() required almost all parameters, even the
loggers. I've wanted to move some of DatabaseFactory into the ctor
here for a while. In order to make this change not a breaking
change, the new 'logger' parameter is optional with NullLogger as
default. This allowed some of the test cases, which were simply
passing NullLogger, to be fixed by passing nothing instead of
passing the new option name.
The Database class is behind a dozen layers of indirection for
real use, so this will still be injected just fine (DBF, LB, LBF,
MWLBF, etc.).
* In LegacyLogger, the handling for $wgDBerrorLog was previously
limited to DBConnection and DBQuery. This now includes errors
from other (generally, newer) parts of Rdbms as well, which were
previously missing.
This only affects sites (typically CI and dev setup) where
$wgDBerrorLog is used, as opposed to the more common
$wgDebugLogGroups by-channel configuration.
* TransactionProfiler gets its logger injected in a rather odd way,
via entrypoints (MediaWiki.php, ApiMain.php, and MaintenanceRunner)
as opposed to service wiring. This is kept as-is for now.
* In LBFactoryTest, in particular testInvalidSelectDBIndependent2,
there are cases that intentionally produce failures of which
the result is then observed. In CI we assert that dberror.log is
empty so instead of adding the missing logger fields to that
LBFactory instance, the only one set (replLogger) is removed.
The alternative is to set 'logger' now, which would naturally
cause CI failures due to unexpected entries coming through to
non-mocked error log.
Bug: T320873
Change-Id: I7ca996618e41b93f488cb5c4de82000bb36e0dd3
2022-10-15 20:16:07 +00:00
|
|
|
'logger' => MediaWiki\Logger\LoggerFactory::getInstance( 'rdbms' ),
|
2021-02-22 21:06:23 +00:00
|
|
|
'localDomain' => new DatabaseDomain( $wgDBname, null, $this->dbPrefix() ),
|
|
|
|
|
'chronologyCallback' => static function () use ( &$called ) {
|
|
|
|
|
$called = true;
|
|
|
|
|
},
|
|
|
|
|
'clusterName' => null
|
|
|
|
|
] );
|
|
|
|
|
$this->assertSame( 'testhost', $lb2->getClusterName() );
|
|
|
|
|
}
|
2017-07-20 03:51:54 +00:00
|
|
|
}
|