* If not specified, the primary DB cluster for the wiki will be used.
* This can be overridden with a custom cluster so that DB handles will
* be retrieved via LBFactory::getExternalLB() and getConnection().
+ * - wanCache : An instance of WANObjectCache to use for caching.
* @param array $params
*/
protected function __construct( array $params ) {
$this->cluster = $params['cluster'];
}
- $this->cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
+ $this->cache = $params['wanCache'] ?? WANObjectCache::newEmpty();
}
protected function supportedOrders() {
'job', '1', [ 'job_cmd' => $this->type, 'job_token' => '' ], __METHOD__
);
} catch ( DBError $e ) {
- $this->throwDBException( $e );
+ throw $this->getDBException( $e );
}
return !$found;
__METHOD__
);
} catch ( DBError $e ) {
- $this->throwDBException( $e );
+ throw $this->getDBException( $e );
}
$this->cache->set( $key, $size, self::CACHE_TTL_SHORT );
__METHOD__
);
} catch ( DBError $e ) {
- $this->throwDBException( $e );
+ throw $this->getDBException( $e );
}
$this->cache->set( $key, $count, self::CACHE_TTL_SHORT );
__METHOD__
);
} catch ( DBError $e ) {
- $this->throwDBException( $e );
+ throw $this->getDBException( $e );
}
$this->cache->set( $key, $count, self::CACHE_TTL_SHORT );
foreach ( array_chunk( $rows, 50 ) as $rowBatch ) {
$dbw->insert( 'job', $rowBatch, $method );
}
- JobQueue::incrStats( 'inserts', $this->type, count( $rows ) );
- JobQueue::incrStats( 'dupe_inserts', $this->type,
+ $this->incrStats( 'inserts', $this->type, count( $rows ) );
+ $this->incrStats( 'dupe_inserts', $this->type,
count( $rowSet ) + count( $rowList ) - count( $rows )
);
} catch ( DBError $e ) {
- $this->throwDBException( $e );
+ throw $this->getDBException( $e );
}
if ( $flags & self::QOS_ATOMIC ) {
$dbw->endAtomic( $method );
/**
* @see JobQueue::doPop()
- * @return Job|bool
+ * @return RunnableJob|bool
*/
protected function doPop() {
$dbw = $this->getMasterDB();
if ( !$row ) {
break; // nothing to do
}
- JobQueue::incrStats( 'pops', $this->type );
+ $this->incrStats( 'pops', $this->type );
+
// Get the job object from the row...
- $title = Title::makeTitle( $row->job_namespace, $row->job_title );
- $job = Job::factory( $row->job_cmd, $title,
- self::extractBlob( $row->job_params ) );
- $job->metadata['id'] = $row->job_id;
- $job->metadata['timestamp'] = $row->job_timestamp;
+ $job = $this->jobFromRow( $row );
break; // done
} while ( true );
$this->recycleAndDeleteStaleJobs();
}
} catch ( DBError $e ) {
- $this->throwDBException( $e );
+ throw $this->getDBException( $e );
}
return $job;
// Check cache to see if the queue has <= OFFSET items
$tinyQueue = $this->cache->get( $this->getCacheKey( 'small' ) );
- $row = false; // the row acquired
$invertedDirection = false; // whether one job_random direction was already scanned
// This uses a replication safe method for acquiring jobs. One could use UPDATE+LIMIT
// instead, but that either uses ORDER BY (in which case it deadlocks in MySQL) or is
/**
* @see JobQueue::doAck()
- * @param Job $job
+ * @param RunnableJob $job
* @throws MWException
*/
- protected function doAck( Job $job ) {
- if ( !isset( $job->metadata['id'] ) ) {
+ protected function doAck( RunnableJob $job ) {
+ $id = $job->getMetadata( 'id' );
+ if ( $id === null ) {
throw new MWException( "Job of type '{$job->getType()}' has no ID." );
}
$scope = $this->getScopedNoTrxFlag( $dbw );
try {
// Delete a row with a single DELETE without holding row locks over RTTs...
- $dbw->delete( 'job',
- [ 'job_cmd' => $this->type, 'job_id' => $job->metadata['id'] ], __METHOD__ );
+ $dbw->delete(
+ 'job',
+ [ 'job_cmd' => $this->type, 'job_id' => $id ],
+ __METHOD__
+ );
- JobQueue::incrStats( 'acks', $this->type );
+ $this->incrStats( 'acks', $this->type );
} catch ( DBError $e ) {
- $this->throwDBException( $e );
+ throw $this->getDBException( $e );
}
}
try {
$dbw->delete( 'job', [ 'job_cmd' => $this->type ] );
} catch ( DBError $e ) {
- $this->throwDBException( $e );
+ throw $this->getDBException( $e );
}
return true;
return new MappedIterator(
$dbr->select( 'job', self::selectFields(), $conds ),
function ( $row ) {
- $job = Job::factory(
- $row->job_cmd,
- Title::makeTitle( $row->job_namespace, $row->job_title ),
- strlen( $row->job_params ) ? unserialize( $row->job_params ) : []
- );
- $job->metadata['id'] = $row->job_id;
- $job->metadata['timestamp'] = $row->job_timestamp;
-
- return $job;
+ return $this->jobFromRow( $row );
}
);
} catch ( DBError $e ) {
- $this->throwDBException( $e );
+ throw $this->getDBException( $e );
}
}
);
$affected = $dbw->affectedRows();
$count += $affected;
- JobQueue::incrStats( 'recycles', $this->type, $affected );
- $this->aggr->notifyQueueNonEmpty( $this->domain, $this->type );
+ $this->incrStats( 'recycles', $this->type, $affected );
}
}
$dbw->delete( 'job', [ 'job_id' => $ids ], __METHOD__ );
$affected = $dbw->affectedRows();
$count += $affected;
- JobQueue::incrStats( 'abandons', $this->type, $affected );
+ $this->incrStats( 'abandons', $this->type, $affected );
}
$dbw->unlock( "jobqueue-recycle-{$this->type}", __METHOD__ );
} catch ( DBError $e ) {
- $this->throwDBException( $e );
+ throw $this->getDBException( $e );
}
return $count;
return [
// Fields that describe the nature of the job
'job_cmd' => $job->getType(),
- 'job_namespace' => $job->getTitle()->getNamespace(),
- 'job_title' => $job->getTitle()->getDBkey(),
+ 'job_namespace' => $job->getParams()['namespace'] ?? NS_SPECIAL,
+ 'job_title' => $job->getParams()['title'] ?? '',
'job_params' => self::makeBlob( $job->getParams() ),
// Additional job metadata
'job_timestamp' => $db->timestamp(),
}
/**
- * @param string $blob
- * @return bool|mixed
+ * @param stdClass $row
+ * @return RunnableJob|null
*/
- protected static function extractBlob( $blob ) {
- if ( (string)$blob !== '' ) {
- return unserialize( $blob );
- } else {
- return false;
+ protected function jobFromRow( $row ) {
+ $params = ( (string)$row->job_params !== '' ) ? unserialize( $row->job_params ) : [];
+ if ( !is_array( $params ) ) { // this shouldn't happen
+ throw new UnexpectedValueException(
+ "Could not unserialize job with ID '{$row->job_id}'." );
}
+
+ $params += [ 'namespace' => $row->job_namespace, 'title' => $row->job_title ];
+ $job = $this->factoryJob( $row->job_cmd, $params );
+ $job->setMetadata( 'id', $row->job_id );
+ $job->setMetadata( 'timestamp', $row->job_timestamp );
+
+ return $job;
}
/**
* @param DBError $e
- * @throws JobQueueError
+ * @return JobQueueError
*/
- protected function throwDBException( DBError $e ) {
- throw new JobQueueError( get_class( $e ) . ": " . $e->getMessage() );
+ protected function getDBException( DBError $e ) {
+ return new JobQueueError( get_class( $e ) . ": " . $e->getMessage() );
}
/**