use Wikimedia\Rdbms\IDatabase;
use MediaWiki\MediaWikiServices;
use Wikimedia\Rdbms\LBFactory;
+use Wikimedia\Rdbms\ILBFactory;
use Wikimedia\Rdbms\LoadBalancer;
/**
// 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 );
+ self::handleUpdateQueue( self::$preSendUpdates, $mode, $stageEffective );
}
if ( $stage === self::ALL || $stage == self::POSTSEND ) {
- self::execute( self::$postSendUpdates, $mode, $stageEffective );
+ self::handleUpdateQueue( self::$postSendUpdates, $mode, $stageEffective );
}
} while ( $stage === self::ALL && self::$preSendUpdates );
}
}
/**
- * Immediately run/queue a list of updates
+ * Immediately run or enqueue a list of updates
*
* @param DeferrableUpdate[] &$queue List of DeferrableUpdate objects
- * @param string $mode Use "enqueue" to use the job queue when possible
+ * @param string $mode Either "run" or "enqueue" (to use the job queue when possible)
* @param int $stage Class constant (PRESEND, POSTSEND) (since 1.28)
* @throws ErrorPageError Happens on top-level calls
* @throws Exception Happens on second-level calls
*/
- protected static function execute( array &$queue, $mode, $stage ) {
+ protected static function handleUpdateQueue( array &$queue, $mode, $stage ) {
$services = MediaWikiServices::getInstance();
$stats = $services->getStatsdDataFactory();
$lbFactory = $services->getDBLoadBalancerFactory();
self::$executeContext = [ 'stage' => $stage, 'subqueue' => [] ];
try {
/** @var DeferrableUpdate $update */
- $guiError = self::runUpdate( $update, $lbFactory, $mode, $stage );
+ $guiError = self::handleUpdate( $update, $lbFactory, $mode, $stage );
$reportableError = $reportableError ?: $guiError;
// Do the subqueue updates for $update until there are none
while ( self::$executeContext['subqueue'] ) {
$subUpdate->setTransactionTicket( $ticket );
}
- $guiError = self::runUpdate( $subUpdate, $lbFactory, $mode, $stage );
+ $guiError = self::handleUpdate( $subUpdate, $lbFactory, $mode, $stage );
$reportableError = $reportableError ?: $guiError;
}
} finally {
}
/**
+ * Run or enqueue an update
+ *
* @param DeferrableUpdate $update
* @param LBFactory $lbFactory
* @param string $mode
* @param int $stage
* @return ErrorPageError|null
*/
- private static function runUpdate(
+ private static function handleUpdate(
DeferrableUpdate $update, LBFactory $lbFactory, $mode, $stage
) {
$guiError = null;
$spec = $update->getAsJobSpecification();
$domain = $spec['domain'] ?? $spec['wiki'];
JobQueueGroup::singleton( $domain )->push( $spec['job'] );
- } elseif ( $update instanceof TransactionRoundDefiningUpdate ) {
- $update->doUpdate();
} else {
- // Run the bulk of the update now
- $fnameTrxOwner = get_class( $update ) . '::doUpdate';
- $lbFactory->beginMasterChanges( $fnameTrxOwner );
- $update->doUpdate();
- $lbFactory->commitMasterChanges( $fnameTrxOwner );
+ self::attemptUpdate( $update, $lbFactory );
}
} catch ( Exception $e ) {
// Reporting GUI exceptions does not work post-send
if ( $e instanceof ErrorPageError && $stage === self::PRESEND ) {
$guiError = $e;
}
- MWExceptionHandler::rollbackMasterChangesAndLog( $e );
+ $lbFactory->rollbackMasterChanges( __METHOD__ );
// VW-style hack to work around T190178, so we can make sure
// PageMetaDataUpdater doesn't throw exceptions.
return $guiError;
}
+ /**
+ * Attempt to run an update with the appropriate transaction round state it expects
+ *
+ * DeferredUpdate classes that wrap the execution of bundles of other DeferredUpdate
+ * instances can use this method to run the updates. Any such wrapper class should
+ * always use TRX_ROUND_ABSENT itself.
+ *
+ * @param DeferrableUpdate $update
+ * @param ILBFactory $lbFactory
+ * @since 1.34
+ */
+ public static function attemptUpdate( DeferrableUpdate $update, ILBFactory $lbFactory ) {
+ if (
+ $update instanceof TransactionRoundAwareUpdate &&
+ $update->getTransactionRoundRequirement() == $update::TRX_ROUND_ABSENT
+ ) {
+ $update->doUpdate();
+ } else {
+ // Run the bulk of the update now
+ $fnameTrxOwner = get_class( $update ) . '::doUpdate';
+ $lbFactory->beginMasterChanges( $fnameTrxOwner );
+ $update->doUpdate();
+ $lbFactory->commitMasterChanges( $fnameTrxOwner );
+ }
+ }
+
/**
* Run all deferred updates immediately if there are no DB writes active
*
* @covers DeferredUpdates::addUpdate
* @covers DeferredUpdates::push
* @covers DeferredUpdates::doUpdates
- * @covers DeferredUpdates::execute
- * @covers DeferredUpdates::runUpdate
+ * @covers DeferredUpdates::handleUpdateQueue
+ * @covers DeferredUpdates::attemptUpdate
*/
public function testAddAndRun() {
$update = $this->getMockBuilder( DeferrableUpdate::class )
/**
* @covers DeferredUpdates::doUpdates
- * @covers DeferredUpdates::execute
+ * @covers DeferredUpdates::handleUpdateQueue
* @covers DeferredUpdates::addUpdate
*/
public function testDoUpdatesWeb() {
/**
* @covers DeferredUpdates::doUpdates
- * @covers DeferredUpdates::execute
+ * @covers DeferredUpdates::handleUpdateQueue
* @covers DeferredUpdates::addUpdate
*/
public function testDoUpdatesCLI() {
/**
* @covers DeferredUpdates::doUpdates
- * @covers DeferredUpdates::execute
+ * @covers DeferredUpdates::handleUpdateQueue
* @covers DeferredUpdates::addUpdate
*/
public function testPresendAddOnPostsendRun() {
}
/**
- * @covers DeferredUpdates::runUpdate
+ * @covers DeferredUpdates::attemptUpdate
*/
public function testRunUpdateTransactionScope() {
$this->setMwGlobals( 'wgCommandLineMode', false );
}
/**
- * @covers DeferredUpdates::runUpdate
+ * @covers DeferredUpdates::attemptUpdate
* @covers TransactionRoundDefiningUpdate::getOrigin
*/
public function testRunOuterScopeUpdate() {
$ran = 0;
DeferredUpdates::addUpdate( new TransactionRoundDefiningUpdate(
- function () use ( &$ran, $lbFactory ) {
- $ran++;
- $this->assertFalse( $lbFactory->hasTransactionRound(), 'No transaction' );
- } )
+ function () use ( &$ran, $lbFactory ) {
+ $ran++;
+ $this->assertFalse( $lbFactory->hasTransactionRound(), 'No transaction' );
+ } )
);
DeferredUpdates::doUpdates();