This allows scheduling of updates that need to start their own transaction round. Specifically for cases where the ability to commit early is not enough (which is already possible via LBFactory getEmptyTransactionTicket and commitAndWaitForReplication). Change-Id: I0910587b61c8ddf825f91e92c2f93582cc7ebd80
336 lines
9.5 KiB
PHP
336 lines
9.5 KiB
PHP
<?php
|
|
|
|
use MediaWiki\MediaWikiServices;
|
|
|
|
class DeferredUpdatesTest extends MediaWikiTestCase {
|
|
|
|
/**
|
|
* @covers DeferredUpdates::addUpdate
|
|
* @covers DeferredUpdates::push
|
|
* @covers DeferredUpdates::doUpdates
|
|
* @covers DeferredUpdates::execute
|
|
* @covers DeferredUpdates::runUpdate
|
|
*/
|
|
public function testAddAndRun() {
|
|
$update = $this->getMockBuilder( DeferrableUpdate::class )
|
|
->setMethods( [ 'doUpdate' ] )->getMock();
|
|
$update->expects( $this->once() )->method( 'doUpdate' );
|
|
|
|
DeferredUpdates::addUpdate( $update );
|
|
DeferredUpdates::doUpdates();
|
|
}
|
|
|
|
/**
|
|
* @covers DeferredUpdates::addUpdate
|
|
* @covers DeferredUpdates::push
|
|
*/
|
|
public function testAddMergeable() {
|
|
$this->setMwGlobals( 'wgCommandLineMode', false );
|
|
|
|
$update1 = $this->getMockBuilder( MergeableUpdate::class )
|
|
->setMethods( [ 'merge', 'doUpdate' ] )->getMock();
|
|
$update1->expects( $this->once() )->method( 'merge' );
|
|
$update1->expects( $this->never() )->method( 'doUpdate' );
|
|
|
|
$update2 = $this->getMockBuilder( MergeableUpdate::class )
|
|
->setMethods( [ 'merge', 'doUpdate' ] )->getMock();
|
|
$update2->expects( $this->never() )->method( 'merge' );
|
|
$update2->expects( $this->never() )->method( 'doUpdate' );
|
|
|
|
DeferredUpdates::addUpdate( $update1 );
|
|
DeferredUpdates::addUpdate( $update2 );
|
|
}
|
|
|
|
/**
|
|
* @covers DeferredUpdates::addCallableUpdate
|
|
* @covers MWCallableUpdate::getOrigin
|
|
*/
|
|
public function testAddCallableUpdate() {
|
|
$this->setMwGlobals( 'wgCommandLineMode', true );
|
|
|
|
$ran = 0;
|
|
DeferredUpdates::addCallableUpdate( function () use ( &$ran ) {
|
|
$ran++;
|
|
} );
|
|
DeferredUpdates::doUpdates();
|
|
|
|
$this->assertSame( 1, $ran, 'Update ran' );
|
|
}
|
|
|
|
/**
|
|
* @covers DeferredUpdates::getPendingUpdates
|
|
* @covers DeferredUpdates::clearPendingUpdates
|
|
*/
|
|
public function testGetPendingUpdates() {
|
|
// Prevent updates from running
|
|
$this->setMwGlobals( 'wgCommandLineMode', false );
|
|
|
|
$pre = DeferredUpdates::PRESEND;
|
|
$post = DeferredUpdates::POSTSEND;
|
|
$all = DeferredUpdates::ALL;
|
|
|
|
$update = $this->getMock( DeferrableUpdate::class );
|
|
$update->expects( $this->never() )
|
|
->method( 'doUpdate' );
|
|
|
|
DeferredUpdates::addUpdate( $update, $pre );
|
|
$this->assertCount( 1, DeferredUpdates::getPendingUpdates( $pre ) );
|
|
$this->assertCount( 0, DeferredUpdates::getPendingUpdates( $post ) );
|
|
$this->assertCount( 1, DeferredUpdates::getPendingUpdates( $all ) );
|
|
$this->assertCount( 1, DeferredUpdates::getPendingUpdates() );
|
|
DeferredUpdates::clearPendingUpdates();
|
|
$this->assertCount( 0, DeferredUpdates::getPendingUpdates() );
|
|
|
|
DeferredUpdates::addUpdate( $update, $post );
|
|
$this->assertCount( 0, DeferredUpdates::getPendingUpdates( $pre ) );
|
|
$this->assertCount( 1, DeferredUpdates::getPendingUpdates( $post ) );
|
|
$this->assertCount( 1, DeferredUpdates::getPendingUpdates( $all ) );
|
|
$this->assertCount( 1, DeferredUpdates::getPendingUpdates() );
|
|
DeferredUpdates::clearPendingUpdates();
|
|
$this->assertCount( 0, DeferredUpdates::getPendingUpdates() );
|
|
}
|
|
|
|
/**
|
|
* @covers DeferredUpdates::doUpdates
|
|
* @covers DeferredUpdates::execute
|
|
* @covers DeferredUpdates::addUpdate
|
|
*/
|
|
public function testDoUpdatesWeb() {
|
|
$this->setMwGlobals( 'wgCommandLineMode', false );
|
|
|
|
$updates = [
|
|
'1' => "deferred update 1;\n",
|
|
'2' => "deferred update 2;\n",
|
|
'2-1' => "deferred update 1 within deferred update 2;\n",
|
|
'2-2' => "deferred update 2 within deferred update 2;\n",
|
|
'3' => "deferred update 3;\n",
|
|
'3-1' => "deferred update 1 within deferred update 3;\n",
|
|
'3-2' => "deferred update 2 within deferred update 3;\n",
|
|
'3-1-1' => "deferred update 1 within deferred update 1 within deferred update 3;\n",
|
|
'3-2-1' => "deferred update 1 within deferred update 2 with deferred update 3;\n",
|
|
];
|
|
DeferredUpdates::addCallableUpdate(
|
|
function () use ( $updates ) {
|
|
echo $updates['1'];
|
|
}
|
|
);
|
|
DeferredUpdates::addCallableUpdate(
|
|
function () use ( $updates ) {
|
|
echo $updates['2'];
|
|
DeferredUpdates::addCallableUpdate(
|
|
function () use ( $updates ) {
|
|
echo $updates['2-1'];
|
|
}
|
|
);
|
|
DeferredUpdates::addCallableUpdate(
|
|
function () use ( $updates ) {
|
|
echo $updates['2-2'];
|
|
}
|
|
);
|
|
}
|
|
);
|
|
DeferredUpdates::addCallableUpdate(
|
|
function () use ( $updates ) {
|
|
echo $updates['3'];
|
|
DeferredUpdates::addCallableUpdate(
|
|
function () use ( $updates ) {
|
|
echo $updates['3-1'];
|
|
DeferredUpdates::addCallableUpdate(
|
|
function () use ( $updates ) {
|
|
echo $updates['3-1-1'];
|
|
}
|
|
);
|
|
}
|
|
);
|
|
DeferredUpdates::addCallableUpdate(
|
|
function () use ( $updates ) {
|
|
echo $updates['3-2'];
|
|
DeferredUpdates::addCallableUpdate(
|
|
function () use ( $updates ) {
|
|
echo $updates['3-2-1'];
|
|
}
|
|
);
|
|
}
|
|
);
|
|
}
|
|
);
|
|
|
|
$this->assertEquals( 3, DeferredUpdates::pendingUpdatesCount() );
|
|
|
|
$this->expectOutputString( implode( '', $updates ) );
|
|
|
|
DeferredUpdates::doUpdates();
|
|
|
|
$x = null;
|
|
$y = null;
|
|
DeferredUpdates::addCallableUpdate(
|
|
function () use ( &$x ) {
|
|
$x = 'Sherity';
|
|
},
|
|
DeferredUpdates::PRESEND
|
|
);
|
|
DeferredUpdates::addCallableUpdate(
|
|
function () use ( &$y ) {
|
|
$y = 'Marychu';
|
|
},
|
|
DeferredUpdates::POSTSEND
|
|
);
|
|
|
|
$this->assertNull( $x, "Update not run yet" );
|
|
$this->assertNull( $y, "Update not run yet" );
|
|
|
|
DeferredUpdates::doUpdates( 'run', DeferredUpdates::PRESEND );
|
|
$this->assertEquals( "Sherity", $x, "PRESEND update ran" );
|
|
$this->assertNull( $y, "POSTSEND update not run yet" );
|
|
|
|
DeferredUpdates::doUpdates( 'run', DeferredUpdates::POSTSEND );
|
|
$this->assertEquals( "Marychu", $y, "POSTSEND update ran" );
|
|
}
|
|
|
|
/**
|
|
* @covers DeferredUpdates::doUpdates
|
|
* @covers DeferredUpdates::execute
|
|
* @covers DeferredUpdates::addUpdate
|
|
*/
|
|
public function testDoUpdatesCLI() {
|
|
$this->setMwGlobals( 'wgCommandLineMode', true );
|
|
$updates = [
|
|
'1' => "deferred update 1;\n",
|
|
'2' => "deferred update 2;\n",
|
|
'2-1' => "deferred update 1 within deferred update 2;\n",
|
|
'2-2' => "deferred update 2 within deferred update 2;\n",
|
|
'3' => "deferred update 3;\n",
|
|
'3-1' => "deferred update 1 within deferred update 3;\n",
|
|
'3-2' => "deferred update 2 within deferred update 3;\n",
|
|
'3-1-1' => "deferred update 1 within deferred update 1 within deferred update 3;\n",
|
|
'3-2-1' => "deferred update 1 within deferred update 2 with deferred update 3;\n",
|
|
];
|
|
|
|
// clear anything
|
|
wfGetLBFactory()->commitMasterChanges( __METHOD__ );
|
|
|
|
DeferredUpdates::addCallableUpdate(
|
|
function () use ( $updates ) {
|
|
echo $updates['1'];
|
|
}
|
|
);
|
|
DeferredUpdates::addCallableUpdate(
|
|
function () use ( $updates ) {
|
|
echo $updates['2'];
|
|
DeferredUpdates::addCallableUpdate(
|
|
function () use ( $updates ) {
|
|
echo $updates['2-1'];
|
|
}
|
|
);
|
|
DeferredUpdates::addCallableUpdate(
|
|
function () use ( $updates ) {
|
|
echo $updates['2-2'];
|
|
}
|
|
);
|
|
}
|
|
);
|
|
DeferredUpdates::addCallableUpdate(
|
|
function () use ( $updates ) {
|
|
echo $updates['3'];
|
|
DeferredUpdates::addCallableUpdate(
|
|
function () use ( $updates ) {
|
|
echo $updates['3-1'];
|
|
DeferredUpdates::addCallableUpdate(
|
|
function () use ( $updates ) {
|
|
echo $updates['3-1-1'];
|
|
}
|
|
);
|
|
}
|
|
);
|
|
DeferredUpdates::addCallableUpdate(
|
|
function () use ( $updates ) {
|
|
echo $updates['3-2'];
|
|
DeferredUpdates::addCallableUpdate(
|
|
function () use ( $updates ) {
|
|
echo $updates['3-2-1'];
|
|
}
|
|
);
|
|
}
|
|
);
|
|
}
|
|
);
|
|
|
|
$this->expectOutputString( implode( '', $updates ) );
|
|
|
|
DeferredUpdates::doUpdates();
|
|
}
|
|
|
|
/**
|
|
* @covers DeferredUpdates::doUpdates
|
|
* @covers DeferredUpdates::execute
|
|
* @covers DeferredUpdates::addUpdate
|
|
*/
|
|
public function testPresendAddOnPostsendRun() {
|
|
$this->setMwGlobals( 'wgCommandLineMode', true );
|
|
|
|
$x = false;
|
|
$y = false;
|
|
// clear anything
|
|
wfGetLBFactory()->commitMasterChanges( __METHOD__ );
|
|
|
|
DeferredUpdates::addCallableUpdate(
|
|
function () use ( &$x, &$y ) {
|
|
$x = true;
|
|
DeferredUpdates::addCallableUpdate(
|
|
function () use ( &$y ) {
|
|
$y = true;
|
|
},
|
|
DeferredUpdates::PRESEND
|
|
);
|
|
},
|
|
DeferredUpdates::POSTSEND
|
|
);
|
|
|
|
DeferredUpdates::doUpdates();
|
|
|
|
$this->assertTrue( $x, "Outer POSTSEND update ran" );
|
|
$this->assertTrue( $y, "Nested PRESEND update ran" );
|
|
}
|
|
|
|
/**
|
|
* @covers DeferredUpdates::runUpdate
|
|
*/
|
|
public function testRunUpdateTransactionScope() {
|
|
$this->setMwGlobals( 'wgCommandLineMode', false );
|
|
|
|
$lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
|
|
$this->assertFalse( $lbFactory->hasTransactionRound(), 'Initial state' );
|
|
|
|
$ran = 0;
|
|
DeferredUpdates::addCallableUpdate( function () use ( &$ran, $lbFactory ) {
|
|
$ran++;
|
|
$this->assertTrue( $lbFactory->hasTransactionRound(), 'Has transaction' );
|
|
} );
|
|
DeferredUpdates::doUpdates();
|
|
|
|
$this->assertSame( 1, $ran, 'Update ran' );
|
|
$this->assertFalse( $lbFactory->hasTransactionRound(), 'Final state' );
|
|
}
|
|
|
|
/**
|
|
* @covers DeferredUpdates::runUpdate
|
|
* @covers TransactionRoundDefiningUpdate::getOrigin
|
|
*/
|
|
public function testRunOuterScopeUpdate() {
|
|
$this->setMwGlobals( 'wgCommandLineMode', false );
|
|
|
|
$lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
|
|
$this->assertFalse( $lbFactory->hasTransactionRound(), 'Initial state' );
|
|
|
|
$ran = 0;
|
|
DeferredUpdates::addUpdate( new TransactionRoundDefiningUpdate(
|
|
function () use ( &$ran, $lbFactory ) {
|
|
$ran++;
|
|
$this->assertFalse( $lbFactory->hasTransactionRound(), 'No transaction' );
|
|
} )
|
|
);
|
|
DeferredUpdates::doUpdates();
|
|
|
|
$this->assertSame( 1, $ran, 'Update ran' );
|
|
}
|
|
}
|