/** * Pop a job off of the queue. * This requires $wgJobClasses to be set for the given job type. * Outside callers should use JobQueueGroup::pop() instead of this function. * * @throws MWException * @return Job|bool Returns false if there are no jobs */ public final function pop() { global $wgJobClasses; $this->assertNotReadOnly(); if ($this->wiki !== wfWikiID()) { throw new MWException("Cannot pop '{$this->type}' job off foreign wiki queue."); } elseif (!isset($wgJobClasses[$this->type])) { // Do not pop jobs if there is no class for the queue type throw new MWException("Unrecognized job type '{$this->type}'."); } $job = $this->doPop(); if (!$job) { $this->aggr->notifyQueueEmpty($this->wiki, $this->type); } // Flag this job as an old duplicate based on its "root" job... try { if ($job && $this->isRootJobOldDuplicate($job)) { JobQueue::incrStats('dupe_pops', $this->type); $job = DuplicateJob::newFromJob($job); // convert to a no-op } } catch (Exception $e) { // don't lose jobs over this } return $job; }
/** * Pop a job off of the queue. * This requires $wgJobClasses to be set for the given job type. * Outside callers should use JobQueueGroup::pop() instead of this function. * * @throws MWException * @return Job|bool Returns false if there are no jobs */ public final function pop() { global $wgJobClasses; if ($this->wiki !== wfWikiID()) { throw new MWException("Cannot pop '{$this->type}' job off foreign wiki queue."); } elseif (!isset($wgJobClasses[$this->type])) { // Do not pop jobs if there is no class for the queue type throw new MWException("Unrecognized job type '{$this->type}'."); } wfProfileIn(__METHOD__); $job = $this->doPop(); wfProfileOut(__METHOD__); // Flag this job as an old duplicate based on its "root" job... try { if ($job && $this->isRootJobOldDuplicate($job)) { JobQueue::incrStats('job-pop-duplicate', $this->type, 1, $this->wiki); $job = DuplicateJob::newFromJob($job); // convert to a no-op } } catch (MWException $e) { // don't lose jobs over this } return $job; }
/** * @see JobQueue::doPop() * @return Job|bool */ protected function doPop() { global $wgMemc; if ($wgMemc->get($this->getCacheKey('empty')) === 'true') { return false; // queue is empty } list($dbw, $scope) = $this->getMasterDB(); $dbw->commit(__METHOD__, 'flush'); // flush existing transaction $autoTrx = $dbw->getFlag(DBO_TRX); // get current setting $dbw->clearFlag(DBO_TRX); // make each query its own transaction $scopedReset = new ScopedCallback(function () use($dbw, $autoTrx) { $dbw->setFlag($autoTrx ? DBO_TRX : 0); // restore old setting }); $uuid = wfRandomString(32); // pop attempt $job = false; // job popped off do { // retry when our row is invalid or deleted as a duplicate // Try to reserve a row in the DB... if (in_array($this->order, array('fifo', 'timestamp'))) { $row = $this->claimOldest($uuid); } else { // random first $rand = mt_rand(0, self::MAX_JOB_RANDOM); // encourage concurrent UPDATEs $gte = (bool) mt_rand(0, 1); // find rows with rand before/after $rand $row = $this->claimRandom($uuid, $rand, $gte); } // Check if we found a row to reserve... if (!$row) { $wgMemc->set($this->getCacheKey('empty'), 'true', self::CACHE_TTL_LONG); break; // nothing to do } wfIncrStats('job-pop'); // Get the job object from the row... $title = Title::makeTitleSafe($row->job_namespace, $row->job_title); if (!$title) { $dbw->delete('job', array('job_id' => $row->job_id), __METHOD__); wfDebugLog('JobQueueDB', "Row has invalid title '{$row->job_title}'."); continue; // try again } $job = Job::factory($row->job_cmd, $title, self::extractBlob($row->job_params), $row->job_id); $job->id = $row->job_id; // XXX: work around broken subclasses // Flag this job as an old duplicate based on its "root" job... if ($this->isRootJobOldDuplicate($job)) { wfIncrStats('job-pop-duplicate'); $job = DuplicateJob::newFromJob($job); // convert to a no-op } break; // done } while (true); return $job; }