* Updates that work through this system will be more likely to complete by the time the client
* makes their next request after this one than with the JobQueue system.
*
- * In CLI mode, updates run immediately if no DB writes are pending. Otherwise, they run when:
- * - a) Any waitForReplication() call if no writes are pending on any DB
- * - b) A commit happens on Maintenance::getDB( DB_MASTER ) if no writes are pending on any DB
- * - c) EnqueueableDataUpdate tasks may enqueue on commit of Maintenance::getDB( DB_MASTER )
- * - d) At the completion of Maintenance::execute()
+ * In CLI mode, deferred updates will run:
+ * - a) During DeferredUpdates::addUpdate if no LBFactory DB handles have writes pending
+ * - b) On commit of an LBFactory DB handle if no other such handles have writes pending
+ * - c) During an LBFactory::waitForReplication call if no LBFactory DBs have writes pending
+ * - d) When the queue is large and an LBFactory DB handle commits (EnqueueableDataUpdate only)
+ * - e) At the completion of Maintenance::execute()
+ *
+ * @see Maintenance::setLBFactoryTriggers
*
* When updates are deferred, they go into one two FIFO "top-queues" (one for pre-send and one
* for post-send). Updates enqueued *during* doUpdate() of a "top" update go into the "sub-queue"
public static function addUpdate( DeferrableUpdate $update, $stage = self::POSTSEND ) {
global $wgCommandLineMode;
- if ( self::$executeContext && self::$executeContext['stage'] >= $stage ) {
+ if (
+ self::$executeContext &&
+ self::$executeContext['stage'] >= $stage &&
+ !( $update instanceof MergeableUpdate )
+ ) {
// This is a sub-DeferredUpdate; run it right after its parent update.
// Also, while post-send updates are running, push any "pre-send" jobs to the
// active post-send queue to make sure they get run this round (or at all).
*/
public static function doUpdates( $mode = 'run', $stage = self::ALL ) {
$stageEffective = ( $stage === self::ALL ) ? self::POSTSEND : $stage;
+ // For ALL mode, make sure that any PRESEND updates added along the way get run.
+ // Normally, these use the subqueue, but that isn't true for MergeableUpdate items.
+ do {
+ if ( $stage === self::ALL || $stage === self::PRESEND ) {
+ self::execute( self::$preSendUpdates, $mode, $stageEffective );
+ }
- if ( $stage === self::ALL || $stage === self::PRESEND ) {
- self::execute( self::$preSendUpdates, $mode, $stageEffective );
- }
-
- if ( $stage === self::ALL || $stage == self::POSTSEND ) {
- self::execute( self::$postSendUpdates, $mode, $stageEffective );
- }
- }
-
- /**
- * @param bool $value Whether to just immediately run updates in addUpdate()
- * @since 1.28
- * @deprecated 1.29 Causes issues in Web-executed jobs - see T165714 and T100085.
- */
- public static function setImmediateMode( $value ) {
- wfDeprecated( __METHOD__, '1.29' );
+ if ( $stage === self::ALL || $stage == self::POSTSEND ) {
+ self::execute( self::$postSendUpdates, $mode, $stageEffective );
+ }
+ } while ( $stage === self::ALL && self::$preSendUpdates );
}
/**
/** @var MergeableUpdate $existingUpdate */
$existingUpdate = $queue[$class];
$existingUpdate->merge( $update );
+ // Move the update to the end to handle things like mergeable purge
+ // updates that might depend on the prior updates in the queue running
+ unset( $queue[$class] );
+ $queue[$class] = $existingUpdate;
} else {
$queue[$class] = $update;
}
foreach ( $updatesByType as $updatesForType ) {
foreach ( $updatesForType as $update ) {
self::$executeContext = [ 'stage' => $stage, 'subqueue' => [] ];
- /** @var DeferrableUpdate $update */
- $guiError = self::runUpdate( $update, $lbFactory, $mode, $stage );
- $reportableError = $reportableError ?: $guiError;
- // Do the subqueue updates for $update until there are none
- while ( self::$executeContext['subqueue'] ) {
- $subUpdate = reset( self::$executeContext['subqueue'] );
- $firstKey = key( self::$executeContext['subqueue'] );
- unset( self::$executeContext['subqueue'][$firstKey] );
-
- if ( $subUpdate instanceof DataUpdate ) {
- $subUpdate->setTransactionTicket( $ticket );
- }
-
- $guiError = self::runUpdate( $subUpdate, $lbFactory, $mode, $stage );
+ try {
+ /** @var DeferrableUpdate $update */
+ $guiError = self::runUpdate( $update, $lbFactory, $mode, $stage );
$reportableError = $reportableError ?: $guiError;
+ // Do the subqueue updates for $update until there are none
+ while ( self::$executeContext['subqueue'] ) {
+ $subUpdate = reset( self::$executeContext['subqueue'] );
+ $firstKey = key( self::$executeContext['subqueue'] );
+ unset( self::$executeContext['subqueue'][$firstKey] );
+
+ if ( $subUpdate instanceof DataUpdate ) {
+ $subUpdate->setTransactionTicket( $ticket );
+ }
+
+ $guiError = self::runUpdate( $subUpdate, $lbFactory, $mode, $stage );
+ $reportableError = $reportableError ?: $guiError;
+ }
+ } finally {
+ // Make sure we always clean up the context.
+ // Losing updates while rewinding the stack is acceptable,
+ // losing updates that are added later is not.
+ self::$executeContext = null;
}
- self::$executeContext = null;
}
}
$guiError = $e;
}
MWExceptionHandler::rollbackMasterChangesAndLog( $e );
+
+ // VW-style hack to work around T190178, so we can make sure
+ // PageMetaDataUpdater doesn't throw exceptions.
+ if ( defined( 'MW_PHPUNIT_TEST' ) ) {
+ throw $e;
+ }
}
return $guiError;
/**
* Run all deferred updates immediately if there are no DB writes active
*
- * If $mode is 'run' but there are busy databates, EnqueueableDataUpdate
- * tasks will be enqueued anyway for the sake of progress.
+ * If there are many deferred updates pending, $mode is 'run', and there
+ * are still busy LBFactory database handles, then any EnqueueableDataUpdate
+ * tasks might be enqueued as jobs to be executed later.
*
* @param string $mode Use "enqueue" to use the job queue when possible
* @return bool Whether updates were allowed to run
*/
private static function areDatabaseTransactionsActive() {
$lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
- if ( $lbFactory->hasTransactionRound() ) {
+ if ( $lbFactory->hasTransactionRound() || !$lbFactory->isReadyForRoundOperations() ) {
return true;
}