2013-11-27 10:17:06 +00:00
|
|
|
<?php
|
|
|
|
|
/**
|
|
|
|
|
* 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
|
|
|
|
|
* @author Antoine Musso
|
|
|
|
|
* @copyright © 2013 Antoine Musso
|
|
|
|
|
* @copyright © 2013 Wikimedia Foundation Inc.
|
|
|
|
|
*/
|
2017-12-28 08:28:45 +00:00
|
|
|
|
2023-02-23 20:44:38 +00:00
|
|
|
use MediaWiki\WikiMap\WikiMap;
|
2020-01-10 00:00:51 +00:00
|
|
|
use Wikimedia\Rdbms\ChronologyProtector;
|
2022-05-27 20:38:32 +00:00
|
|
|
use Wikimedia\Rdbms\Database;
|
2020-01-10 00:00:51 +00:00
|
|
|
use Wikimedia\Rdbms\DatabaseDomain;
|
2019-02-12 12:01:26 +00:00
|
|
|
use Wikimedia\Rdbms\IDatabase;
|
|
|
|
|
use Wikimedia\Rdbms\IMaintainableDatabase;
|
2023-04-21 09:35:36 +00:00
|
|
|
use Wikimedia\Rdbms\IReadableDatabase;
|
2018-09-21 01:21:24 +00:00
|
|
|
use Wikimedia\Rdbms\LBFactory;
|
2017-12-28 08:28:45 +00:00
|
|
|
use Wikimedia\Rdbms\LBFactoryMulti;
|
2020-01-10 00:00:51 +00:00
|
|
|
use Wikimedia\Rdbms\LBFactorySimple;
|
2018-01-13 00:02:09 +00:00
|
|
|
use Wikimedia\Rdbms\LoadBalancer;
|
2019-08-29 04:05:10 +00:00
|
|
|
use Wikimedia\Rdbms\LoadMonitorNull;
|
2021-05-14 19:48:07 +00:00
|
|
|
use Wikimedia\Rdbms\MySQLPrimaryPos;
|
2022-04-26 03:54:59 +00:00
|
|
|
use Wikimedia\TestingAccessWrapper;
|
2017-12-28 08:28:45 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @group Database
|
objectcache,resourceloader,rdbms,jobqueue: Widen @covers annotations
Follows-up I4c7d826c7ec654b, I1287f3979aba1bf1.
We lose useful coverage and spend valuable time keeping these accurate
through refactors (or worse, forget to do so). The theoretically "bad"
accidental coverage is almost never actually bad.
Having said that, I'm not removing them wholesale (yet). I've audited
each of these specific files to confirm it is a general test of the
specified subject class, and also kept it limited to those specified
classes. That's imho more than 100% of the benefit for less than 1%
of the cost (more because `@covers` is more valuable than the fragile
and corrosive individual private method tracking in tests that
inevitably get out of date with no local incentive to keep them up to
date).
Cases like structure tests keep `@coversNothing` etc and we still don't
count coverage of other classes. There may be a handful of large
legacy classes where some methods are effectively class-like in
complexity and that's why it's good for PHPUnit to offer the precision
instrument but that doesn't meant we have to use that by-default for
everything.
I think best practice is to write good narrow unit tests, that reflect
how the code should be used in practice. Not to write bad tests and
hide part of its coverage within the same class or even namespace.
Fortunately, that's generally what we do already it's just that we
also kept these annotations still in many cases.
This wastes time to keep methods in sync, time to realize (and fix)
when other people inevitably didn't keep them in sync, time to find
uncovered code only to realize it is already covered, time for a less
experienced engineer to feel obligate to and do write a low quality
test to cover the "missing" branch in an unrealistic way, time wasted
in on-boarding by using such "bad" tests as example for how to use
the code and then having to unlearn it months/years later, loss of
telemetry in knowing what code actually isn't propertly tested due to
being masked by a bad test, and lost oppertunities to find actually
ununused/unreachable code and to think about how to instead structure
the code such that maybe that code can be removed.
------
Especially cases like LBFactoryTest.php were getting out of hand,
and in GlobalIdGeneratorTest.php we even resorted to reminding people
with inline comments to keep tags in sync.
Change-Id: I69b5385868cc6b451e5f2ebec9539694968bf58c
2023-04-10 21:25:34 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\ChronologyProtector
|
|
|
|
|
* @covers \Wikimedia\Rdbms\DatabaseMysqlBase
|
|
|
|
|
* @covers \Wikimedia\Rdbms\DatabasePostgres
|
|
|
|
|
* @covers \Wikimedia\Rdbms\DatabaseSqlite
|
|
|
|
|
* @covers \Wikimedia\Rdbms\LBFactory
|
2018-09-21 01:24:43 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LBFactory
|
2017-12-28 08:28:45 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LBFactoryMulti
|
objectcache,resourceloader,rdbms,jobqueue: Widen @covers annotations
Follows-up I4c7d826c7ec654b, I1287f3979aba1bf1.
We lose useful coverage and spend valuable time keeping these accurate
through refactors (or worse, forget to do so). The theoretically "bad"
accidental coverage is almost never actually bad.
Having said that, I'm not removing them wholesale (yet). I've audited
each of these specific files to confirm it is a general test of the
specified subject class, and also kept it limited to those specified
classes. That's imho more than 100% of the benefit for less than 1%
of the cost (more because `@covers` is more valuable than the fragile
and corrosive individual private method tracking in tests that
inevitably get out of date with no local incentive to keep them up to
date).
Cases like structure tests keep `@coversNothing` etc and we still don't
count coverage of other classes. There may be a handful of large
legacy classes where some methods are effectively class-like in
complexity and that's why it's good for PHPUnit to offer the precision
instrument but that doesn't meant we have to use that by-default for
everything.
I think best practice is to write good narrow unit tests, that reflect
how the code should be used in practice. Not to write bad tests and
hide part of its coverage within the same class or even namespace.
Fortunately, that's generally what we do already it's just that we
also kept these annotations still in many cases.
This wastes time to keep methods in sync, time to realize (and fix)
when other people inevitably didn't keep them in sync, time to find
uncovered code only to realize it is already covered, time for a less
experienced engineer to feel obligate to and do write a low quality
test to cover the "missing" branch in an unrealistic way, time wasted
in on-boarding by using such "bad" tests as example for how to use
the code and then having to unlearn it months/years later, loss of
telemetry in knowing what code actually isn't propertly tested due to
being masked by a bad test, and lost oppertunities to find actually
ununused/unreachable code and to think about how to instead structure
the code such that maybe that code can be removed.
------
Especially cases like LBFactoryTest.php were getting out of hand,
and in GlobalIdGeneratorTest.php we even resorted to reminding people
with inline comments to keep tags in sync.
Change-Id: I69b5385868cc6b451e5f2ebec9539694968bf58c
2023-04-10 21:25:34 +00:00
|
|
|
* @covers \Wikimedia\Rdbms\LBFactorySimple
|
|
|
|
|
* @covers \Wikimedia\Rdbms\LoadBalancer
|
2017-12-28 08:28:45 +00:00
|
|
|
*/
|
2020-06-30 15:09:24 +00:00
|
|
|
class LBFactoryTest extends MediaWikiIntegrationTestCase {
|
2022-05-24 13:50:02 +00:00
|
|
|
|
|
|
|
|
private function getPrimaryServerConfig() {
|
2023-04-26 18:53:56 +00:00
|
|
|
global $wgDBserver, $wgDBport, $wgDBname, $wgDBuser, $wgDBpassword, $wgDBtype;
|
|
|
|
|
global $wgSQLiteDataDir;
|
|
|
|
|
|
2022-05-24 13:50:02 +00:00
|
|
|
return [
|
2023-01-03 20:52:10 +00:00
|
|
|
'serverName' => 'db1',
|
2022-05-24 13:50:02 +00:00
|
|
|
'host' => $wgDBserver,
|
2023-04-26 18:53:56 +00:00
|
|
|
'port' => $wgDBport,
|
2022-05-24 13:50:02 +00:00
|
|
|
'dbname' => $wgDBname,
|
|
|
|
|
'user' => $wgDBuser,
|
|
|
|
|
'password' => $wgDBpassword,
|
|
|
|
|
'type' => $wgDBtype,
|
|
|
|
|
'dbDirectory' => $wgSQLiteDataDir,
|
|
|
|
|
'load' => 0,
|
|
|
|
|
'flags' => DBO_TRX // REPEATABLE-READ for consistency
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-30 23:06:47 +00:00
|
|
|
public function testLBFactorySimpleServer() {
|
2022-05-24 13:50:02 +00:00
|
|
|
$servers = [ $this->getPrimaryServerConfig() ];
|
2016-09-15 18:52:55 +00:00
|
|
|
$factory = new LBFactorySimple( [ 'servers' => $servers ] );
|
2015-09-30 23:06:47 +00:00
|
|
|
$lb = $factory->getMainLB();
|
|
|
|
|
|
2021-04-29 02:37:11 +00:00
|
|
|
$dbw = $lb->getConnection( DB_PRIMARY );
|
2019-07-14 22:43:26 +00:00
|
|
|
$this->assertEquals(
|
|
|
|
|
$dbw::ROLE_STREAMING_MASTER, $dbw->getTopologyRole(), 'master shows as master' );
|
2015-09-30 23:06:47 +00:00
|
|
|
|
2017-08-04 18:53:34 +00:00
|
|
|
$dbr = $lb->getConnection( DB_REPLICA );
|
2019-07-14 22:43:26 +00:00
|
|
|
$this->assertEquals(
|
|
|
|
|
$dbr::ROLE_STREAMING_MASTER, $dbw->getTopologyRole(), 'replica shows as replica' );
|
2015-09-30 23:06:47 +00:00
|
|
|
|
2023-03-03 00:27:59 +00:00
|
|
|
$this->assertSame( 'DEFAULT', $lb->getClusterName() );
|
2018-07-05 12:50:57 +00:00
|
|
|
$this->assertSame( 'my_test_wiki', $factory->resolveDomainID( 'my_test_wiki' ) );
|
|
|
|
|
$this->assertSame( $factory->getLocalDomainID(), $factory->resolveDomainID( false ) );
|
|
|
|
|
|
2015-09-30 23:06:47 +00:00
|
|
|
$factory->shutdown();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testLBFactorySimpleServers() {
|
2022-05-24 13:50:02 +00:00
|
|
|
$primaryConfig = $this->getPrimaryServerConfig();
|
2023-01-03 20:52:10 +00:00
|
|
|
$fakeReplica = [ 'serverName' => 'db2', 'load' => 100 ] + $primaryConfig;
|
2015-09-30 23:06:47 +00:00
|
|
|
|
2016-09-15 18:52:55 +00:00
|
|
|
$servers = [
|
2022-05-24 13:50:02 +00:00
|
|
|
$primaryConfig,
|
|
|
|
|
$fakeReplica
|
2016-09-15 18:52:55 +00:00
|
|
|
];
|
2015-09-30 23:06:47 +00:00
|
|
|
|
2016-09-15 18:52:55 +00:00
|
|
|
$factory = new LBFactorySimple( [
|
|
|
|
|
'servers' => $servers,
|
2020-03-28 03:18:05 +00:00
|
|
|
'loadMonitor' => [ 'class' => LoadMonitorNull::class ],
|
2016-09-15 18:52:55 +00:00
|
|
|
] );
|
2015-09-30 23:06:47 +00:00
|
|
|
$lb = $factory->getMainLB();
|
|
|
|
|
|
2021-04-29 02:37:11 +00:00
|
|
|
$dbw = $lb->getConnection( DB_PRIMARY );
|
2022-08-24 00:57:22 +00:00
|
|
|
$dbw->ensureConnection();
|
|
|
|
|
|
2019-07-14 22:43:26 +00:00
|
|
|
$this->assertEquals(
|
2021-09-02 18:52:20 +00:00
|
|
|
$dbw::ROLE_STREAMING_MASTER, $dbw->getTopologyRole(), 'primary shows as primary' );
|
2015-09-30 23:06:47 +00:00
|
|
|
|
2017-08-04 18:53:34 +00:00
|
|
|
$dbr = $lb->getConnection( DB_REPLICA );
|
2022-08-24 00:57:22 +00:00
|
|
|
$dbr->ensureConnection();
|
|
|
|
|
|
2019-07-14 22:43:26 +00:00
|
|
|
$this->assertEquals(
|
|
|
|
|
$dbr::ROLE_STREAMING_REPLICA, $dbr->getTopologyRole(), 'replica shows as replica' );
|
2015-09-30 23:06:47 +00:00
|
|
|
|
|
|
|
|
$factory->shutdown();
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-10 04:14:40 +00:00
|
|
|
public function testLBFactoryMultiConns() {
|
|
|
|
|
$factory = $this->newLBFactoryMultiLBs();
|
2015-09-30 23:06:47 +00:00
|
|
|
|
2023-03-03 00:27:59 +00:00
|
|
|
$this->assertSame( 's3', $factory->getMainLB()->getClusterName() );
|
|
|
|
|
|
2021-04-29 02:37:11 +00:00
|
|
|
$dbw = $factory->getMainLB()->getConnection( DB_PRIMARY );
|
2019-07-14 22:43:26 +00:00
|
|
|
$this->assertEquals(
|
|
|
|
|
$dbw::ROLE_STREAMING_MASTER, $dbw->getTopologyRole(), 'master shows as master' );
|
2015-09-30 23:06:47 +00:00
|
|
|
|
2018-05-10 04:14:40 +00:00
|
|
|
$dbr = $factory->getMainLB()->getConnection( DB_REPLICA );
|
2019-07-14 22:43:26 +00:00
|
|
|
$this->assertEquals(
|
|
|
|
|
$dbr::ROLE_STREAMING_REPLICA, $dbr->getTopologyRole(), 'replica shows as replica' );
|
2015-09-30 23:06:47 +00:00
|
|
|
|
2018-05-10 04:14:40 +00:00
|
|
|
// Destructor should trigger without round stage errors
|
|
|
|
|
unset( $factory );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testLBFactoryMultiRoundCallbacks() {
|
|
|
|
|
$called = 0;
|
2021-02-06 19:30:20 +00:00
|
|
|
$countLBsFunc = static function ( LBFactoryMulti $factory ) {
|
2018-05-10 04:14:40 +00:00
|
|
|
$count = 0;
|
2022-04-26 03:54:59 +00:00
|
|
|
foreach ( $factory->getAllLBs() as $lb ) {
|
2018-05-10 04:14:40 +00:00
|
|
|
++$count;
|
2022-04-26 03:54:59 +00:00
|
|
|
}
|
2018-05-10 04:14:40 +00:00
|
|
|
|
|
|
|
|
return $count;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$factory = $this->newLBFactoryMultiLBs();
|
2019-09-17 14:31:49 +00:00
|
|
|
$this->assertSame( 0, $countLBsFunc( $factory ) );
|
2021-04-29 02:37:11 +00:00
|
|
|
$dbw = $factory->getMainLB()->getConnection( DB_PRIMARY );
|
2020-05-30 10:36:42 +00:00
|
|
|
$this->assertSame( 1, $countLBsFunc( $factory ) );
|
2018-05-10 04:14:40 +00:00
|
|
|
// Test that LoadBalancer instances made during pre-commit callbacks in do not
|
|
|
|
|
// throw DBTransactionError due to transaction ROUND_* stages being mismatched.
|
2021-05-14 22:16:39 +00:00
|
|
|
$factory->beginPrimaryChanges( __METHOD__ );
|
2021-02-07 13:10:36 +00:00
|
|
|
$dbw->onTransactionPreCommitOrIdle( static function () use ( $factory, &$called ) {
|
2018-05-10 04:14:40 +00:00
|
|
|
++$called;
|
2018-05-05 22:09:30 +00:00
|
|
|
// Trigger s1 LoadBalancer instantiation during "finalize" stage.
|
|
|
|
|
// There is no s1wiki DB to select so it is not in getConnection(),
|
|
|
|
|
// but this fools getMainLB() at least.
|
2021-04-29 02:37:11 +00:00
|
|
|
$factory->getMainLB( 's1wiki' )->getConnection( DB_PRIMARY );
|
2018-05-05 22:09:30 +00:00
|
|
|
} );
|
2021-05-14 22:22:15 +00:00
|
|
|
$factory->commitPrimaryChanges( __METHOD__ );
|
2020-05-30 10:36:42 +00:00
|
|
|
$this->assertSame( 1, $called );
|
2018-05-10 04:14:40 +00:00
|
|
|
$this->assertEquals( 2, $countLBsFunc( $factory ) );
|
|
|
|
|
$factory->shutdown();
|
2022-03-16 19:35:31 +00:00
|
|
|
$factory->closeAll( __METHOD__ );
|
2018-05-05 22:09:30 +00:00
|
|
|
|
2018-05-10 04:14:40 +00:00
|
|
|
$called = 0;
|
|
|
|
|
$factory = $this->newLBFactoryMultiLBs();
|
2019-09-17 14:31:49 +00:00
|
|
|
$this->assertSame( 0, $countLBsFunc( $factory ) );
|
2021-04-29 02:37:11 +00:00
|
|
|
$dbw = $factory->getMainLB()->getConnection( DB_PRIMARY );
|
2020-05-30 10:36:42 +00:00
|
|
|
$this->assertSame( 1, $countLBsFunc( $factory ) );
|
2018-05-10 04:14:40 +00:00
|
|
|
// Test that LoadBalancer instances made during pre-commit callbacks in do not
|
|
|
|
|
// throw DBTransactionError due to transaction ROUND_* stages being mismatched.hrow
|
|
|
|
|
// DBTransactionError due to transaction ROUND_* stages being mismatched.
|
2021-05-14 22:16:39 +00:00
|
|
|
$factory->beginPrimaryChanges( __METHOD__ );
|
2018-05-10 04:14:40 +00:00
|
|
|
$dbw->query( "SELECT 1 as t", __METHOD__ );
|
2021-02-07 13:10:36 +00:00
|
|
|
$dbw->onTransactionResolution( static function () use ( $factory, &$called ) {
|
2018-05-10 04:14:40 +00:00
|
|
|
++$called;
|
|
|
|
|
// Trigger s1 LoadBalancer instantiation during "finalize" stage.
|
|
|
|
|
// There is no s1wiki DB to select so it is not in getConnection(),
|
|
|
|
|
// but this fools getMainLB() at least.
|
2021-04-29 02:37:11 +00:00
|
|
|
$factory->getMainLB( 's1wiki' )->getConnection( DB_PRIMARY );
|
2018-05-05 22:09:30 +00:00
|
|
|
} );
|
2021-05-14 22:22:15 +00:00
|
|
|
$factory->commitPrimaryChanges( __METHOD__ );
|
2020-05-30 10:36:42 +00:00
|
|
|
$this->assertSame( 1, $called );
|
2018-05-10 04:14:40 +00:00
|
|
|
$this->assertEquals( 2, $countLBsFunc( $factory ) );
|
|
|
|
|
$factory->shutdown();
|
2022-03-16 19:35:31 +00:00
|
|
|
$factory->closeAll( __METHOD__ );
|
2018-05-05 22:09:30 +00:00
|
|
|
|
2018-05-10 04:14:40 +00:00
|
|
|
$factory = $this->newLBFactoryMultiLBs();
|
2021-04-29 02:37:11 +00:00
|
|
|
$dbw = $factory->getMainLB()->getConnection( DB_PRIMARY );
|
2018-05-09 21:44:34 +00:00
|
|
|
// DBTransactionError should not be thrown
|
|
|
|
|
$ran = 0;
|
2021-02-07 13:10:36 +00:00
|
|
|
$dbw->onTransactionPreCommitOrIdle( static function () use ( &$ran ) {
|
2018-05-09 21:44:34 +00:00
|
|
|
++$ran;
|
|
|
|
|
} );
|
2023-02-01 07:28:03 +00:00
|
|
|
$factory->commitPrimaryChanges( __METHOD__ );
|
2020-05-30 10:36:42 +00:00
|
|
|
$this->assertSame( 1, $ran );
|
2018-05-09 21:44:34 +00:00
|
|
|
|
2015-09-30 23:06:47 +00:00
|
|
|
$factory->shutdown();
|
2022-03-16 19:35:31 +00:00
|
|
|
$factory->closeAll( __METHOD__ );
|
2015-09-30 23:06:47 +00:00
|
|
|
}
|
2015-10-18 19:53:40 +00:00
|
|
|
|
2018-05-10 04:14:40 +00:00
|
|
|
private function newLBFactoryMultiLBs() {
|
2023-04-26 18:53:56 +00:00
|
|
|
global $wgDBserver, $wgDBport, $wgDBname, $wgDBuser, $wgDBpassword, $wgDBtype;
|
|
|
|
|
global $wgSQLiteDataDir;
|
2018-05-10 04:14:40 +00:00
|
|
|
|
|
|
|
|
return new LBFactoryMulti( [
|
|
|
|
|
'sectionsByDB' => [
|
|
|
|
|
's1wiki' => 's1',
|
2023-03-03 00:27:59 +00:00
|
|
|
'DEFAULT' => 's3'
|
2018-05-10 04:14:40 +00:00
|
|
|
],
|
|
|
|
|
'sectionLoads' => [
|
|
|
|
|
's1' => [
|
|
|
|
|
'test-db3' => 0,
|
|
|
|
|
'test-db4' => 100,
|
|
|
|
|
],
|
2023-03-03 00:27:59 +00:00
|
|
|
's3' => [
|
2018-05-10 04:14:40 +00:00
|
|
|
'test-db1' => 0,
|
|
|
|
|
'test-db2' => 100,
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
'serverTemplate' => [
|
2023-04-26 18:53:56 +00:00
|
|
|
'port' => $wgDBport,
|
2018-05-10 04:14:40 +00:00
|
|
|
'dbname' => $wgDBname,
|
|
|
|
|
'user' => $wgDBuser,
|
|
|
|
|
'password' => $wgDBpassword,
|
|
|
|
|
'type' => $wgDBtype,
|
|
|
|
|
'dbDirectory' => $wgSQLiteDataDir,
|
|
|
|
|
'flags' => DBO_DEFAULT
|
|
|
|
|
],
|
|
|
|
|
'hostsByName' => [
|
|
|
|
|
'test-db1' => $wgDBserver,
|
|
|
|
|
'test-db2' => $wgDBserver,
|
|
|
|
|
'test-db3' => $wgDBserver,
|
|
|
|
|
'test-db4' => $wgDBserver
|
|
|
|
|
],
|
2020-03-28 03:18:05 +00:00
|
|
|
'loadMonitor' => [ 'class' => LoadMonitorNull::class ],
|
2018-05-10 04:14:40 +00:00
|
|
|
] );
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-28 08:28:45 +00:00
|
|
|
/**
|
|
|
|
|
* @covers \Wikimedia\Rdbms\ChronologyProtector
|
|
|
|
|
*/
|
2015-10-18 19:53:40 +00:00
|
|
|
public function testChronologyProtector() {
|
2016-09-08 20:07:15 +00:00
|
|
|
$now = microtime( true );
|
2017-12-08 21:36:25 +00:00
|
|
|
|
2022-07-05 23:25:44 +00:00
|
|
|
$hasChangesFunc = static function ( $mockDB ) {
|
|
|
|
|
$p = $mockDB->writesOrCallbacksPending();
|
|
|
|
|
$last = $mockDB->lastDoneWrites();
|
|
|
|
|
|
|
|
|
|
return is_float( $last ) || $p;
|
|
|
|
|
};
|
|
|
|
|
|
2018-02-07 09:39:24 +00:00
|
|
|
// (a) First HTTP request
|
2021-05-14 19:48:07 +00:00
|
|
|
$m1Pos = new MySQLPrimaryPos( 'db1034-bin.000976/843431247', $now );
|
|
|
|
|
$m2Pos = new MySQLPrimaryPos( 'db1064-bin.002400/794074907', $now );
|
2018-02-07 09:39:24 +00:00
|
|
|
|
2021-05-14 20:04:02 +00:00
|
|
|
// Primary DB 1
|
2020-05-29 06:46:30 +00:00
|
|
|
/** @var IDatabase|\PHPUnit\Framework\MockObject\MockObject $mockDB1 */
|
2022-07-14 12:42:07 +00:00
|
|
|
$mockDB1 = $this->createMock( IDatabase::class );
|
2017-12-08 21:36:25 +00:00
|
|
|
$mockDB1->method( 'writesOrCallbacksPending' )->willReturn( true );
|
|
|
|
|
$mockDB1->method( 'lastDoneWrites' )->willReturn( $now );
|
2021-05-14 21:56:40 +00:00
|
|
|
$mockDB1->method( 'getPrimaryPos' )->willReturn( $m1Pos );
|
2021-05-14 20:04:02 +00:00
|
|
|
// Load balancer for primary DB 1
|
2022-07-14 12:42:07 +00:00
|
|
|
$lb1 = $this->createMock( LoadBalancer::class );
|
2017-12-08 21:36:25 +00:00
|
|
|
$lb1->method( 'getConnection' )->willReturn( $mockDB1 );
|
|
|
|
|
$lb1->method( 'getServerCount' )->willReturn( 2 );
|
2019-06-18 21:12:06 +00:00
|
|
|
$lb1->method( 'hasReplicaServers' )->willReturn( true );
|
|
|
|
|
$lb1->method( 'hasStreamingReplicaServers' )->willReturn( true );
|
2017-12-08 21:36:25 +00:00
|
|
|
$lb1->method( 'getAnyOpenConnection' )->willReturn( $mockDB1 );
|
2022-06-05 23:39:02 +00:00
|
|
|
$lb1->method( 'hasOrMadeRecentPrimaryChanges' )->willReturnCallback(
|
2022-07-05 23:25:44 +00:00
|
|
|
static function () use ( $mockDB1, $hasChangesFunc ) {
|
|
|
|
|
return $hasChangesFunc( $mockDB1 );
|
2022-06-05 23:39:02 +00:00
|
|
|
}
|
|
|
|
|
);
|
2021-05-14 21:56:40 +00:00
|
|
|
$lb1->method( 'getPrimaryPos' )->willReturn( $m1Pos );
|
2019-06-18 23:57:32 +00:00
|
|
|
$lb1->method( 'getReplicaResumePos' )->willReturn( $m1Pos );
|
2017-12-08 21:36:25 +00:00
|
|
|
$lb1->method( 'getServerName' )->with( 0 )->willReturn( 'master1' );
|
2021-05-14 20:04:02 +00:00
|
|
|
// Primary DB 2
|
2020-05-29 06:46:30 +00:00
|
|
|
/** @var IDatabase|\PHPUnit\Framework\MockObject\MockObject $mockDB2 */
|
2022-07-14 12:42:07 +00:00
|
|
|
$mockDB2 = $this->createMock( IDatabase::class );
|
2017-12-08 21:36:25 +00:00
|
|
|
$mockDB2->method( 'writesOrCallbacksPending' )->willReturn( true );
|
|
|
|
|
$mockDB2->method( 'lastDoneWrites' )->willReturn( $now );
|
2021-05-14 21:56:40 +00:00
|
|
|
$mockDB2->method( 'getPrimaryPos' )->willReturn( $m2Pos );
|
2021-05-14 20:04:02 +00:00
|
|
|
// Load balancer for primary DB 2
|
2022-07-14 12:42:07 +00:00
|
|
|
$lb2 = $this->createMock( LoadBalancer::class );
|
2017-12-08 21:36:25 +00:00
|
|
|
$lb2->method( 'getConnection' )->willReturn( $mockDB2 );
|
|
|
|
|
$lb2->method( 'getServerCount' )->willReturn( 2 );
|
2019-06-18 21:12:06 +00:00
|
|
|
$lb2->method( 'hasReplicaServers' )->willReturn( true );
|
|
|
|
|
$lb2->method( 'hasStreamingReplicaServers' )->willReturn( true );
|
2017-12-08 21:36:25 +00:00
|
|
|
$lb2->method( 'getAnyOpenConnection' )->willReturn( $mockDB2 );
|
2022-06-05 23:39:02 +00:00
|
|
|
$lb2->method( 'hasOrMadeRecentPrimaryChanges' )->willReturnCallback(
|
2022-07-05 23:25:44 +00:00
|
|
|
static function () use ( $mockDB2, $hasChangesFunc ) {
|
|
|
|
|
return $hasChangesFunc( $mockDB2 );
|
2017-12-08 21:36:25 +00:00
|
|
|
}
|
2022-06-05 23:39:02 +00:00
|
|
|
);
|
2021-05-14 21:56:40 +00:00
|
|
|
$lb2->method( 'getPrimaryPos' )->willReturn( $m2Pos );
|
2019-06-18 23:57:32 +00:00
|
|
|
$lb2->method( 'getReplicaResumePos' )->willReturn( $m2Pos );
|
2017-12-08 21:36:25 +00:00
|
|
|
$lb2->method( 'getServerName' )->with( 0 )->willReturn( 'master2' );
|
2015-10-18 19:53:40 +00:00
|
|
|
|
|
|
|
|
$bag = new HashBagOStuff();
|
|
|
|
|
$cp = new ChronologyProtector(
|
|
|
|
|
$bag,
|
2016-02-17 09:09:32 +00:00
|
|
|
[
|
2015-10-18 19:53:40 +00:00
|
|
|
'ip' => '127.0.0.1',
|
|
|
|
|
'agent' => "Totally-Not-FireFox"
|
2019-04-19 23:18:01 +00:00
|
|
|
],
|
|
|
|
|
null
|
2015-10-18 19:53:40 +00:00
|
|
|
);
|
|
|
|
|
|
2021-04-22 07:42:28 +00:00
|
|
|
$mockDB1->expects( $this->once() )->method( 'writesOrCallbacksPending' );
|
|
|
|
|
$mockDB1->expects( $this->once() )->method( 'lastDoneWrites' );
|
|
|
|
|
$mockDB2->expects( $this->once() )->method( 'writesOrCallbacksPending' );
|
|
|
|
|
$mockDB2->expects( $this->once() )->method( 'lastDoneWrites' );
|
2017-12-08 21:36:25 +00:00
|
|
|
|
|
|
|
|
// Nothing to wait for on first HTTP request start
|
2022-12-21 05:23:48 +00:00
|
|
|
$sPos1 = $cp->yieldSessionPrimaryPos( $lb1 );
|
|
|
|
|
$sPos2 = $cp->yieldSessionPrimaryPos( $lb2 );
|
2017-12-08 21:36:25 +00:00
|
|
|
// Record positions in stash on first HTTP request end
|
2022-12-21 05:23:48 +00:00
|
|
|
$cp->stageSessionPrimaryPos( $lb1 );
|
|
|
|
|
$cp->stageSessionPrimaryPos( $lb2 );
|
2017-12-08 21:36:25 +00:00
|
|
|
$cpIndex = null;
|
2021-04-05 19:29:15 +00:00
|
|
|
$cp->persistSessionReplicationPositions( $cpIndex );
|
2015-10-18 19:53:40 +00:00
|
|
|
|
2022-12-21 05:23:48 +00:00
|
|
|
$this->assertNull( $sPos1 );
|
|
|
|
|
$this->assertNull( $sPos2 );
|
2020-05-30 10:36:42 +00:00
|
|
|
$this->assertSame( 1, $cpIndex, "CP write index set" );
|
2015-10-18 19:53:40 +00:00
|
|
|
|
|
|
|
|
// (b) Second HTTP request
|
2017-12-08 21:36:25 +00:00
|
|
|
|
2021-05-14 20:04:02 +00:00
|
|
|
// Load balancer for primary DB 1
|
2022-07-14 12:42:07 +00:00
|
|
|
$lb1 = $this->createMock( LoadBalancer::class );
|
2017-12-08 21:36:25 +00:00
|
|
|
$lb1->method( 'getServerCount' )->willReturn( 2 );
|
2019-06-18 21:12:06 +00:00
|
|
|
$lb1->method( 'hasReplicaServers' )->willReturn( true );
|
|
|
|
|
$lb1->method( 'hasStreamingReplicaServers' )->willReturn( true );
|
2017-12-08 21:36:25 +00:00
|
|
|
$lb1->method( 'getServerName' )->with( 0 )->willReturn( 'master1' );
|
2021-05-14 20:04:02 +00:00
|
|
|
// Load balancer for primary DB 2
|
2022-07-14 12:42:07 +00:00
|
|
|
$lb2 = $this->createMock( LoadBalancer::class );
|
2017-12-08 21:36:25 +00:00
|
|
|
$lb2->method( 'getServerCount' )->willReturn( 2 );
|
2019-06-18 21:12:06 +00:00
|
|
|
$lb2->method( 'hasReplicaServers' )->willReturn( true );
|
|
|
|
|
$lb2->method( 'hasStreamingReplicaServers' )->willReturn( true );
|
2017-12-08 21:36:25 +00:00
|
|
|
$lb2->method( 'getServerName' )->with( 0 )->willReturn( 'master2' );
|
|
|
|
|
|
2015-10-18 19:53:40 +00:00
|
|
|
$cp = new ChronologyProtector(
|
|
|
|
|
$bag,
|
2016-02-17 09:09:32 +00:00
|
|
|
[
|
2015-10-18 19:53:40 +00:00
|
|
|
'ip' => '127.0.0.1',
|
|
|
|
|
'agent' => "Totally-Not-FireFox"
|
2017-12-08 21:36:25 +00:00
|
|
|
],
|
|
|
|
|
$cpIndex
|
2015-10-18 19:53:40 +00:00
|
|
|
);
|
|
|
|
|
|
2022-12-21 05:23:48 +00:00
|
|
|
// Get last positions to be reached on second HTTP request start
|
|
|
|
|
$sPos1 = $cp->yieldSessionPrimaryPos( $lb1 );
|
|
|
|
|
$sPos2 = $cp->yieldSessionPrimaryPos( $lb2 );
|
2017-12-08 21:36:25 +00:00
|
|
|
// Shutdown (nothing to record)
|
2022-12-21 05:23:48 +00:00
|
|
|
$cp->stageSessionPrimaryPos( $lb1 );
|
|
|
|
|
$cp->stageSessionPrimaryPos( $lb2 );
|
2017-12-08 21:36:25 +00:00
|
|
|
$cpIndex = null;
|
2021-04-05 19:29:15 +00:00
|
|
|
$cp->persistSessionReplicationPositions( $cpIndex );
|
2015-10-18 19:53:40 +00:00
|
|
|
|
2022-12-21 05:23:48 +00:00
|
|
|
$this->assertNotNull( $sPos1 );
|
|
|
|
|
$this->assertNotNull( $sPos2 );
|
|
|
|
|
$this->assertSame( $m1Pos->__toString(), $sPos1->__toString() );
|
|
|
|
|
$this->assertSame( $m2Pos->__toString(), $sPos2->__toString() );
|
2019-09-17 14:03:28 +00:00
|
|
|
$this->assertNull( $cpIndex, "CP write index retained" );
|
2018-05-25 07:04:23 +00:00
|
|
|
|
|
|
|
|
$this->assertEquals( '45e93a9c215c031d38b7c42d8e4700ca', $cp->getClientId() );
|
2015-10-18 19:53:40 +00:00
|
|
|
}
|
2016-09-17 04:39:57 +00:00
|
|
|
|
2016-11-16 10:35:20 +00:00
|
|
|
private function newLBFactoryMulti( array $baseOverride = [], array $serverOverride = [] ) {
|
2018-01-12 21:44:12 +00:00
|
|
|
global $wgDBserver, $wgDBuser, $wgDBpassword, $wgDBname, $wgDBprefix, $wgDBtype;
|
|
|
|
|
global $wgSQLiteDataDir;
|
2016-11-16 10:35:20 +00:00
|
|
|
|
|
|
|
|
return new LBFactoryMulti( $baseOverride + [
|
|
|
|
|
'sectionsByDB' => [],
|
|
|
|
|
'sectionLoads' => [
|
|
|
|
|
'DEFAULT' => [
|
|
|
|
|
'test-db1' => 1,
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
'serverTemplate' => $serverOverride + [
|
|
|
|
|
'dbname' => $wgDBname,
|
2018-01-12 21:44:12 +00:00
|
|
|
'tablePrefix' => $wgDBprefix,
|
2016-11-16 10:35:20 +00:00
|
|
|
'user' => $wgDBuser,
|
|
|
|
|
'password' => $wgDBpassword,
|
|
|
|
|
'type' => $wgDBtype,
|
|
|
|
|
'dbDirectory' => $wgSQLiteDataDir,
|
|
|
|
|
'flags' => DBO_DEFAULT
|
|
|
|
|
],
|
|
|
|
|
'hostsByName' => [
|
|
|
|
|
'test-db1' => $wgDBserver,
|
|
|
|
|
],
|
2020-03-28 03:18:05 +00:00
|
|
|
'loadMonitor' => [ 'class' => LoadMonitorNull::class ],
|
2018-05-25 07:04:23 +00:00
|
|
|
'localDomain' => new DatabaseDomain( $wgDBname, null, $wgDBprefix ),
|
|
|
|
|
'agent' => 'MW-UNIT-TESTS'
|
2016-11-16 10:35:20 +00:00
|
|
|
] );
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-17 04:39:57 +00:00
|
|
|
public function testNiceDomains() {
|
2018-04-13 08:39:22 +00:00
|
|
|
global $wgDBname;
|
|
|
|
|
|
2021-04-29 02:37:11 +00:00
|
|
|
if ( wfGetDB( DB_PRIMARY )->databasesAreIndependent() ) {
|
2018-04-13 08:39:22 +00:00
|
|
|
self::markTestSkipped( "Skipping tests about selecting DBs: not applicable" );
|
|
|
|
|
return;
|
2016-10-27 05:44:08 +00:00
|
|
|
}
|
2016-09-17 04:39:57 +00:00
|
|
|
|
2016-10-27 05:44:08 +00:00
|
|
|
$factory = $this->newLBFactoryMulti(
|
|
|
|
|
[],
|
2018-04-13 08:39:22 +00:00
|
|
|
[]
|
2016-10-27 05:44:08 +00:00
|
|
|
);
|
2016-09-17 04:39:57 +00:00
|
|
|
$lb = $factory->getMainLB();
|
|
|
|
|
|
2021-04-29 02:37:11 +00:00
|
|
|
$db = $lb->getConnectionRef( DB_PRIMARY );
|
2018-04-13 08:39:22 +00:00
|
|
|
$this->assertEquals(
|
2021-12-21 00:47:14 +00:00
|
|
|
WikiMap::getCurrentWikiId(),
|
2018-04-13 08:39:22 +00:00
|
|
|
$db->getDomainID()
|
|
|
|
|
);
|
|
|
|
|
unset( $db );
|
2016-09-17 04:39:57 +00:00
|
|
|
|
2019-02-12 12:01:26 +00:00
|
|
|
/** @var IMaintainableDatabase $db */
|
2021-04-29 02:37:11 +00:00
|
|
|
$db = $lb->getConnection( DB_PRIMARY, [], $lb::DOMAIN_ANY );
|
2016-09-17 04:39:57 +00:00
|
|
|
|
2019-09-17 14:28:35 +00:00
|
|
|
$this->assertSame(
|
2018-02-09 02:48:38 +00:00
|
|
|
'',
|
2020-05-26 13:14:46 +00:00
|
|
|
$db->getDomainID(),
|
2018-02-09 02:48:38 +00:00
|
|
|
'Null domain ID handle used'
|
2018-01-12 21:44:12 +00:00
|
|
|
);
|
2019-09-17 14:28:35 +00:00
|
|
|
$this->assertNull(
|
2018-01-12 21:44:12 +00:00
|
|
|
$db->getDBname(),
|
2018-02-09 02:48:38 +00:00
|
|
|
'Null domain ID handle used'
|
2018-01-12 21:44:12 +00:00
|
|
|
);
|
2019-09-17 14:28:35 +00:00
|
|
|
$this->assertSame(
|
2016-09-17 04:39:57 +00:00
|
|
|
'',
|
2018-01-12 21:44:12 +00:00
|
|
|
$db->tablePrefix(),
|
|
|
|
|
'Main domain ID handle used; prefix is empty though'
|
2016-09-17 04:39:57 +00:00
|
|
|
);
|
|
|
|
|
$this->assertEquals(
|
2016-10-27 05:44:08 +00:00
|
|
|
$this->quoteTable( $db, 'page' ),
|
2016-09-17 04:39:57 +00:00
|
|
|
$db->tableName( 'page' ),
|
|
|
|
|
"Correct full table name"
|
|
|
|
|
);
|
|
|
|
|
$this->assertEquals(
|
2016-10-27 05:44:08 +00:00
|
|
|
$this->quoteTable( $db, $wgDBname ) . '.' . $this->quoteTable( $db, 'page' ),
|
2016-09-17 04:39:57 +00:00
|
|
|
$db->tableName( "$wgDBname.page" ),
|
|
|
|
|
"Correct full table name"
|
|
|
|
|
);
|
|
|
|
|
$this->assertEquals(
|
2016-10-27 05:44:08 +00:00
|
|
|
$this->quoteTable( $db, 'nice_db' ) . '.' . $this->quoteTable( $db, 'page' ),
|
2016-09-17 04:39:57 +00:00
|
|
|
$db->tableName( 'nice_db.page' ),
|
|
|
|
|
"Correct full table name"
|
|
|
|
|
);
|
|
|
|
|
|
2022-03-03 12:33:24 +00:00
|
|
|
unset( $db );
|
2018-01-12 21:44:12 +00:00
|
|
|
|
2018-11-20 23:34:11 +00:00
|
|
|
$factory->setLocalDomainPrefix( 'my_' );
|
2022-03-03 12:33:24 +00:00
|
|
|
$db = $lb->getConnection( DB_PRIMARY ); // local domain connection
|
2018-01-12 21:44:12 +00:00
|
|
|
|
2018-04-13 08:39:22 +00:00
|
|
|
$this->assertEquals( $wgDBname, $db->getDBname() );
|
2016-09-17 04:39:57 +00:00
|
|
|
$this->assertEquals(
|
2018-01-12 21:44:12 +00:00
|
|
|
"$wgDBname-my_",
|
2016-09-17 04:39:57 +00:00
|
|
|
$db->getDomainID()
|
|
|
|
|
);
|
|
|
|
|
$this->assertEquals(
|
2016-10-27 05:44:08 +00:00
|
|
|
$this->quoteTable( $db, 'my_page' ),
|
2016-09-17 04:39:57 +00:00
|
|
|
$db->tableName( 'page' ),
|
|
|
|
|
"Correct full table name"
|
|
|
|
|
);
|
|
|
|
|
$this->assertEquals(
|
2016-10-27 05:44:08 +00:00
|
|
|
$this->quoteTable( $db, 'other_nice_db' ) . '.' . $this->quoteTable( $db, 'page' ),
|
2016-09-17 04:39:57 +00:00
|
|
|
$db->tableName( 'other_nice_db.page' ),
|
|
|
|
|
"Correct full table name"
|
|
|
|
|
);
|
|
|
|
|
|
2022-03-16 19:35:31 +00:00
|
|
|
$factory->closeAll( __METHOD__ );
|
2016-09-17 04:39:57 +00:00
|
|
|
$factory->destroy();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testTrickyDomain() {
|
2018-04-13 08:39:22 +00:00
|
|
|
global $wgDBname;
|
|
|
|
|
|
2021-04-29 02:37:11 +00:00
|
|
|
if ( wfGetDB( DB_PRIMARY )->databasesAreIndependent() ) {
|
2018-04-13 08:39:22 +00:00
|
|
|
self::markTestSkipped( "Skipping tests about selecting DBs: not applicable" );
|
|
|
|
|
return;
|
2016-10-27 05:44:08 +00:00
|
|
|
}
|
|
|
|
|
|
2018-01-12 21:44:12 +00:00
|
|
|
$dbname = 'unittest-domain'; // explodes if DB is selected
|
2016-09-17 04:39:57 +00:00
|
|
|
$factory = $this->newLBFactoryMulti(
|
2018-01-12 21:44:12 +00:00
|
|
|
[ 'localDomain' => ( new DatabaseDomain( $dbname, null, '' ) )->getId() ],
|
2018-02-09 02:48:38 +00:00
|
|
|
[
|
2019-03-22 01:12:44 +00:00
|
|
|
'dbname' => 'do_not_select_me' // explodes if DB is selected
|
2018-02-09 02:48:38 +00:00
|
|
|
]
|
2016-10-27 05:44:08 +00:00
|
|
|
);
|
2016-09-17 04:39:57 +00:00
|
|
|
$lb = $factory->getMainLB();
|
2019-02-12 12:01:26 +00:00
|
|
|
/** @var IMaintainableDatabase $db */
|
2021-04-29 02:37:11 +00:00
|
|
|
$db = $lb->getConnection( DB_PRIMARY, [], $lb::DOMAIN_ANY );
|
2016-09-17 04:39:57 +00:00
|
|
|
|
2019-09-17 14:28:35 +00:00
|
|
|
$this->assertSame( '', $db->getDomainID(), "Null domain used" );
|
2016-09-17 04:39:57 +00:00
|
|
|
|
|
|
|
|
$this->assertEquals(
|
2016-10-27 05:44:08 +00:00
|
|
|
$this->quoteTable( $db, 'page' ),
|
2016-09-17 04:39:57 +00:00
|
|
|
$db->tableName( 'page' ),
|
|
|
|
|
"Correct full table name"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$this->assertEquals(
|
2016-10-27 05:44:08 +00:00
|
|
|
$this->quoteTable( $db, $dbname ) . '.' . $this->quoteTable( $db, 'page' ),
|
2016-09-17 04:39:57 +00:00
|
|
|
$db->tableName( "$dbname.page" ),
|
|
|
|
|
"Correct full table name"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$this->assertEquals(
|
2016-10-27 05:44:08 +00:00
|
|
|
$this->quoteTable( $db, 'nice_db' ) . '.' . $this->quoteTable( $db, 'page' ),
|
2016-09-17 04:39:57 +00:00
|
|
|
$db->tableName( 'nice_db.page' ),
|
|
|
|
|
"Correct full table name"
|
|
|
|
|
);
|
|
|
|
|
|
2022-03-03 12:33:24 +00:00
|
|
|
unset( $db );
|
2018-01-12 21:44:12 +00:00
|
|
|
|
2018-11-20 23:34:11 +00:00
|
|
|
$factory->setLocalDomainPrefix( 'my_' );
|
2021-04-29 02:37:11 +00:00
|
|
|
$db = $lb->getConnection( DB_PRIMARY, [], "$wgDBname-my_" );
|
2016-09-17 04:39:57 +00:00
|
|
|
|
|
|
|
|
$this->assertEquals(
|
2016-10-27 05:44:08 +00:00
|
|
|
$this->quoteTable( $db, 'my_page' ),
|
2016-09-17 04:39:57 +00:00
|
|
|
$db->tableName( 'page' ),
|
|
|
|
|
"Correct full table name"
|
|
|
|
|
);
|
|
|
|
|
$this->assertEquals(
|
2016-10-27 05:44:08 +00:00
|
|
|
$this->quoteTable( $db, 'other_nice_db' ) . '.' . $this->quoteTable( $db, 'page' ),
|
2016-09-17 04:39:57 +00:00
|
|
|
$db->tableName( 'other_nice_db.page' ),
|
|
|
|
|
"Correct full table name"
|
|
|
|
|
);
|
|
|
|
|
$this->assertEquals(
|
2016-10-27 05:44:08 +00:00
|
|
|
$this->quoteTable( $db, 'garbage-db' ) . '.' . $this->quoteTable( $db, 'page' ),
|
2016-09-17 04:39:57 +00:00
|
|
|
$db->tableName( 'garbage-db.page' ),
|
|
|
|
|
"Correct full table name"
|
|
|
|
|
);
|
|
|
|
|
|
2018-04-13 08:39:22 +00:00
|
|
|
$lb->reuseConnection( $db ); // don't care
|
|
|
|
|
|
2022-03-16 19:35:31 +00:00
|
|
|
$factory->closeAll( __METHOD__ );
|
2018-04-13 08:39:22 +00:00
|
|
|
$factory->destroy();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testInvalidSelectDB() {
|
2021-04-29 02:37:11 +00:00
|
|
|
if ( wfGetDB( DB_PRIMARY )->databasesAreIndependent() ) {
|
2019-03-22 01:12:44 +00:00
|
|
|
$this->markTestSkipped( "Not applicable per databasesAreIndependent()" );
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-13 08:39:22 +00:00
|
|
|
$dbname = 'unittest-domain'; // explodes if DB is selected
|
|
|
|
|
$factory = $this->newLBFactoryMulti(
|
|
|
|
|
[ 'localDomain' => ( new DatabaseDomain( $dbname, null, '' ) )->getId() ],
|
|
|
|
|
[
|
2019-03-22 01:12:44 +00:00
|
|
|
'dbname' => 'do_not_select_me' // explodes if DB is selected
|
2018-04-13 08:39:22 +00:00
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
$lb = $factory->getMainLB();
|
2019-02-12 12:01:26 +00:00
|
|
|
/** @var IDatabase $db */
|
2021-04-29 02:37:11 +00:00
|
|
|
$db = $lb->getConnection( DB_PRIMARY, [], $lb::DOMAIN_ANY );
|
2018-04-13 08:39:22 +00:00
|
|
|
|
2022-03-03 12:33:24 +00:00
|
|
|
$this->expectException( \Wikimedia\Rdbms\DBUnexpectedError::class );
|
|
|
|
|
$db->selectDomain( 'garbagedb' );
|
2019-03-22 01:12:44 +00:00
|
|
|
}
|
|
|
|
|
|
2019-07-20 19:42:14 +00:00
|
|
|
public function testInvalidSelectDBIndependent() {
|
2019-03-22 01:12:44 +00:00
|
|
|
$dbname = 'unittest-domain'; // explodes if DB is selected
|
|
|
|
|
$factory = $this->newLBFactoryMulti(
|
|
|
|
|
[ 'localDomain' => ( new DatabaseDomain( $dbname, null, '' ) )->getId() ],
|
|
|
|
|
[
|
2019-07-20 19:42:14 +00:00
|
|
|
// Explodes with SQLite and Postgres during open/USE
|
|
|
|
|
'dbname' => 'bad_dir/do_not_select_me'
|
2019-03-22 01:12:44 +00:00
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
$lb = $factory->getMainLB();
|
|
|
|
|
|
2021-06-20 18:30:15 +00:00
|
|
|
// FIXME: this should probably be lower (T235311)
|
2019-10-11 22:22:26 +00:00
|
|
|
$this->expectException( \Wikimedia\Rdbms\DBConnectionError::class );
|
2019-07-14 22:43:26 +00:00
|
|
|
if ( !$factory->getMainLB()->getServerAttributes( 0 )[Database::ATTR_DB_IS_FILE] ) {
|
|
|
|
|
$this->markTestSkipped( "Not applicable per ATTR_DB_IS_FILE" );
|
2017-05-10 01:27:28 +00:00
|
|
|
}
|
2019-03-22 01:12:44 +00:00
|
|
|
|
|
|
|
|
/** @var IDatabase $db */
|
2022-03-03 12:33:24 +00:00
|
|
|
$this->assertNotNull( $lb->getConnectionInternal( DB_PRIMARY, [], $lb::DOMAIN_ANY ) );
|
2016-09-17 04:39:57 +00:00
|
|
|
}
|
2016-10-27 05:44:08 +00:00
|
|
|
|
2019-07-20 19:42:14 +00:00
|
|
|
public function testInvalidSelectDBIndependent2() {
|
2019-03-22 01:12:44 +00:00
|
|
|
$dbname = 'unittest-domain'; // explodes if DB is selected
|
|
|
|
|
$factory = $this->newLBFactoryMulti(
|
|
|
|
|
[ 'localDomain' => ( new DatabaseDomain( $dbname, null, '' ) )->getId() ],
|
|
|
|
|
[
|
2019-07-20 19:42:14 +00:00
|
|
|
// Explodes with SQLite and Postgres during open/USE
|
|
|
|
|
'dbname' => 'bad_dir/do_not_select_me'
|
2019-03-22 01:12:44 +00:00
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
$lb = $factory->getMainLB();
|
|
|
|
|
|
2021-06-20 18:30:15 +00:00
|
|
|
// FIXME: this should probably be lower (T235311)
|
2019-10-11 22:22:26 +00:00
|
|
|
$this->expectException( \Wikimedia\Rdbms\DBExpectedError::class );
|
2021-04-29 02:37:11 +00:00
|
|
|
if ( !$lb->getConnection( DB_PRIMARY )->databasesAreIndependent() ) {
|
2019-03-22 01:12:44 +00:00
|
|
|
$this->markTestSkipped( "Not applicable per databasesAreIndependent()" );
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-03 12:33:24 +00:00
|
|
|
$db = $lb->getConnectionInternal( DB_PRIMARY );
|
2019-07-14 22:43:26 +00:00
|
|
|
$db->selectDomain( 'garbage-db' );
|
2019-03-22 01:12:44 +00:00
|
|
|
}
|
|
|
|
|
|
2018-11-19 08:13:30 +00:00
|
|
|
public function testRedefineLocalDomain() {
|
|
|
|
|
global $wgDBname;
|
|
|
|
|
|
2021-04-29 02:37:11 +00:00
|
|
|
if ( wfGetDB( DB_PRIMARY )->databasesAreIndependent() ) {
|
2018-11-19 08:13:30 +00:00
|
|
|
self::markTestSkipped( "Skipping tests about selecting DBs: not applicable" );
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$factory = $this->newLBFactoryMulti(
|
|
|
|
|
[],
|
|
|
|
|
[]
|
|
|
|
|
);
|
|
|
|
|
$lb = $factory->getMainLB();
|
|
|
|
|
|
2021-04-29 02:37:11 +00:00
|
|
|
$conn1 = $lb->getConnectionRef( DB_PRIMARY );
|
2018-11-19 08:13:30 +00:00
|
|
|
$this->assertEquals(
|
2021-12-21 00:47:14 +00:00
|
|
|
WikiMap::getCurrentWikiId(),
|
2018-11-19 08:13:30 +00:00
|
|
|
$conn1->getDomainID()
|
|
|
|
|
);
|
|
|
|
|
unset( $conn1 );
|
|
|
|
|
|
2019-03-25 14:51:45 +00:00
|
|
|
$factory->redefineLocalDomain( 'somedb-prefix_' );
|
|
|
|
|
$this->assertEquals( 'somedb-prefix_', $factory->getLocalDomainID() );
|
2018-11-19 08:13:30 +00:00
|
|
|
|
2019-03-25 14:51:45 +00:00
|
|
|
$domain = new DatabaseDomain( $wgDBname, null, 'pref_' );
|
2018-11-19 08:13:30 +00:00
|
|
|
$factory->redefineLocalDomain( $domain );
|
|
|
|
|
|
2022-04-26 03:54:59 +00:00
|
|
|
/** @var LoadBalancer $lbWrapper */
|
|
|
|
|
$lbWrapper = TestingAccessWrapper::newFromObject( $lb );
|
|
|
|
|
$n = iterator_count( $lbWrapper->getOpenConnections() );
|
2019-09-17 14:31:49 +00:00
|
|
|
$this->assertSame( 0, $n, "Connections closed" );
|
2018-11-19 08:13:30 +00:00
|
|
|
|
2021-04-29 02:37:11 +00:00
|
|
|
$conn2 = $lb->getConnectionRef( DB_PRIMARY );
|
2018-11-19 08:13:30 +00:00
|
|
|
$this->assertEquals(
|
|
|
|
|
$domain->getId(),
|
|
|
|
|
$conn2->getDomainID()
|
|
|
|
|
);
|
|
|
|
|
unset( $conn2 );
|
|
|
|
|
|
2022-03-16 19:35:31 +00:00
|
|
|
$factory->closeAll( __METHOD__ );
|
2018-11-19 08:13:30 +00:00
|
|
|
$factory->destroy();
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-21 09:35:36 +00:00
|
|
|
private function quoteTable( IReadableDatabase $db, $table ) {
|
2016-10-27 05:44:08 +00:00
|
|
|
if ( $db->getType() === 'sqlite' ) {
|
|
|
|
|
return $table;
|
|
|
|
|
} else {
|
|
|
|
|
return $db->addIdentifierQuotes( $table );
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-10 23:18:19 +00:00
|
|
|
|
|
|
|
|
public function testCPPosIndexCookieValues() {
|
|
|
|
|
$time = 1526522031;
|
2018-05-25 07:04:23 +00:00
|
|
|
$agentId = md5( 'Ramsey\'s Loyal Presa Canario' );
|
|
|
|
|
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
'3@542#c47dcfb0566e7d7bc110a6128a45c93a',
|
|
|
|
|
LBFactory::makeCookieValueFromCPIndex( 3, 542, $agentId )
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$lbFactory = $this->newLBFactoryMulti();
|
|
|
|
|
$lbFactory->setRequestInfo( [ 'IPAddress' => '10.64.24.52', 'UserAgent' => 'meow' ] );
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
'1@542#c47dcfb0566e7d7bc110a6128a45c93a',
|
|
|
|
|
LBFactory::makeCookieValueFromCPIndex( 1, 542, $agentId )
|
|
|
|
|
);
|
2018-06-11 21:52:37 +00:00
|
|
|
|
2018-05-10 23:18:19 +00:00
|
|
|
$this->assertSame(
|
2018-06-11 21:52:37 +00:00
|
|
|
null,
|
|
|
|
|
LBFactory::getCPInfoFromCookieValue( "5#$agentId", $time - 10 )['index'],
|
2018-05-25 07:04:23 +00:00
|
|
|
'No time set'
|
2018-05-10 23:18:19 +00:00
|
|
|
);
|
|
|
|
|
$this->assertSame(
|
|
|
|
|
null,
|
2018-06-11 21:52:37 +00:00
|
|
|
LBFactory::getCPInfoFromCookieValue( "5@$time", $time - 10 )['index'],
|
|
|
|
|
'No agent set'
|
|
|
|
|
);
|
|
|
|
|
$this->assertSame(
|
|
|
|
|
null,
|
|
|
|
|
LBFactory::getCPInfoFromCookieValue( "0@$time#$agentId", $time - 10 )['index'],
|
2018-05-25 07:04:23 +00:00
|
|
|
'Bad index'
|
2018-05-10 23:18:19 +00:00
|
|
|
);
|
2018-05-25 07:04:23 +00:00
|
|
|
|
2018-05-10 23:18:19 +00:00
|
|
|
$this->assertSame(
|
|
|
|
|
2,
|
2018-06-11 21:52:37 +00:00
|
|
|
LBFactory::getCPInfoFromCookieValue( "2@$time#$agentId", $time - 10 )['index'],
|
2018-05-25 07:04:23 +00:00
|
|
|
'Fresh'
|
2018-05-10 23:18:19 +00:00
|
|
|
);
|
|
|
|
|
$this->assertSame(
|
|
|
|
|
2,
|
2018-06-11 21:52:37 +00:00
|
|
|
LBFactory::getCPInfoFromCookieValue( "2@$time#$agentId", $time + 9 - 10 )['index'],
|
2018-05-25 07:04:23 +00:00
|
|
|
'Almost stale'
|
|
|
|
|
);
|
|
|
|
|
$this->assertSame(
|
|
|
|
|
null,
|
2018-06-11 21:52:37 +00:00
|
|
|
LBFactory::getCPInfoFromCookieValue( "0@$time#$agentId", $time + 9 - 10 )['index'],
|
2018-05-25 07:04:23 +00:00
|
|
|
'Almost stale; bad index'
|
2018-05-10 23:18:19 +00:00
|
|
|
);
|
|
|
|
|
$this->assertSame(
|
|
|
|
|
null,
|
2018-06-11 21:52:37 +00:00
|
|
|
LBFactory::getCPInfoFromCookieValue( "2@$time#$agentId", $time + 11 - 10 )['index'],
|
2018-05-25 07:04:23 +00:00
|
|
|
'Stale'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$this->assertSame(
|
|
|
|
|
$agentId,
|
|
|
|
|
LBFactory::getCPInfoFromCookieValue( "5@$time#$agentId", $time - 10 )['clientId'],
|
|
|
|
|
'Live (client ID)'
|
2018-05-10 23:18:19 +00:00
|
|
|
);
|
|
|
|
|
$this->assertSame(
|
|
|
|
|
null,
|
2018-06-11 21:52:37 +00:00
|
|
|
LBFactory::getCPInfoFromCookieValue( "5@$time#$agentId", $time + 11 - 10 )['clientId'],
|
2018-05-25 07:04:23 +00:00
|
|
|
'Stale (client ID)'
|
2018-05-10 23:18:19 +00:00
|
|
|
);
|
|
|
|
|
}
|
2019-07-03 22:02:41 +00:00
|
|
|
|
|
|
|
|
public function testSetDomainAliases() {
|
|
|
|
|
$lb = $this->newLBFactoryMulti();
|
|
|
|
|
$origDomain = $lb->getLocalDomainID();
|
|
|
|
|
|
|
|
|
|
$this->assertEquals( $origDomain, $lb->resolveDomainID( false ) );
|
|
|
|
|
$this->assertEquals( "db-prefix_", $lb->resolveDomainID( "db-prefix_" ) );
|
|
|
|
|
|
|
|
|
|
$lb->setDomainAliases( [
|
|
|
|
|
'alias-db' => 'realdb',
|
|
|
|
|
'alias-db-prefix_' => 'realdb-realprefix_'
|
|
|
|
|
] );
|
|
|
|
|
|
|
|
|
|
$this->assertEquals( 'realdb', $lb->resolveDomainID( 'alias-db' ) );
|
|
|
|
|
$this->assertEquals( "realdb-realprefix_", $lb->resolveDomainID( "alias-db-prefix_" ) );
|
|
|
|
|
}
|
2020-05-29 00:34:45 +00:00
|
|
|
|
|
|
|
|
public function testGetChronologyProtectorTouched() {
|
|
|
|
|
$store = new HashBagOStuff;
|
|
|
|
|
$lbFactory = $this->newLBFactoryMulti( [
|
2021-02-05 04:18:47 +00:00
|
|
|
'cpStash' => $store,
|
|
|
|
|
'cliMode' => false
|
2020-05-29 00:34:45 +00:00
|
|
|
] );
|
|
|
|
|
$lbFactory->setRequestInfo( [ 'ChronologyClientId' => 'ii' ] );
|
2021-02-05 04:18:47 +00:00
|
|
|
|
2022-08-02 21:30:01 +00:00
|
|
|
// 2019-02-05T05:03:20Z
|
|
|
|
|
$mockWallClock = 1549343000.0;
|
2021-02-05 04:18:47 +00:00
|
|
|
$priorTime = $mockWallClock; // reference time
|
|
|
|
|
$lbFactory->setMockTime( $mockWallClock );
|
|
|
|
|
|
2022-08-02 21:30:01 +00:00
|
|
|
$lbWrap = TestingAccessWrapper::newFromObject( $lbFactory );
|
|
|
|
|
$cpWrap = TestingAccessWrapper::newFromObject( $lbWrap->getChronologyProtector() );
|
|
|
|
|
$cpWrap->store->set(
|
|
|
|
|
$cpWrap->key,
|
|
|
|
|
$cpWrap->mergePositions(
|
|
|
|
|
false,
|
|
|
|
|
[],
|
|
|
|
|
[
|
|
|
|
|
$lbFactory::CLUSTER_MAIN_DEFAULT => $priorTime
|
|
|
|
|
]
|
|
|
|
|
),
|
2021-02-05 04:18:47 +00:00
|
|
|
3600
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$mockWallClock += 1.0;
|
2020-05-29 00:34:45 +00:00
|
|
|
$touched = $lbFactory->getChronologyProtectorTouched();
|
2021-02-05 04:18:47 +00:00
|
|
|
$this->assertEquals( $priorTime, $touched );
|
2020-05-29 00:34:45 +00:00
|
|
|
}
|
2022-05-24 13:50:02 +00:00
|
|
|
|
2022-12-22 03:19:19 +00:00
|
|
|
public function testReconfigureWithOneReplica() {
|
2022-05-24 13:50:02 +00:00
|
|
|
$primaryConfig = $this->getPrimaryServerConfig();
|
2022-08-31 16:21:40 +00:00
|
|
|
$fakeReplica = [ 'load' => 100, 'serverName' => 'replica' ] + $primaryConfig;
|
2022-05-24 13:50:02 +00:00
|
|
|
|
|
|
|
|
$conf = [ 'servers' => [
|
|
|
|
|
$primaryConfig,
|
|
|
|
|
$fakeReplica
|
|
|
|
|
] ];
|
|
|
|
|
|
|
|
|
|
// Configure an LBFactory with one replica
|
|
|
|
|
$factory = new LBFactorySimple( $conf );
|
|
|
|
|
$lb = $factory->getMainLB();
|
|
|
|
|
$this->assertSame( 2, $lb->getServerCount() );
|
|
|
|
|
|
|
|
|
|
$con = $lb->getConnectionInternal( DB_REPLICA );
|
|
|
|
|
$ref = $lb->getConnection( DB_REPLICA );
|
|
|
|
|
|
|
|
|
|
// Call reconfigure with the same config, should have no effect
|
2022-08-31 16:21:40 +00:00
|
|
|
$factory->reconfigure( $conf );
|
2022-05-24 13:50:02 +00:00
|
|
|
$this->assertSame( 2, $lb->getServerCount() );
|
|
|
|
|
$this->assertTrue( $con->isOpen() );
|
|
|
|
|
$this->assertTrue( $ref->isOpen() );
|
|
|
|
|
|
|
|
|
|
// Call reconfigure with empty config, should have no effect
|
2022-08-31 16:21:40 +00:00
|
|
|
$factory->reconfigure( [] );
|
2022-05-24 13:50:02 +00:00
|
|
|
$this->assertSame( 2, $lb->getServerCount() );
|
|
|
|
|
$this->assertTrue( $con->isOpen() );
|
|
|
|
|
$this->assertTrue( $ref->isOpen() );
|
|
|
|
|
|
|
|
|
|
// Reconfigure the LBFactory to only have a single server.
|
|
|
|
|
$conf['servers'] = [ $this->getPrimaryServerConfig() ];
|
2022-08-31 16:21:40 +00:00
|
|
|
$factory->reconfigure( $conf );
|
2022-05-24 13:50:02 +00:00
|
|
|
|
|
|
|
|
// The LoadBalancer should have been reconfigured automatically.
|
|
|
|
|
$this->assertSame( 1, $lb->getServerCount() );
|
|
|
|
|
|
|
|
|
|
// 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() );
|
|
|
|
|
$this->assertSame( IDatabase::ROLE_STREAMING_MASTER, $ref->getTopologyRole() );
|
|
|
|
|
|
2022-12-22 03:19:19 +00:00
|
|
|
// The old connection should have been closed by DBConnRef.
|
|
|
|
|
$this->assertFalse( $con->isOpen() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testReconfigureWithThreeReplicas() {
|
|
|
|
|
$primaryConfig = $this->getPrimaryServerConfig();
|
|
|
|
|
$replica1Config = [ 'serverName' => 'db2', 'load' => 0 ] + $primaryConfig;
|
|
|
|
|
$replica2Config = [ 'serverName' => 'db3', 'load' => 1 ] + $primaryConfig;
|
|
|
|
|
$replica3Config = [ 'serverName' => 'db4', 'load' => 1 ] + $primaryConfig;
|
|
|
|
|
|
|
|
|
|
$conf = [ 'servers' => [
|
|
|
|
|
$primaryConfig,
|
|
|
|
|
$replica1Config,
|
|
|
|
|
$replica2Config,
|
|
|
|
|
$replica3Config
|
|
|
|
|
] ];
|
|
|
|
|
|
|
|
|
|
// Configure an LBFactory with two replicas
|
|
|
|
|
$factory = new LBFactorySimple( $conf );
|
|
|
|
|
$lb = $factory->getMainLB();
|
|
|
|
|
$this->assertSame( 4, $lb->getServerCount() );
|
|
|
|
|
$this->assertSame( 'db1', $lb->getServerName( 0 ) );
|
|
|
|
|
$this->assertSame( 'db2', $lb->getServerName( 1 ) );
|
|
|
|
|
$this->assertSame( 'db3', $lb->getServerName( 2 ) );
|
|
|
|
|
$this->assertSame( 'db4', $lb->getServerName( 3 ) );
|
|
|
|
|
|
|
|
|
|
$con = $lb->getConnectionInternal( DB_REPLICA );
|
|
|
|
|
$ref = $lb->getConnection( DB_REPLICA );
|
|
|
|
|
|
|
|
|
|
// Call reconfigure with the same config, should have no effect
|
|
|
|
|
$factory->reconfigure( $conf );
|
|
|
|
|
$this->assertSame( 4, $lb->getServerCount() );
|
|
|
|
|
$this->assertSame( 'db1', $lb->getServerName( 0 ) );
|
|
|
|
|
$this->assertSame( 'db2', $lb->getServerName( 1 ) );
|
|
|
|
|
$this->assertSame( 'db3', $lb->getServerName( 2 ) );
|
|
|
|
|
$this->assertSame( 'db4', $lb->getServerName( 3 ) );
|
|
|
|
|
$this->assertTrue( $con->isOpen() );
|
|
|
|
|
$this->assertTrue( $ref->isOpen() );
|
|
|
|
|
|
|
|
|
|
// Call reconfigure with empty config, should have no effect
|
|
|
|
|
$factory->reconfigure( [] );
|
|
|
|
|
$this->assertSame( 4, $lb->getServerCount() );
|
|
|
|
|
$this->assertSame( 'db1', $lb->getServerName( 0 ) );
|
|
|
|
|
$this->assertSame( 'db2', $lb->getServerName( 1 ) );
|
|
|
|
|
$this->assertSame( 'db3', $lb->getServerName( 2 ) );
|
|
|
|
|
$this->assertSame( 'db4', $lb->getServerName( 3 ) );
|
|
|
|
|
$this->assertTrue( $con->isOpen() );
|
|
|
|
|
$this->assertTrue( $ref->isOpen() );
|
|
|
|
|
|
|
|
|
|
// Reconfigure the LBFactory to only have a two servers (server indexes shifted).
|
|
|
|
|
$conf['servers'] = [ $primaryConfig, $replica2Config, $replica3Config ];
|
|
|
|
|
$factory->reconfigure( $conf );
|
|
|
|
|
// The LoadBalancer should have been reconfigured automatically.
|
|
|
|
|
$this->assertSame( 3, $lb->getServerCount() );
|
|
|
|
|
$this->assertSame( 'db1', $lb->getServerName( 0 ) );
|
|
|
|
|
$this->assertSame( false, $lb->getServerInfo( 1 ) );
|
|
|
|
|
$this->assertSame( 'db3', $lb->getServerName( 2 ) );
|
|
|
|
|
$this->assertSame( 'db4', $lb->getServerName( 3 ) );
|
|
|
|
|
// 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() );
|
|
|
|
|
$this->assertSame( IDatabase::ROLE_STREAMING_REPLICA, $ref->getTopologyRole() );
|
|
|
|
|
// The old connection should have been closed by DBConnRef.
|
2022-05-24 13:50:02 +00:00
|
|
|
$this->assertFalse( $con->isOpen() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testAutoReconfigure() {
|
|
|
|
|
$primaryConfig = $this->getPrimaryServerConfig();
|
2022-08-31 16:21:40 +00:00
|
|
|
$fakeReplica = [ 'load' => 100, 'serverName' => 'replica1' ] + $primaryConfig;
|
2022-05-24 13:50:02 +00:00
|
|
|
|
|
|
|
|
$conf = [
|
|
|
|
|
'servers' => [
|
|
|
|
|
$primaryConfig,
|
|
|
|
|
$fakeReplica
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// The config callback should return $conf, reflecting changes
|
|
|
|
|
// made to the local variable.
|
|
|
|
|
$conf['configCallback'] = static function () use ( &$conf ) {
|
2022-08-31 16:21:40 +00:00
|
|
|
static $calls = 0;
|
|
|
|
|
$calls++;
|
|
|
|
|
if ( $calls == 1 ) {
|
|
|
|
|
return $conf;
|
|
|
|
|
} else {
|
|
|
|
|
unset( $conf['servers'][1] );
|
|
|
|
|
return $conf;
|
|
|
|
|
}
|
2022-05-24 13:50:02 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Configure an LBFactory with one replica
|
|
|
|
|
$factory = new LBFactorySimple( $conf );
|
|
|
|
|
|
|
|
|
|
$lb = $factory->getMainLB();
|
|
|
|
|
$this->assertSame( 2, $lb->getServerCount() );
|
|
|
|
|
|
|
|
|
|
$con = $lb->getConnectionInternal( DB_REPLICA );
|
|
|
|
|
$ref = $lb->getConnection( DB_REPLICA );
|
|
|
|
|
|
|
|
|
|
// Nothing changed, autoReconfigure() should do nothing.
|
2022-08-31 16:21:40 +00:00
|
|
|
$factory->autoReconfigure();
|
2022-05-24 13:50:02 +00:00
|
|
|
|
|
|
|
|
$this->assertSame( 2, $lb->getServerCount() );
|
|
|
|
|
$this->assertTrue( $con->isOpen() );
|
|
|
|
|
$this->assertTrue( $ref->isOpen() );
|
|
|
|
|
|
|
|
|
|
// Now autoReconfigure() should detect the change and reconfigure all LoadBalancers.
|
2022-08-31 16:21:40 +00:00
|
|
|
$factory->autoReconfigure();
|
2022-05-24 13:50:02 +00:00
|
|
|
|
|
|
|
|
// The LoadBalancer should have been reconfigured now.
|
|
|
|
|
$this->assertSame( 1, $lb->getServerCount() );
|
|
|
|
|
|
|
|
|
|
// 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() );
|
|
|
|
|
$this->assertSame( IDatabase::ROLE_STREAMING_MASTER, $ref->getTopologyRole() );
|
|
|
|
|
|
|
|
|
|
// The old connection should have been called by DBConnRef.
|
|
|
|
|
$this->assertFalse( $con->isOpen() );
|
|
|
|
|
}
|
2013-11-27 10:17:06 +00:00
|
|
|
}
|