const ROUND_BEGINNING = 'within-begin';
const ROUND_COMMITTING = 'within-commit';
const ROUND_ROLLING_BACK = 'within-rollback';
+ const ROUND_COMMIT_CALLBACKS = 'within-commit-callbacks';
+ const ROUND_ROLLBACK_CALLBACKS = 'within-rollback-callbacks';
private static $loggerFields =
[ 'replLogger', 'connLogger', 'queryLogger', 'perfLogger' ];
// Actually perform the commit on all master DB connections and revert DBO_TRX
$this->forEachLBCallMethod( 'commitMasterChanges', [ $fname ] );
// Run all post-commit callbacks in a separate step
+ $this->trxRoundStage = self::ROUND_COMMIT_CALLBACKS;
$e = $this->executePostTransactionCallbacks();
$this->trxRoundStage = self::ROUND_CURSORY;
// Throw any last post-commit callback error
// Actually perform the rollback on all master DB connections and revert DBO_TRX
$this->forEachLBCallMethod( 'rollbackMasterChanges', [ $fname ] );
// Run all post-commit callbacks in a separate step
+ $this->trxRoundStage = self::ROUND_ROLLBACK_CALLBACKS;
$this->executePostTransactionCallbacks();
$this->trxRoundStage = self::ROUND_CURSORY;
}
* @return array
*/
final protected function baseLoadBalancerParams() {
+ if ( $this->trxRoundStage === self::ROUND_COMMIT_CALLBACKS ) {
+ $initStage = ILoadBalancer::STAGE_POSTCOMMIT_CALLBACKS;
+ } elseif ( $this->trxRoundStage === self::ROUND_ROLLBACK_CALLBACKS ) {
+ $initStage = ILoadBalancer::STAGE_POSTROLLBACK_CALLBACKS;
+ } else {
+ $initStage = null;
+ }
+
return [
'localDomain' => $this->localDomain,
'readOnlyReason' => $this->readOnlyReason,
// Defer ChronologyProtector construction in case setRequestInfo() ends up
// being called later (but before the first connection attempt) (T192611)
$this->getChronologyProtector()->initLB( $lb );
- }
+ },
+ 'roundStage' => $initStage
];
}
$lb->closeAll();
}
- public function testLBFactoryMulti() {
- global $wgDBserver, $wgDBname, $wgDBuser, $wgDBpassword, $wgDBtype, $wgSQLiteDataDir;
+ public function testLBFactoryMultiConns() {
+ $factory = $this->newLBFactoryMultiLBs();
- $factory = new LBFactoryMulti( [
- 'sectionsByDB' => [
- 's1wiki' => 's1',
- ],
- 'sectionLoads' => [
- 's1' => [
- 'test-db3' => 0,
- 'test-db4' => 100,
- ],
- 'DEFAULT' => [
- 'test-db1' => 0,
- 'test-db2' => 100,
- ]
- ],
- 'serverTemplate' => [
- '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
- ],
- 'loadMonitorClass' => LoadMonitorNull::class
- ] );
- $lb = $factory->getMainLB();
-
- $dbw = $lb->getConnection( DB_MASTER );
+ $dbw = $factory->getMainLB()->getConnection( DB_MASTER );
$this->assertTrue( $dbw->getLBInfo( 'master' ), 'master shows as master' );
- $dbr = $lb->getConnection( DB_REPLICA );
+ $dbr = $factory->getMainLB()->getConnection( DB_REPLICA );
$this->assertTrue( $dbr->getLBInfo( 'replica' ), 'slave shows as slave' );
- // Test that LoadBalancer instances made during commitMasterChanges() do not throw
- // DBTransactionError due to transaction ROUND_* stages being mismatched.
+ // Destructor should trigger without round stage errors
+ unset( $factory );
+ }
+
+ public function testLBFactoryMultiRoundCallbacks() {
+ $called = 0;
+ $countLBsFunc = function ( LBFactoryMulti $factory ) {
+ $count = 0;
+ $factory->forEachLB( function () use ( &$count ) {
+ ++$count;
+ } );
+
+ return $count;
+ };
+
+ $factory = $this->newLBFactoryMultiLBs();
+ $this->assertEquals( 0, $countLBsFunc( $factory ) );
+ $dbw = $factory->getMainLB()->getConnection( DB_MASTER );
+ $this->assertEquals( 1, $countLBsFunc( $factory ) );
+ // Test that LoadBalancer instances made during pre-commit callbacks in do not
+ // throw DBTransactionError due to transaction ROUND_* stages being mismatched.
$factory->beginMasterChanges( __METHOD__ );
- $dbw->onTransactionPreCommitOrIdle( function () use ( $factory ) {
+ $dbw->onTransactionPreCommitOrIdle( function () use ( $factory, &$called ) {
+ ++$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.
$factory->getMainLB( 's1wiki' )->getConnection( DB_MASTER );
} );
$factory->commitMasterChanges( __METHOD__ );
+ $this->assertEquals( 1, $called );
+ $this->assertEquals( 2, $countLBsFunc( $factory ) );
+ $factory->shutdown();
+ $factory->closeAll();
- $count = 0;
- $factory->forEachLB( function () use ( &$count ) {
- ++$count;
+ $called = 0;
+ $factory = $this->newLBFactoryMultiLBs();
+ $this->assertEquals( 0, $countLBsFunc( $factory ) );
+ $dbw = $factory->getMainLB()->getConnection( DB_MASTER );
+ $this->assertEquals( 1, $countLBsFunc( $factory ) );
+ // 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.
+ $factory->beginMasterChanges( __METHOD__ );
+ $dbw->query( "SELECT 1 as t", __METHOD__ );
+ $dbw->onTransactionResolution( function () use ( $factory, &$called ) {
+ ++$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.
+ $factory->getMainLB( 's1wiki' )->getConnection( DB_MASTER );
} );
- $this->assertEquals( 2, $count );
+ $factory->commitMasterChanges( __METHOD__ );
+ $this->assertEquals( 1, $called );
+ $this->assertEquals( 2, $countLBsFunc( $factory ) );
+ $factory->shutdown();
+ $factory->closeAll();
+ $factory = $this->newLBFactoryMultiLBs();
+ $dbw = $factory->getMainLB()->getConnection( DB_MASTER );
// DBTransactionError should not be thrown
$ran = 0;
$dbw->onTransactionPreCommitOrIdle( function () use ( &$ran ) {
$factory->closeAll();
}
+ private function newLBFactoryMultiLBs() {
+ global $wgDBserver, $wgDBname, $wgDBuser, $wgDBpassword, $wgDBtype, $wgSQLiteDataDir;
+
+ return new LBFactoryMulti( [
+ 'sectionsByDB' => [
+ 's1wiki' => 's1',
+ ],
+ 'sectionLoads' => [
+ 's1' => [
+ 'test-db3' => 0,
+ 'test-db4' => 100,
+ ],
+ 'DEFAULT' => [
+ 'test-db1' => 0,
+ 'test-db2' => 100,
+ ]
+ ],
+ 'serverTemplate' => [
+ '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
+ ],
+ 'loadMonitorClass' => LoadMonitorNull::class
+ ] );
+ }
+
/**
* @covers \Wikimedia\Rdbms\ChronologyProtector
*/