wiki.techinc.nl/tests/phpunit/includes/deferred/DeferredUpdatesTest.php

485 lines
14 KiB
PHP
Raw Normal View History

<?php
use MediaWiki\MediaWikiServices;
class DeferredUpdatesTest extends MediaWikiIntegrationTestCase {
/**
* @covers DeferredUpdates::addUpdate
* @covers DeferredUpdates::doUpdates
* @covers DeferredUpdates::attemptUpdate
deferred: make DeferredUpdates::doUpdates() recursion more uniform Follow-up ba6490aa1eb. That patch worked around $executeContext iteration errors due to the lack of recursion support in doUpdates()/handleUpdateQueue(). Errors were triggered by MediaWiki::schedulePostSendJobs() enqueueing deferred updates that invoke JobRunner::run(), which, in turn, invoke doUpdates() for each job via JobRunner:: doExecuteJob(). The doUpdates() method was changed to operate on the sub-queue for the in-progress DeferrableUpdate. Further improve the recursion logic: * Use classes for the scope stack and scope queues. * Put *all* updates added during a DeferrableUpdate::doUpdate() call into the subqueue, regardless of "defer until" stage or whether it implements MergeableUpdate. Now, doUpdate() can run *everything* it enqueued instead of a non-obvious subset. Note that the TransactionRoundDefiningUpdate that invokes JobRunner was already a POSTSEND update before ba6490aa1eb; the only effect of this change is that MergeableUpdate instances from jobs will once again run in doExecuteJob(). * Make recursive DeferredUpdates::doUpdate() calls error out immediately unless the DeferrableUpdate responsible is a TransactionRoundAwareUpdate with the TRX_ROUND_ABSENT flag. This covers the schedulePostSendJobs() scenario and only prohibits insane call patterns. Failing early avoids the risk of handleUpdateQueue() dropping all the updates due to the same DBTransactionError error in DeferredUpdates::attemptUpdate(). * Avoid recursion loop in tryOpportunisticExecute() when JobQueueDB is in use and a large number of tasks are pending. This happened due to methods like onTransactionPreCommitOrIdle() being used within JobQueueDB. Mark DeferredUpdates::doUpdates()/tryOpportunisticExecute() as @internal and create a Maintenance::shutdown() method to avoid a direct call in the doMaintenance.php file. Bug: T249069 Bug: T268840 Change-Id: Ib369f0e74243a48ababdb9cd83b155c9a0f5e741
2020-05-06 14:37:00 +00:00
* @covers DeferredUpdatesScopeStack
* @covers DeferredUpdatesScope
*/
public function testAddAndRun() {
$update = $this->getMockBuilder( DeferrableUpdate::class )
->onlyMethods( [ 'doUpdate' ] )->getMock();
$update->expects( $this->once() )->method( 'doUpdate' );
DeferredUpdates::addUpdate( $update );
DeferredUpdates::doUpdates();
}
/**
* @covers DeferredUpdates::addUpdate
deferred: make DeferredUpdates::doUpdates() recursion more uniform Follow-up ba6490aa1eb. That patch worked around $executeContext iteration errors due to the lack of recursion support in doUpdates()/handleUpdateQueue(). Errors were triggered by MediaWiki::schedulePostSendJobs() enqueueing deferred updates that invoke JobRunner::run(), which, in turn, invoke doUpdates() for each job via JobRunner:: doExecuteJob(). The doUpdates() method was changed to operate on the sub-queue for the in-progress DeferrableUpdate. Further improve the recursion logic: * Use classes for the scope stack and scope queues. * Put *all* updates added during a DeferrableUpdate::doUpdate() call into the subqueue, regardless of "defer until" stage or whether it implements MergeableUpdate. Now, doUpdate() can run *everything* it enqueued instead of a non-obvious subset. Note that the TransactionRoundDefiningUpdate that invokes JobRunner was already a POSTSEND update before ba6490aa1eb; the only effect of this change is that MergeableUpdate instances from jobs will once again run in doExecuteJob(). * Make recursive DeferredUpdates::doUpdate() calls error out immediately unless the DeferrableUpdate responsible is a TransactionRoundAwareUpdate with the TRX_ROUND_ABSENT flag. This covers the schedulePostSendJobs() scenario and only prohibits insane call patterns. Failing early avoids the risk of handleUpdateQueue() dropping all the updates due to the same DBTransactionError error in DeferredUpdates::attemptUpdate(). * Avoid recursion loop in tryOpportunisticExecute() when JobQueueDB is in use and a large number of tasks are pending. This happened due to methods like onTransactionPreCommitOrIdle() being used within JobQueueDB. Mark DeferredUpdates::doUpdates()/tryOpportunisticExecute() as @internal and create a Maintenance::shutdown() method to avoid a direct call in the doMaintenance.php file. Bug: T249069 Bug: T268840 Change-Id: Ib369f0e74243a48ababdb9cd83b155c9a0f5e741
2020-05-06 14:37:00 +00:00
* @covers DeferredUpdatesScopeStack
* @covers DeferredUpdatesScope
*/
public function testAddMergeable() {
$this->setMwGlobals( 'wgCommandLineMode', false );
$update1 = $this->getMockBuilder( MergeableUpdate::class )
->onlyMethods( [ 'merge', 'doUpdate' ] )->getMock();
$update1->expects( $this->once() )->method( 'merge' );
$update1->expects( $this->never() )->method( 'doUpdate' );
$update2 = $this->getMockBuilder( MergeableUpdate::class )
->onlyMethods( [ '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( static 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->createMock( DeferrableUpdate::class );
$update->expects( $this->never() )
->method( 'doUpdate' );
DeferredUpdates::addUpdate( $update, $pre );
$this->assertCount( 1, DeferredUpdates::getPendingUpdates( $pre ) );
$this->assertSame( [], DeferredUpdates::getPendingUpdates( $post ) );
$this->assertCount( 1, DeferredUpdates::getPendingUpdates( $all ) );
$this->assertCount( 1, DeferredUpdates::getPendingUpdates() );
DeferredUpdates::clearPendingUpdates();
$this->assertSame( [], DeferredUpdates::getPendingUpdates() );
DeferredUpdates::addUpdate( $update, $post );
$this->assertSame( [], DeferredUpdates::getPendingUpdates( $pre ) );
$this->assertCount( 1, DeferredUpdates::getPendingUpdates( $post ) );
$this->assertCount( 1, DeferredUpdates::getPendingUpdates( $all ) );
$this->assertCount( 1, DeferredUpdates::getPendingUpdates() );
DeferredUpdates::clearPendingUpdates();
$this->assertSame( [], DeferredUpdates::getPendingUpdates() );
}
/**
* @covers DeferredUpdates::doUpdates
* @covers DeferredUpdates::addUpdate
deferred: make DeferredUpdates::doUpdates() recursion more uniform Follow-up ba6490aa1eb. That patch worked around $executeContext iteration errors due to the lack of recursion support in doUpdates()/handleUpdateQueue(). Errors were triggered by MediaWiki::schedulePostSendJobs() enqueueing deferred updates that invoke JobRunner::run(), which, in turn, invoke doUpdates() for each job via JobRunner:: doExecuteJob(). The doUpdates() method was changed to operate on the sub-queue for the in-progress DeferrableUpdate. Further improve the recursion logic: * Use classes for the scope stack and scope queues. * Put *all* updates added during a DeferrableUpdate::doUpdate() call into the subqueue, regardless of "defer until" stage or whether it implements MergeableUpdate. Now, doUpdate() can run *everything* it enqueued instead of a non-obvious subset. Note that the TransactionRoundDefiningUpdate that invokes JobRunner was already a POSTSEND update before ba6490aa1eb; the only effect of this change is that MergeableUpdate instances from jobs will once again run in doExecuteJob(). * Make recursive DeferredUpdates::doUpdate() calls error out immediately unless the DeferrableUpdate responsible is a TransactionRoundAwareUpdate with the TRX_ROUND_ABSENT flag. This covers the schedulePostSendJobs() scenario and only prohibits insane call patterns. Failing early avoids the risk of handleUpdateQueue() dropping all the updates due to the same DBTransactionError error in DeferredUpdates::attemptUpdate(). * Avoid recursion loop in tryOpportunisticExecute() when JobQueueDB is in use and a large number of tasks are pending. This happened due to methods like onTransactionPreCommitOrIdle() being used within JobQueueDB. Mark DeferredUpdates::doUpdates()/tryOpportunisticExecute() as @internal and create a Maintenance::shutdown() method to avoid a direct call in the doMaintenance.php file. Bug: T249069 Bug: T268840 Change-Id: Ib369f0e74243a48ababdb9cd83b155c9a0f5e741
2020-05-06 14:37:00 +00:00
* @covers DeferredUpdatesScopeStack
* @covers DeferredUpdatesScope
*/
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(
static function () use ( $updates ) {
echo $updates['1'];
}
);
DeferredUpdates::addCallableUpdate(
static function () use ( $updates ) {
echo $updates['2'];
DeferredUpdates::addCallableUpdate(
static function () use ( $updates ) {
echo $updates['2-1'];
}
);
DeferredUpdates::addCallableUpdate(
static function () use ( $updates ) {
echo $updates['2-2'];
}
);
}
);
DeferredUpdates::addCallableUpdate(
static function () use ( $updates ) {
echo $updates['3'];
DeferredUpdates::addCallableUpdate(
static function () use ( $updates ) {
echo $updates['3-1'];
DeferredUpdates::addCallableUpdate(
static function () use ( $updates ) {
echo $updates['3-1-1'];
}
);
}
);
DeferredUpdates::addCallableUpdate(
static function () use ( $updates ) {
echo $updates['3-2'];
DeferredUpdates::addCallableUpdate(
static 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(
static function () use ( &$x ) {
$x = 'Sherity';
},
DeferredUpdates::PRESEND
);
DeferredUpdates::addCallableUpdate(
static 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::addUpdate
deferred: make DeferredUpdates::doUpdates() recursion more uniform Follow-up ba6490aa1eb. That patch worked around $executeContext iteration errors due to the lack of recursion support in doUpdates()/handleUpdateQueue(). Errors were triggered by MediaWiki::schedulePostSendJobs() enqueueing deferred updates that invoke JobRunner::run(), which, in turn, invoke doUpdates() for each job via JobRunner:: doExecuteJob(). The doUpdates() method was changed to operate on the sub-queue for the in-progress DeferrableUpdate. Further improve the recursion logic: * Use classes for the scope stack and scope queues. * Put *all* updates added during a DeferrableUpdate::doUpdate() call into the subqueue, regardless of "defer until" stage or whether it implements MergeableUpdate. Now, doUpdate() can run *everything* it enqueued instead of a non-obvious subset. Note that the TransactionRoundDefiningUpdate that invokes JobRunner was already a POSTSEND update before ba6490aa1eb; the only effect of this change is that MergeableUpdate instances from jobs will once again run in doExecuteJob(). * Make recursive DeferredUpdates::doUpdate() calls error out immediately unless the DeferrableUpdate responsible is a TransactionRoundAwareUpdate with the TRX_ROUND_ABSENT flag. This covers the schedulePostSendJobs() scenario and only prohibits insane call patterns. Failing early avoids the risk of handleUpdateQueue() dropping all the updates due to the same DBTransactionError error in DeferredUpdates::attemptUpdate(). * Avoid recursion loop in tryOpportunisticExecute() when JobQueueDB is in use and a large number of tasks are pending. This happened due to methods like onTransactionPreCommitOrIdle() being used within JobQueueDB. Mark DeferredUpdates::doUpdates()/tryOpportunisticExecute() as @internal and create a Maintenance::shutdown() method to avoid a direct call in the doMaintenance.php file. Bug: T249069 Bug: T268840 Change-Id: Ib369f0e74243a48ababdb9cd83b155c9a0f5e741
2020-05-06 14:37:00 +00:00
* @covers DeferredUpdatesScopeStack
* @covers DeferredUpdatesScope
*/
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
$lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
$lbFactory->commitPrimaryChanges( __METHOD__ );
DeferredUpdates::addCallableUpdate(
static function () use ( $updates ) {
echo $updates['1'];
}
);
DeferredUpdates::addCallableUpdate(
static function () use ( $updates ) {
echo $updates['2'];
DeferredUpdates::addCallableUpdate(
static function () use ( $updates ) {
echo $updates['2-1'];
}
);
DeferredUpdates::addCallableUpdate(
static function () use ( $updates ) {
echo $updates['2-2'];
}
);
}
);
DeferredUpdates::addCallableUpdate(
static function () use ( $updates ) {
echo $updates['3'];
DeferredUpdates::addCallableUpdate(
static function () use ( $updates ) {
echo $updates['3-1'];
DeferredUpdates::addCallableUpdate(
static function () use ( $updates ) {
echo $updates['3-1-1'];
}
);
}
);
DeferredUpdates::addCallableUpdate(
static function () use ( $updates ) {
echo $updates['3-2'];
DeferredUpdates::addCallableUpdate(
static function () use ( $updates ) {
echo $updates['3-2-1'];
}
);
}
);
}
);
$this->expectOutputString( implode( '', $updates ) );
DeferredUpdates::doUpdates();
}
/**
* @covers DeferredUpdates::doUpdates
* @covers DeferredUpdates::addUpdate
deferred: make DeferredUpdates::doUpdates() recursion more uniform Follow-up ba6490aa1eb. That patch worked around $executeContext iteration errors due to the lack of recursion support in doUpdates()/handleUpdateQueue(). Errors were triggered by MediaWiki::schedulePostSendJobs() enqueueing deferred updates that invoke JobRunner::run(), which, in turn, invoke doUpdates() for each job via JobRunner:: doExecuteJob(). The doUpdates() method was changed to operate on the sub-queue for the in-progress DeferrableUpdate. Further improve the recursion logic: * Use classes for the scope stack and scope queues. * Put *all* updates added during a DeferrableUpdate::doUpdate() call into the subqueue, regardless of "defer until" stage or whether it implements MergeableUpdate. Now, doUpdate() can run *everything* it enqueued instead of a non-obvious subset. Note that the TransactionRoundDefiningUpdate that invokes JobRunner was already a POSTSEND update before ba6490aa1eb; the only effect of this change is that MergeableUpdate instances from jobs will once again run in doExecuteJob(). * Make recursive DeferredUpdates::doUpdate() calls error out immediately unless the DeferrableUpdate responsible is a TransactionRoundAwareUpdate with the TRX_ROUND_ABSENT flag. This covers the schedulePostSendJobs() scenario and only prohibits insane call patterns. Failing early avoids the risk of handleUpdateQueue() dropping all the updates due to the same DBTransactionError error in DeferredUpdates::attemptUpdate(). * Avoid recursion loop in tryOpportunisticExecute() when JobQueueDB is in use and a large number of tasks are pending. This happened due to methods like onTransactionPreCommitOrIdle() being used within JobQueueDB. Mark DeferredUpdates::doUpdates()/tryOpportunisticExecute() as @internal and create a Maintenance::shutdown() method to avoid a direct call in the doMaintenance.php file. Bug: T249069 Bug: T268840 Change-Id: Ib369f0e74243a48ababdb9cd83b155c9a0f5e741
2020-05-06 14:37:00 +00:00
* @covers DeferredUpdatesScopeStack
* @covers DeferredUpdatesScope
*/
public function testPresendAddOnPostsendRun() {
$this->setMwGlobals( 'wgCommandLineMode', true );
$x = false;
$y = false;
// clear anything
$lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
$lbFactory->commitPrimaryChanges( __METHOD__ );
DeferredUpdates::addCallableUpdate(
static function () use ( &$x, &$y ) {
$x = true;
DeferredUpdates::addCallableUpdate(
static 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::attemptUpdate
*/
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::attemptUpdate
* @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' );
}
/**
* @covers DeferredUpdates::tryOpportunisticExecute
deferred: make DeferredUpdates::doUpdates() recursion more uniform Follow-up ba6490aa1eb. That patch worked around $executeContext iteration errors due to the lack of recursion support in doUpdates()/handleUpdateQueue(). Errors were triggered by MediaWiki::schedulePostSendJobs() enqueueing deferred updates that invoke JobRunner::run(), which, in turn, invoke doUpdates() for each job via JobRunner:: doExecuteJob(). The doUpdates() method was changed to operate on the sub-queue for the in-progress DeferrableUpdate. Further improve the recursion logic: * Use classes for the scope stack and scope queues. * Put *all* updates added during a DeferrableUpdate::doUpdate() call into the subqueue, regardless of "defer until" stage or whether it implements MergeableUpdate. Now, doUpdate() can run *everything* it enqueued instead of a non-obvious subset. Note that the TransactionRoundDefiningUpdate that invokes JobRunner was already a POSTSEND update before ba6490aa1eb; the only effect of this change is that MergeableUpdate instances from jobs will once again run in doExecuteJob(). * Make recursive DeferredUpdates::doUpdate() calls error out immediately unless the DeferrableUpdate responsible is a TransactionRoundAwareUpdate with the TRX_ROUND_ABSENT flag. This covers the schedulePostSendJobs() scenario and only prohibits insane call patterns. Failing early avoids the risk of handleUpdateQueue() dropping all the updates due to the same DBTransactionError error in DeferredUpdates::attemptUpdate(). * Avoid recursion loop in tryOpportunisticExecute() when JobQueueDB is in use and a large number of tasks are pending. This happened due to methods like onTransactionPreCommitOrIdle() being used within JobQueueDB. Mark DeferredUpdates::doUpdates()/tryOpportunisticExecute() as @internal and create a Maintenance::shutdown() method to avoid a direct call in the doMaintenance.php file. Bug: T249069 Bug: T268840 Change-Id: Ib369f0e74243a48ababdb9cd83b155c9a0f5e741
2020-05-06 14:37:00 +00:00
* @covers DeferredUpdates::doUpdates
* @covers DeferredUpdatesScopeStack
* @covers DeferredUpdatesScope
*/
public function testTryOpportunisticExecute() {
$calls = [];
$callback1 = static function () use ( &$calls ) {
$calls[] = 1;
};
$callback2 = static function () use ( &$calls ) {
$calls[] = 2;
};
$lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
$lbFactory->beginPrimaryChanges( __METHOD__ );
DeferredUpdates::addCallableUpdate( $callback1 );
$this->assertEquals( [], $calls );
DeferredUpdates::tryOpportunisticExecute( 'run' );
$this->assertEquals( [], $calls );
$dbw = wfGetDB( DB_PRIMARY );
$dbw->onTransactionCommitOrIdle( function () use ( &$calls, $callback2 ) {
DeferredUpdates::addCallableUpdate( $callback2 );
$this->assertEquals( [], $calls );
$calls[] = 'oti';
} );
$this->assertSame( 1, $dbw->trxLevel() );
$this->assertEquals( [], $calls );
$lbFactory->commitPrimaryChanges( __METHOD__ );
$this->assertEquals( [ 'oti' ], $calls );
DeferredUpdates::tryOpportunisticExecute( 'run' );
$this->assertEquals( [ 'oti', 1, 2 ], $calls );
}
/**
* @covers DeferredUpdates::attemptUpdate
*/
public function testCallbackUpdateRounds() {
$lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
$fname = __METHOD__;
$called = false;
DeferredUpdates::attemptUpdate(
new MWCallableUpdate(
static function () use ( $lbFactory, $fname, &$called ) {
$lbFactory->flushReplicaSnapshots( $fname );
$lbFactory->commitPrimaryChanges( $fname );
$called = true;
},
$fname
),
$lbFactory
);
$this->assertTrue( $called, "Callback ran" );
}
/**
* @covers DeferredUpdates::doUpdates
deferred: make DeferredUpdates::doUpdates() recursion more uniform Follow-up ba6490aa1eb. That patch worked around $executeContext iteration errors due to the lack of recursion support in doUpdates()/handleUpdateQueue(). Errors were triggered by MediaWiki::schedulePostSendJobs() enqueueing deferred updates that invoke JobRunner::run(), which, in turn, invoke doUpdates() for each job via JobRunner:: doExecuteJob(). The doUpdates() method was changed to operate on the sub-queue for the in-progress DeferrableUpdate. Further improve the recursion logic: * Use classes for the scope stack and scope queues. * Put *all* updates added during a DeferrableUpdate::doUpdate() call into the subqueue, regardless of "defer until" stage or whether it implements MergeableUpdate. Now, doUpdate() can run *everything* it enqueued instead of a non-obvious subset. Note that the TransactionRoundDefiningUpdate that invokes JobRunner was already a POSTSEND update before ba6490aa1eb; the only effect of this change is that MergeableUpdate instances from jobs will once again run in doExecuteJob(). * Make recursive DeferredUpdates::doUpdate() calls error out immediately unless the DeferrableUpdate responsible is a TransactionRoundAwareUpdate with the TRX_ROUND_ABSENT flag. This covers the schedulePostSendJobs() scenario and only prohibits insane call patterns. Failing early avoids the risk of handleUpdateQueue() dropping all the updates due to the same DBTransactionError error in DeferredUpdates::attemptUpdate(). * Avoid recursion loop in tryOpportunisticExecute() when JobQueueDB is in use and a large number of tasks are pending. This happened due to methods like onTransactionPreCommitOrIdle() being used within JobQueueDB. Mark DeferredUpdates::doUpdates()/tryOpportunisticExecute() as @internal and create a Maintenance::shutdown() method to avoid a direct call in the doMaintenance.php file. Bug: T249069 Bug: T268840 Change-Id: Ib369f0e74243a48ababdb9cd83b155c9a0f5e741
2020-05-06 14:37:00 +00:00
* @covers DeferredUpdatesScopeStack
* @covers DeferredUpdatesScope
*/
public function testNestedExecution() {
// No immediate execution
$this->setMwGlobals( 'wgCommandLineMode', false );
$res = null;
$resSub = null;
$resSubSub = null;
$resA = null;
deferred: make DeferredUpdates::doUpdates() recursion more uniform Follow-up ba6490aa1eb. That patch worked around $executeContext iteration errors due to the lack of recursion support in doUpdates()/handleUpdateQueue(). Errors were triggered by MediaWiki::schedulePostSendJobs() enqueueing deferred updates that invoke JobRunner::run(), which, in turn, invoke doUpdates() for each job via JobRunner:: doExecuteJob(). The doUpdates() method was changed to operate on the sub-queue for the in-progress DeferrableUpdate. Further improve the recursion logic: * Use classes for the scope stack and scope queues. * Put *all* updates added during a DeferrableUpdate::doUpdate() call into the subqueue, regardless of "defer until" stage or whether it implements MergeableUpdate. Now, doUpdate() can run *everything* it enqueued instead of a non-obvious subset. Note that the TransactionRoundDefiningUpdate that invokes JobRunner was already a POSTSEND update before ba6490aa1eb; the only effect of this change is that MergeableUpdate instances from jobs will once again run in doExecuteJob(). * Make recursive DeferredUpdates::doUpdate() calls error out immediately unless the DeferrableUpdate responsible is a TransactionRoundAwareUpdate with the TRX_ROUND_ABSENT flag. This covers the schedulePostSendJobs() scenario and only prohibits insane call patterns. Failing early avoids the risk of handleUpdateQueue() dropping all the updates due to the same DBTransactionError error in DeferredUpdates::attemptUpdate(). * Avoid recursion loop in tryOpportunisticExecute() when JobQueueDB is in use and a large number of tasks are pending. This happened due to methods like onTransactionPreCommitOrIdle() being used within JobQueueDB. Mark DeferredUpdates::doUpdates()/tryOpportunisticExecute() as @internal and create a Maintenance::shutdown() method to avoid a direct call in the doMaintenance.php file. Bug: T249069 Bug: T268840 Change-Id: Ib369f0e74243a48ababdb9cd83b155c9a0f5e741
2020-05-06 14:37:00 +00:00
DeferredUpdates::clearPendingUpdates();
$this->assertSame( 0, DeferredUpdates::pendingUpdatesCount() );
$this->assertSame( 0, DeferredUpdates::getRecursiveExecutionStackDepth() );
// T249069: TransactionRoundDefiningUpdate => JobRunner => DeferredUpdates::doUpdates()
DeferredUpdates::addUpdate( new TransactionRoundDefiningUpdate(
function () use ( &$res, &$resSub, &$resSubSub, &$resA ) {
$res = 1;
deferred: make DeferredUpdates::doUpdates() recursion more uniform Follow-up ba6490aa1eb. That patch worked around $executeContext iteration errors due to the lack of recursion support in doUpdates()/handleUpdateQueue(). Errors were triggered by MediaWiki::schedulePostSendJobs() enqueueing deferred updates that invoke JobRunner::run(), which, in turn, invoke doUpdates() for each job via JobRunner:: doExecuteJob(). The doUpdates() method was changed to operate on the sub-queue for the in-progress DeferrableUpdate. Further improve the recursion logic: * Use classes for the scope stack and scope queues. * Put *all* updates added during a DeferrableUpdate::doUpdate() call into the subqueue, regardless of "defer until" stage or whether it implements MergeableUpdate. Now, doUpdate() can run *everything* it enqueued instead of a non-obvious subset. Note that the TransactionRoundDefiningUpdate that invokes JobRunner was already a POSTSEND update before ba6490aa1eb; the only effect of this change is that MergeableUpdate instances from jobs will once again run in doExecuteJob(). * Make recursive DeferredUpdates::doUpdate() calls error out immediately unless the DeferrableUpdate responsible is a TransactionRoundAwareUpdate with the TRX_ROUND_ABSENT flag. This covers the schedulePostSendJobs() scenario and only prohibits insane call patterns. Failing early avoids the risk of handleUpdateQueue() dropping all the updates due to the same DBTransactionError error in DeferredUpdates::attemptUpdate(). * Avoid recursion loop in tryOpportunisticExecute() when JobQueueDB is in use and a large number of tasks are pending. This happened due to methods like onTransactionPreCommitOrIdle() being used within JobQueueDB. Mark DeferredUpdates::doUpdates()/tryOpportunisticExecute() as @internal and create a Maintenance::shutdown() method to avoid a direct call in the doMaintenance.php file. Bug: T249069 Bug: T268840 Change-Id: Ib369f0e74243a48ababdb9cd83b155c9a0f5e741
2020-05-06 14:37:00 +00:00
$this->assertSame( 0, DeferredUpdates::pendingUpdatesCount() );
$this->assertSame( 1, DeferredUpdates::getRecursiveExecutionStackDepth() );
// Add update to subqueue of in-progress top-queue job
DeferredUpdates::addCallableUpdate( function () use ( &$resSub, &$resSubSub ) {
$resSub = 'a';
deferred: make DeferredUpdates::doUpdates() recursion more uniform Follow-up ba6490aa1eb. That patch worked around $executeContext iteration errors due to the lack of recursion support in doUpdates()/handleUpdateQueue(). Errors were triggered by MediaWiki::schedulePostSendJobs() enqueueing deferred updates that invoke JobRunner::run(), which, in turn, invoke doUpdates() for each job via JobRunner:: doExecuteJob(). The doUpdates() method was changed to operate on the sub-queue for the in-progress DeferrableUpdate. Further improve the recursion logic: * Use classes for the scope stack and scope queues. * Put *all* updates added during a DeferrableUpdate::doUpdate() call into the subqueue, regardless of "defer until" stage or whether it implements MergeableUpdate. Now, doUpdate() can run *everything* it enqueued instead of a non-obvious subset. Note that the TransactionRoundDefiningUpdate that invokes JobRunner was already a POSTSEND update before ba6490aa1eb; the only effect of this change is that MergeableUpdate instances from jobs will once again run in doExecuteJob(). * Make recursive DeferredUpdates::doUpdate() calls error out immediately unless the DeferrableUpdate responsible is a TransactionRoundAwareUpdate with the TRX_ROUND_ABSENT flag. This covers the schedulePostSendJobs() scenario and only prohibits insane call patterns. Failing early avoids the risk of handleUpdateQueue() dropping all the updates due to the same DBTransactionError error in DeferredUpdates::attemptUpdate(). * Avoid recursion loop in tryOpportunisticExecute() when JobQueueDB is in use and a large number of tasks are pending. This happened due to methods like onTransactionPreCommitOrIdle() being used within JobQueueDB. Mark DeferredUpdates::doUpdates()/tryOpportunisticExecute() as @internal and create a Maintenance::shutdown() method to avoid a direct call in the doMaintenance.php file. Bug: T249069 Bug: T268840 Change-Id: Ib369f0e74243a48ababdb9cd83b155c9a0f5e741
2020-05-06 14:37:00 +00:00
$this->assertSame( 0, DeferredUpdates::pendingUpdatesCount() );
$this->assertSame( 2, DeferredUpdates::getRecursiveExecutionStackDepth() );
// Add update to subqueue of in-progress top-queue job (not recursive)
DeferredUpdates::addCallableUpdate( static function () use ( &$resSubSub ) {
$resSubSub = 'b';
} );
deferred: make DeferredUpdates::doUpdates() recursion more uniform Follow-up ba6490aa1eb. That patch worked around $executeContext iteration errors due to the lack of recursion support in doUpdates()/handleUpdateQueue(). Errors were triggered by MediaWiki::schedulePostSendJobs() enqueueing deferred updates that invoke JobRunner::run(), which, in turn, invoke doUpdates() for each job via JobRunner:: doExecuteJob(). The doUpdates() method was changed to operate on the sub-queue for the in-progress DeferrableUpdate. Further improve the recursion logic: * Use classes for the scope stack and scope queues. * Put *all* updates added during a DeferrableUpdate::doUpdate() call into the subqueue, regardless of "defer until" stage or whether it implements MergeableUpdate. Now, doUpdate() can run *everything* it enqueued instead of a non-obvious subset. Note that the TransactionRoundDefiningUpdate that invokes JobRunner was already a POSTSEND update before ba6490aa1eb; the only effect of this change is that MergeableUpdate instances from jobs will once again run in doExecuteJob(). * Make recursive DeferredUpdates::doUpdate() calls error out immediately unless the DeferrableUpdate responsible is a TransactionRoundAwareUpdate with the TRX_ROUND_ABSENT flag. This covers the schedulePostSendJobs() scenario and only prohibits insane call patterns. Failing early avoids the risk of handleUpdateQueue() dropping all the updates due to the same DBTransactionError error in DeferredUpdates::attemptUpdate(). * Avoid recursion loop in tryOpportunisticExecute() when JobQueueDB is in use and a large number of tasks are pending. This happened due to methods like onTransactionPreCommitOrIdle() being used within JobQueueDB. Mark DeferredUpdates::doUpdates()/tryOpportunisticExecute() as @internal and create a Maintenance::shutdown() method to avoid a direct call in the doMaintenance.php file. Bug: T249069 Bug: T268840 Change-Id: Ib369f0e74243a48ababdb9cd83b155c9a0f5e741
2020-05-06 14:37:00 +00:00
$this->assertSame( 1, DeferredUpdates::pendingUpdatesCount() );
} );
deferred: make DeferredUpdates::doUpdates() recursion more uniform Follow-up ba6490aa1eb. That patch worked around $executeContext iteration errors due to the lack of recursion support in doUpdates()/handleUpdateQueue(). Errors were triggered by MediaWiki::schedulePostSendJobs() enqueueing deferred updates that invoke JobRunner::run(), which, in turn, invoke doUpdates() for each job via JobRunner:: doExecuteJob(). The doUpdates() method was changed to operate on the sub-queue for the in-progress DeferrableUpdate. Further improve the recursion logic: * Use classes for the scope stack and scope queues. * Put *all* updates added during a DeferrableUpdate::doUpdate() call into the subqueue, regardless of "defer until" stage or whether it implements MergeableUpdate. Now, doUpdate() can run *everything* it enqueued instead of a non-obvious subset. Note that the TransactionRoundDefiningUpdate that invokes JobRunner was already a POSTSEND update before ba6490aa1eb; the only effect of this change is that MergeableUpdate instances from jobs will once again run in doExecuteJob(). * Make recursive DeferredUpdates::doUpdate() calls error out immediately unless the DeferrableUpdate responsible is a TransactionRoundAwareUpdate with the TRX_ROUND_ABSENT flag. This covers the schedulePostSendJobs() scenario and only prohibits insane call patterns. Failing early avoids the risk of handleUpdateQueue() dropping all the updates due to the same DBTransactionError error in DeferredUpdates::attemptUpdate(). * Avoid recursion loop in tryOpportunisticExecute() when JobQueueDB is in use and a large number of tasks are pending. This happened due to methods like onTransactionPreCommitOrIdle() being used within JobQueueDB. Mark DeferredUpdates::doUpdates()/tryOpportunisticExecute() as @internal and create a Maintenance::shutdown() method to avoid a direct call in the doMaintenance.php file. Bug: T249069 Bug: T268840 Change-Id: Ib369f0e74243a48ababdb9cd83b155c9a0f5e741
2020-05-06 14:37:00 +00:00
$this->assertSame( 1, DeferredUpdates::pendingUpdatesCount() );
$this->assertSame( 1, DeferredUpdates::getRecursiveExecutionStackDepth() );
if ( $resSub === null && $resA === null && $resSubSub === null ) {
$res = 418;
}
deferred: make DeferredUpdates::doUpdates() recursion more uniform Follow-up ba6490aa1eb. That patch worked around $executeContext iteration errors due to the lack of recursion support in doUpdates()/handleUpdateQueue(). Errors were triggered by MediaWiki::schedulePostSendJobs() enqueueing deferred updates that invoke JobRunner::run(), which, in turn, invoke doUpdates() for each job via JobRunner:: doExecuteJob(). The doUpdates() method was changed to operate on the sub-queue for the in-progress DeferrableUpdate. Further improve the recursion logic: * Use classes for the scope stack and scope queues. * Put *all* updates added during a DeferrableUpdate::doUpdate() call into the subqueue, regardless of "defer until" stage or whether it implements MergeableUpdate. Now, doUpdate() can run *everything* it enqueued instead of a non-obvious subset. Note that the TransactionRoundDefiningUpdate that invokes JobRunner was already a POSTSEND update before ba6490aa1eb; the only effect of this change is that MergeableUpdate instances from jobs will once again run in doExecuteJob(). * Make recursive DeferredUpdates::doUpdate() calls error out immediately unless the DeferrableUpdate responsible is a TransactionRoundAwareUpdate with the TRX_ROUND_ABSENT flag. This covers the schedulePostSendJobs() scenario and only prohibits insane call patterns. Failing early avoids the risk of handleUpdateQueue() dropping all the updates due to the same DBTransactionError error in DeferredUpdates::attemptUpdate(). * Avoid recursion loop in tryOpportunisticExecute() when JobQueueDB is in use and a large number of tasks are pending. This happened due to methods like onTransactionPreCommitOrIdle() being used within JobQueueDB. Mark DeferredUpdates::doUpdates()/tryOpportunisticExecute() as @internal and create a Maintenance::shutdown() method to avoid a direct call in the doMaintenance.php file. Bug: T249069 Bug: T268840 Change-Id: Ib369f0e74243a48ababdb9cd83b155c9a0f5e741
2020-05-06 14:37:00 +00:00
DeferredUpdates::doUpdates();
}
) );
deferred: make DeferredUpdates::doUpdates() recursion more uniform Follow-up ba6490aa1eb. That patch worked around $executeContext iteration errors due to the lack of recursion support in doUpdates()/handleUpdateQueue(). Errors were triggered by MediaWiki::schedulePostSendJobs() enqueueing deferred updates that invoke JobRunner::run(), which, in turn, invoke doUpdates() for each job via JobRunner:: doExecuteJob(). The doUpdates() method was changed to operate on the sub-queue for the in-progress DeferrableUpdate. Further improve the recursion logic: * Use classes for the scope stack and scope queues. * Put *all* updates added during a DeferrableUpdate::doUpdate() call into the subqueue, regardless of "defer until" stage or whether it implements MergeableUpdate. Now, doUpdate() can run *everything* it enqueued instead of a non-obvious subset. Note that the TransactionRoundDefiningUpdate that invokes JobRunner was already a POSTSEND update before ba6490aa1eb; the only effect of this change is that MergeableUpdate instances from jobs will once again run in doExecuteJob(). * Make recursive DeferredUpdates::doUpdate() calls error out immediately unless the DeferrableUpdate responsible is a TransactionRoundAwareUpdate with the TRX_ROUND_ABSENT flag. This covers the schedulePostSendJobs() scenario and only prohibits insane call patterns. Failing early avoids the risk of handleUpdateQueue() dropping all the updates due to the same DBTransactionError error in DeferredUpdates::attemptUpdate(). * Avoid recursion loop in tryOpportunisticExecute() when JobQueueDB is in use and a large number of tasks are pending. This happened due to methods like onTransactionPreCommitOrIdle() being used within JobQueueDB. Mark DeferredUpdates::doUpdates()/tryOpportunisticExecute() as @internal and create a Maintenance::shutdown() method to avoid a direct call in the doMaintenance.php file. Bug: T249069 Bug: T268840 Change-Id: Ib369f0e74243a48ababdb9cd83b155c9a0f5e741
2020-05-06 14:37:00 +00:00
$this->assertSame( 1, DeferredUpdates::pendingUpdatesCount() );
$this->assertSame( 0, DeferredUpdates::getRecursiveExecutionStackDepth() );
DeferredUpdates::addCallableUpdate( static function () use ( &$resA ) {
$resA = 93;
} );
deferred: make DeferredUpdates::doUpdates() recursion more uniform Follow-up ba6490aa1eb. That patch worked around $executeContext iteration errors due to the lack of recursion support in doUpdates()/handleUpdateQueue(). Errors were triggered by MediaWiki::schedulePostSendJobs() enqueueing deferred updates that invoke JobRunner::run(), which, in turn, invoke doUpdates() for each job via JobRunner:: doExecuteJob(). The doUpdates() method was changed to operate on the sub-queue for the in-progress DeferrableUpdate. Further improve the recursion logic: * Use classes for the scope stack and scope queues. * Put *all* updates added during a DeferrableUpdate::doUpdate() call into the subqueue, regardless of "defer until" stage or whether it implements MergeableUpdate. Now, doUpdate() can run *everything* it enqueued instead of a non-obvious subset. Note that the TransactionRoundDefiningUpdate that invokes JobRunner was already a POSTSEND update before ba6490aa1eb; the only effect of this change is that MergeableUpdate instances from jobs will once again run in doExecuteJob(). * Make recursive DeferredUpdates::doUpdate() calls error out immediately unless the DeferrableUpdate responsible is a TransactionRoundAwareUpdate with the TRX_ROUND_ABSENT flag. This covers the schedulePostSendJobs() scenario and only prohibits insane call patterns. Failing early avoids the risk of handleUpdateQueue() dropping all the updates due to the same DBTransactionError error in DeferredUpdates::attemptUpdate(). * Avoid recursion loop in tryOpportunisticExecute() when JobQueueDB is in use and a large number of tasks are pending. This happened due to methods like onTransactionPreCommitOrIdle() being used within JobQueueDB. Mark DeferredUpdates::doUpdates()/tryOpportunisticExecute() as @internal and create a Maintenance::shutdown() method to avoid a direct call in the doMaintenance.php file. Bug: T249069 Bug: T268840 Change-Id: Ib369f0e74243a48ababdb9cd83b155c9a0f5e741
2020-05-06 14:37:00 +00:00
$this->assertSame( 2, DeferredUpdates::pendingUpdatesCount() );
$this->assertSame( 0, DeferredUpdates::getRecursiveExecutionStackDepth() );
$this->assertNull( $resA );
$this->assertNull( $res );
$this->assertNull( $resSub );
$this->assertNull( $resSubSub );
DeferredUpdates::doUpdates();
deferred: make DeferredUpdates::doUpdates() recursion more uniform Follow-up ba6490aa1eb. That patch worked around $executeContext iteration errors due to the lack of recursion support in doUpdates()/handleUpdateQueue(). Errors were triggered by MediaWiki::schedulePostSendJobs() enqueueing deferred updates that invoke JobRunner::run(), which, in turn, invoke doUpdates() for each job via JobRunner:: doExecuteJob(). The doUpdates() method was changed to operate on the sub-queue for the in-progress DeferrableUpdate. Further improve the recursion logic: * Use classes for the scope stack and scope queues. * Put *all* updates added during a DeferrableUpdate::doUpdate() call into the subqueue, regardless of "defer until" stage or whether it implements MergeableUpdate. Now, doUpdate() can run *everything* it enqueued instead of a non-obvious subset. Note that the TransactionRoundDefiningUpdate that invokes JobRunner was already a POSTSEND update before ba6490aa1eb; the only effect of this change is that MergeableUpdate instances from jobs will once again run in doExecuteJob(). * Make recursive DeferredUpdates::doUpdate() calls error out immediately unless the DeferrableUpdate responsible is a TransactionRoundAwareUpdate with the TRX_ROUND_ABSENT flag. This covers the schedulePostSendJobs() scenario and only prohibits insane call patterns. Failing early avoids the risk of handleUpdateQueue() dropping all the updates due to the same DBTransactionError error in DeferredUpdates::attemptUpdate(). * Avoid recursion loop in tryOpportunisticExecute() when JobQueueDB is in use and a large number of tasks are pending. This happened due to methods like onTransactionPreCommitOrIdle() being used within JobQueueDB. Mark DeferredUpdates::doUpdates()/tryOpportunisticExecute() as @internal and create a Maintenance::shutdown() method to avoid a direct call in the doMaintenance.php file. Bug: T249069 Bug: T268840 Change-Id: Ib369f0e74243a48ababdb9cd83b155c9a0f5e741
2020-05-06 14:37:00 +00:00
$this->assertSame( 0, DeferredUpdates::pendingUpdatesCount() );
$this->assertSame( 0, DeferredUpdates::getRecursiveExecutionStackDepth() );
$this->assertSame( 418, $res );
$this->assertSame( 'a', $resSub );
$this->assertSame( 'b', $resSubSub );
$this->assertSame( 93, $resA );
}
}