/** * @param DeferrableUpdate $update * @param LBFactory $lbFactory * @param integer $stage * @return ErrorPageError|null */ private static function runUpdate(DeferrableUpdate $update, LBFactory $lbFactory, $stage) { $guiError = null; try { $fnameTrxOwner = get_class($update) . '::doUpdate'; $lbFactory->beginMasterChanges($fnameTrxOwner); $update->doUpdate(); $lbFactory->commitMasterChanges($fnameTrxOwner); } catch (Exception $e) { // Reporting GUI exceptions does not work post-send if ($e instanceof ErrorPageError && $stage === self::PRESEND) { $guiError = $e; } MWExceptionHandler::rollbackMasterChangesAndLog($e); } return $guiError; }
/** * @param Job $job * @param LBFactory $lbFactory * @param StatsdDataFactory $stats * @param float $popTime * @return array Map of status/error/timeMs */ private function executeJob(Job $job, LBFactory $lbFactory, $stats, $popTime) { $jType = $job->getType(); $msg = $job->toString() . " STARTING"; $this->logger->debug($msg); $this->debugCallback($msg); // Run the job... $rssStart = $this->getMaxRssKb(); $jobStartTime = microtime(true); try { $fnameTrxOwner = get_class($job) . '::run'; // give run() outer scope $lbFactory->beginMasterChanges($fnameTrxOwner); $status = $job->run(); $error = $job->getLastError(); $this->commitMasterChanges($lbFactory, $job, $fnameTrxOwner); // Run any deferred update tasks; doUpdates() manages transactions itself DeferredUpdates::doUpdates(); } catch (Exception $e) { MWExceptionHandler::rollbackMasterChangesAndLog($e); $status = false; $error = get_class($e) . ': ' . $e->getMessage(); } // Always attempt to call teardown() even if Job throws exception. try { $job->teardown($status); } catch (Exception $e) { MWExceptionHandler::logException($e); } // Commit all outstanding connections that are in a transaction // to get a fresh repeatable read snapshot on every connection. // Note that jobs are still responsible for handling replica DB lag. $lbFactory->flushReplicaSnapshots(__METHOD__); // Clear out title cache data from prior snapshots MediaWikiServices::getInstance()->getLinkCache()->clear(); $timeMs = intval((microtime(true) - $jobStartTime) * 1000); $rssEnd = $this->getMaxRssKb(); // Record how long jobs wait before getting popped $readyTs = $job->getReadyTimestamp(); if ($readyTs) { $pickupDelay = max(0, $popTime - $readyTs); $stats->timing('jobqueue.pickup_delay.all', 1000 * $pickupDelay); $stats->timing("jobqueue.pickup_delay.{$jType}", 1000 * $pickupDelay); } // Record root job age for jobs being run $rootTimestamp = $job->getRootJobParams()['rootJobTimestamp']; if ($rootTimestamp) { $age = max(0, $popTime - wfTimestamp(TS_UNIX, $rootTimestamp)); $stats->timing("jobqueue.pickup_root_age.{$jType}", 1000 * $age); } // Track the execution time for jobs $stats->timing("jobqueue.run.{$jType}", $timeMs); // Track RSS increases for jobs (in case of memory leaks) if ($rssStart && $rssEnd) { $stats->updateCount("jobqueue.rss_delta.{$jType}", $rssEnd - $rssStart); } if ($status === false) { $msg = $job->toString() . " t={$timeMs} error={$error}"; $this->logger->error($msg); $this->debugCallback($msg); } else { $msg = $job->toString() . " t={$timeMs} good"; $this->logger->info($msg); $this->debugCallback($msg); } return ['status' => $status, 'error' => $error, 'timeMs' => $timeMs]; }