'JobQueueFederated' => __DIR__ . '/includes/jobqueue/JobQueueFederated.php',
'JobQueueGroup' => __DIR__ . '/includes/jobqueue/JobQueueGroup.php',
'JobQueueMemory' => __DIR__ . '/includes/jobqueue/JobQueueMemory.php',
+ 'JobQueueReadOnlyError' => __DIR__ . '/includes/jobqueue/JobQueue.php',
'JobQueueRedis' => __DIR__ . '/includes/jobqueue/JobQueueRedis.php',
'JobRunner' => __DIR__ . '/includes/jobqueue/JobRunner.php',
'JobSpecification' => __DIR__ . '/includes/jobqueue/JobSpecification.php',
abstract class JobQueue {
/** @var string Wiki ID */
protected $wiki;
-
/** @var string Job type */
protected $type;
-
/** @var string Job priority for pop() */
protected $order;
-
/** @var int Time to live in seconds */
protected $claimTTL;
-
/** @var int Maximum number of times to try a job */
protected $maxTries;
+ /** @var string|bool Read only rationale (or false if r/w) */
+ protected $readOnlyReason;
/** @var BagOStuff */
protected $dupCache;
$this->aggr = isset( $params['aggregator'] )
? $params['aggregator']
: new JobQueueAggregatorNull( [] );
+ $this->readOnlyReason = isset( $params['readOnlyReason'] )
+ ? $params['readOnlyReason']
+ : false;
}
/**
* but not acknowledged as completed after this many seconds. Recycling
* of jobs simply means re-inserting them into the queue. Jobs can be
* attempted up to three times before being discarded.
+ * - readOnlyReason : Set this to a string to make the queue read-only.
*
* Queue classes should throw an exception if they do not support the options given.
*
return $this->supportsDelayedJobs();
}
+ /**
+ * @return string|bool Read-only rational or false if r/w
+ * @since 1.27
+ */
+ public function getReadOnlyReason() {
+ return $this->readOnlyReason;
+ }
+
/**
* Quickly check if the queue has no available (unacquired, non-delayed) jobs.
* Queue classes should use caching if they are any slower without memcached.
* @throws MWException
*/
final public function batchPush( array $jobs, $flags = 0 ) {
+ $this->assertNotReadOnly();
+
if ( !count( $jobs ) ) {
return; // nothing to do
}
final public 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] ) ) {
* @throws MWException
*/
final public function ack( Job $job ) {
+ $this->assertNotReadOnly();
if ( $job->getType() !== $this->type ) {
throw new MWException( "Got '{$job->getType()}' job; expected '{$this->type}'." );
}
+
$this->doAck( $job );
}
* @return bool
*/
final public function deduplicateRootJob( IJobSpecification $job ) {
+ $this->assertNotReadOnly();
if ( $job->getType() !== $this->type ) {
throw new MWException( "Got '{$job->getType()}' job; expected '{$this->type}'." );
}
- $ok = $this->doDeduplicateRootJob( $job );
- return $ok;
+ return $this->doDeduplicateRootJob( $job );
}
/**
* @return void
*/
final public function delete() {
+ $this->assertNotReadOnly();
+
$this->doDelete();
}
return null; // not supported
}
+ /**
+ * @throws JobQueueReadOnlyError
+ */
+ protected function assertNotReadOnly() {
+ if ( $this->readOnlyReason !== false ) {
+ throw new JobQueueReadOnlyError( "Job queue is read-only: {$this->readOnlyReason}" );
+ }
+ }
+
/**
* Call wfIncrStats() for the queue overall and for the queue type
*
class JobQueueConnectionError extends JobQueueError {
}
+
+class JobQueueReadOnlyError extends JobQueueError {
+
+}
/** @var string Wiki ID */
protected $wiki;
+ /** @var string|bool Read only rationale (or false if r/w) */
+ protected $readOnlyReason;
/** @var array Map of (bucket => (queue => JobQueue, types => list of types) */
protected $coalescedQueues;
/**
* @param string $wiki Wiki ID
+ * @param string|bool $readOnlyReason Read-only reason or false
*/
- protected function __construct( $wiki ) {
+ protected function __construct( $wiki, $readOnlyReason ) {
$this->wiki = $wiki;
+ $this->readOnlyReason = $readOnlyReason;
$this->cache = new ProcessCacheLRU( 10 );
}
public static function singleton( $wiki = false ) {
$wiki = ( $wiki === false ) ? wfWikiID() : $wiki;
if ( !isset( self::$instances[$wiki] ) ) {
- self::$instances[$wiki] = new self( $wiki );
+ self::$instances[$wiki] = new self( $wiki, wfConfiguredReadOnlyReason() );
}
return self::$instances[$wiki];
$conf = $conf + $wgJobTypeConf['default'];
}
$conf['aggregator'] = JobQueueAggregator::singleton();
+ if ( $this->readOnlyReason !== false ) {
+ $conf['readOnlyReason'] = $this->readOnlyReason;
+ }
return JobQueue::factory( $conf );
}