/** * Immediately run/queue a list of updates * * @param DeferrableUpdate[] &$queue List of DeferrableUpdate objects * @param string $mode Use "enqueue" to use the job queue when possible * @param integer $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) { $services = MediaWikiServices::getInstance(); $stats = $services->getStatsdDataFactory(); $lbFactory = $services->getDBLoadBalancerFactory(); $method = RequestContext::getMain()->getRequest()->getMethod(); $ticket = $lbFactory->getEmptyTransactionTicket(__METHOD__); /** @var ErrorPageError $reportableError */ $reportableError = null; /** @var DeferrableUpdate[] $updates Snapshot of queue */ $updates = $queue; // Keep doing rounds of updates until none get enqueued... while ($updates) { $queue = []; // clear the queue if ($mode === 'enqueue') { try { // Push enqueuable updates to the job queue and get the rest $updates = self::enqueueUpdates($updates); } catch (Exception $e) { // Let other updates have a chance to run if this failed MWExceptionHandler::rollbackMasterChangesAndLog($e); } } // Order will be DataUpdate followed by generic DeferrableUpdate tasks $updatesByType = ['data' => [], 'generic' => []]; foreach ($updates as $du) { if ($du instanceof DataUpdate) { $du->setTransactionTicket($ticket); $updatesByType['data'][] = $du; } else { $updatesByType['generic'][] = $du; } $name = $du instanceof DeferrableCallback ? get_class($du) . '-' . $du->getOrigin() : get_class($du); $stats->increment('deferred_updates.' . $method . '.' . $name); } // Execute all remaining tasks... foreach ($updatesByType as $updatesForType) { foreach ($updatesForType as $update) { self::$executeContext = ['update' => $update, 'stage' => $stage, 'subqueue' => []]; /** @var DeferrableUpdate $update */ $guiError = self::runUpdate($update, $lbFactory, $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, $stage); $reportableError = $reportableError ?: $guiError; } self::$executeContext = null; } } $updates = $queue; // new snapshot of queue (check for new entries) } if ($reportableError) { throw $reportableError; // throw the first of any GUI errors } }