function execute()
 {
     $totalOnly = $this->hasOption('totalonly');
     $pendingDBs = JobQueueAggregator::singleton()->getAllReadyWikiQueues();
     $sizeByWiki = array();
     // (wiki => type => count) map
     foreach ($pendingDBs as $type => $wikis) {
         foreach ($wikis as $wiki) {
             $sizeByWiki[$wiki][$type] = JobQueueGroup::singleton($wiki)->get($type)->getSize();
         }
     }
     if ($this->hasOption('grouponly')) {
         $this->output(FormatJSON::encode($sizeByWiki, true) . "\n");
     } else {
         $total = 0;
         foreach ($sizeByWiki as $wiki => $counts) {
             $count = array_sum($counts);
             if ($count > 0) {
                 if (!$totalOnly) {
                     $this->output("{$wiki} {$count}\n");
                 }
                 $total += $count;
             }
         }
         if (!$this->hasOption('nototal')) {
             $this->output("Total {$total}\n");
         }
     }
 }
Example #2
0
 public function execute()
 {
     global $wgJobTypesExcludedFromDefaultQueue;
     // job type required/picked
     if ($this->hasOption('types')) {
         $types = explode(' ', $this->getOption('types'));
     } elseif ($this->hasOption('type')) {
         $types = array($this->getOption('type'));
     } else {
         $types = false;
     }
     // Handle any required periodic queue maintenance
     $this->executeReadyPeriodicTasks();
     // Get all the queues with jobs in them
     $pendingDBs = JobQueueAggregator::singleton()->getAllReadyWikiQueues();
     if (!count($pendingDBs)) {
         return;
         // no DBs with jobs or cache is both empty and locked
     }
     do {
         $again = false;
         $candidates = array();
         // list of (type, db)
         // Flatten the tree of candidates into a flat list so that a random
         // item can be selected, weighing each queue (type/db tuple) equally.
         foreach ($pendingDBs as $type => $dbs) {
             if (is_array($types) && in_array($type, $types) || $types === false && !in_array($type, $wgJobTypesExcludedFromDefaultQueue)) {
                 foreach ($dbs as $db) {
                     $candidates[] = array($type, $db);
                 }
             }
         }
         if (!count($candidates)) {
             return;
             // no jobs for this type
         }
         list($type, $db) = $candidates[mt_rand(0, count($candidates) - 1)];
         if (JobQueueGroup::singleton($db)->isQueueDeprioritized($type)) {
             $pendingDBs[$type] = array_diff($pendingDBs[$type], array($db));
             $again = true;
         }
     } while ($again);
     if ($this->hasOption('types')) {
         $this->output($db . " " . $type . "\n");
     } else {
         $this->output($db . "\n");
     }
 }
 /**
  * Get the job queue object for a given queue type
  *
  * @param string $type
  * @return JobQueue
  */
 public function get($type)
 {
     global $wgJobTypeConf;
     $conf = array('wiki' => $this->wiki, 'type' => $type);
     if (isset($wgJobTypeConf[$type])) {
         $conf = $conf + $wgJobTypeConf[$type];
     } else {
         $conf = $conf + $wgJobTypeConf['default'];
     }
     $conf['aggregator'] = JobQueueAggregator::singleton();
     return JobQueue::factory($conf);
 }
Example #4
0
 /**
  * Get the job queue object for a given queue type
  *
  * @param string $type
  * @return JobQueue
  */
 public function get($type)
 {
     global $wgJobTypeConf;
     $conf = ['wiki' => $this->wiki, 'type' => $type];
     if (isset($wgJobTypeConf[$type])) {
         $conf = $conf + $wgJobTypeConf[$type];
     } else {
         $conf = $conf + $wgJobTypeConf['default'];
     }
     $conf['aggregator'] = JobQueueAggregator::singleton();
     if ($this->readOnlyReason !== false) {
         $conf['readOnlyReason'] = $this->readOnlyReason;
     }
     return JobQueue::factory($conf);
 }
 /**
  * Pop a job off one of the job queues
  *
  * This pops a job off a queue as specified by $wgJobTypeConf and
  * updates the aggregate job queue information cache as needed.
  *
  * @param $qtype integer|string JobQueueGroup::TYPE_DEFAULT or type string
  * @param $flags integer Bitfield of JobQueueGroup::USE_* constants
  * @return Job|bool Returns false on failure
  */
 public function pop($qtype = self::TYPE_DEFAULT, $flags = 0)
 {
     if (is_string($qtype)) {
         // specific job type
         $job = $this->get($qtype)->pop();
         if (!$job) {
             JobQueueAggregator::singleton()->notifyQueueEmpty($this->wiki, $qtype);
         }
         return $job;
     } else {
         // any job in the "default" jobs types
         if ($flags & self::USE_CACHE) {
             if (!$this->cache->has('queues-ready', 'list', self::PROC_CACHE_TTL)) {
                 $this->cache->set('queues-ready', 'list', $this->getQueuesWithJobs());
             }
             $types = $this->cache->get('queues-ready', 'list');
         } else {
             $types = $this->getQueuesWithJobs();
         }
         if ($qtype == self::TYPE_DEFAULT) {
             $types = array_intersect($types, $this->getDefaultQueueTypes());
         }
         shuffle($types);
         // avoid starvation
         foreach ($types as $type) {
             // for each queue...
             $job = $this->get($type)->pop();
             if ($job) {
                 // found
                 return $job;
             } else {
                 // not found
                 JobQueueAggregator::singleton()->notifyQueueEmpty($this->wiki, $type);
                 $this->cache->clear('queues-ready');
             }
         }
         return false;
         // no jobs found
     }
 }
Example #6
0
 /**
  * Execute any due periodic queue maintenance tasks for all queues.
  *
  * A task is "due" if the time ellapsed since the last run is greater than
  * the defined run period. Concurrent calls to this function will cause tasks
  * to be attempted twice, so they may need their own methods of mutual exclusion.
  *
  * @return int Number of tasks run
  */
 public function executeReadyPeriodicTasks()
 {
     global $wgMemc;
     list($db, $prefix) = wfSplitWikiID($this->wiki);
     $key = wfForeignMemcKey($db, $prefix, 'jobqueuegroup', 'taskruns', 'v1');
     $lastRuns = $wgMemc->get($key);
     // (queue => task => UNIX timestamp)
     $count = 0;
     $tasksRun = array();
     // (queue => task => UNIX timestamp)
     foreach ($this->getQueueTypes() as $type) {
         $queue = $this->get($type);
         foreach ($queue->getPeriodicTasks() as $task => $definition) {
             if ($definition['period'] <= 0) {
                 continue;
                 // disabled
             } elseif (!isset($lastRuns[$type][$task]) || $lastRuns[$type][$task] < time() - $definition['period']) {
                 try {
                     if (call_user_func($definition['callback']) !== null) {
                         $tasksRun[$type][$task] = time();
                         ++$count;
                     }
                 } catch (JobQueueError $e) {
                     MWExceptionHandler::logException($e);
                 }
             }
         }
         // The tasks may have recycled jobs or release delayed jobs into the queue
         if (isset($tasksRun[$type]) && !$queue->isEmpty()) {
             JobQueueAggregator::singleton()->notifyQueueNonEmpty($this->wiki, $type);
         }
     }
     $wgMemc->merge($key, function ($cache, $key, $lastRuns) use($tasksRun) {
         if (is_array($lastRuns)) {
             foreach ($tasksRun as $type => $tasks) {
                 foreach ($tasks as $task => $timestamp) {
                     if (!isset($lastRuns[$type][$task]) || $timestamp > $lastRuns[$type][$task]) {
                         $lastRuns[$type][$task] = $timestamp;
                     }
                 }
             }
         } else {
             $lastRuns = $tasksRun;
         }
         return $lastRuns;
     });
     return $count;
 }