MediaWiki master
Job.php
Go to the documentation of this file.
1<?php
7namespace MediaWiki\JobQueue;
8
9use InvalidArgumentException;
15use Wikimedia\Timestamp\TimestampFormat as TS;
16
28abstract class Job implements RunnableJob {
30 public $command;
31
33 public $params;
34
36 public $metadata = [];
37
39 protected $title;
40
42 protected $removeDuplicates = false;
43
45 protected $error;
46
48 protected $teardownCallbacks = [];
49
51 protected $executionFlags = 0;
52
62 public static function factory( $command, $params = [] ) {
63 $factory = MediaWikiServices::getInstance()->getJobFactory();
64
65 // FIXME: fix handling for legacy signature!
66 // @phan-suppress-next-line PhanParamTooFewUnpack one argument is known to be present.
67 return $factory->newJob( ...func_get_args() );
68 }
69
77 public function __construct( $command, $params = null ) {
78 if ( $params instanceof PageReference ) {
79 // Backwards compatibility for old signature ($command, $title, $params)
80 $page = $params;
81 $params = func_num_args() >= 3 ? func_get_arg( 2 ) : [];
82 } else {
83 // Newer jobs may choose to not have a top-level title (e.g. GenericParameterJob)
84 $page = null;
85 }
86
87 if ( !is_array( $params ) ) {
88 throw new InvalidArgumentException( '$params must be an array' );
89 }
90
91 if (
92 $page &&
93 !isset( $params['namespace'] ) &&
94 !isset( $params['title'] )
95 ) {
96 // When constructing this class for submitting to the queue,
97 // normalise the $page arg of old job classes as part of $params.
98 $params['namespace'] = $page->getNamespace();
99 $params['title'] = $page->getDBkey();
100 }
101
102 $this->command = $command;
103 $this->params = $params + [
104 'requestId' => Telemetry::getInstance()->getRequestId(),
105 ];
106
107 if ( $this->title === null ) {
108 // Set this field for access via getTitle().
109 $this->title = ( isset( $params['namespace'] ) && isset( $params['title'] ) )
110 ? Title::makeTitle( $params['namespace'], $params['title'] )
111 // GenericParameterJob classes without namespace/title params
112 // should not use getTitle(). Set an invalid title as placeholder.
114 }
115 }
116
121 public function hasExecutionFlag( $flag ) {
122 return ( $this->executionFlags & $flag ) === $flag;
123 }
124
129 public function getType() {
130 return $this->command;
131 }
132
136 final public function getTitle() {
137 return $this->title;
138 }
139
144 public function getParams() {
145 return $this->params;
146 }
147
154 public function getMetadata( $field = null ) {
155 if ( $field === null ) {
156 return $this->metadata;
157 }
158
159 return $this->metadata[$field] ?? null;
160 }
161
169 public function setMetadata( $field, $value ) {
170 $old = $this->getMetadata( $field );
171 if ( $value === null ) {
172 unset( $this->metadata[$field] );
173 } else {
174 $this->metadata[$field] = $value;
175 }
176
177 return $old;
178 }
179
185 public function getReleaseTimestamp() {
186 $time = wfTimestampOrNull( TS::UNIX, $this->params['jobReleaseTimestamp'] ?? null );
187 return $time ? (int)$time : null;
188 }
189
194 public function getQueuedTimestamp() {
195 $time = wfTimestampOrNull( TS::UNIX, $this->metadata['timestamp'] ?? null );
196 return $time ? (int)$time : null;
197 }
198
203 public function getRequestId() {
204 return $this->params['requestId'] ?? null;
205 }
206
211 public function getReadyTimestamp() {
212 return $this->getReleaseTimestamp() ?: $this->getQueuedTimestamp();
213 }
214
228 public function ignoreDuplicates() {
230 }
231
236 public function allowRetries() {
237 return true;
238 }
239
244 public function workItemCount() {
245 return 1;
246 }
247
258 public function getDeduplicationInfo() {
259 $info = [
260 'type' => $this->getType(),
261 'params' => $this->getParams()
262 ];
263 if ( is_array( $info['params'] ) ) {
264 // Identical jobs with different "root" jobs should count as duplicates
265 unset( $info['params']['rootJobSignature'] );
266 unset( $info['params']['rootJobTimestamp'] );
267 // Likewise for jobs with different delay times
268 unset( $info['params']['jobReleaseTimestamp'] );
269 // Identical jobs from different requests should count as duplicates
270 unset( $info['params']['requestId'] );
271 // Queues pack and hash this array, so normalize the order
272 ksort( $info['params'] );
273 }
274
275 return $info;
276 }
277
297 public static function newRootJobParams( $key ) {
298 return [
299 'rootJobIsSelf' => true,
300 'rootJobSignature' => sha1( $key ),
301 'rootJobTimestamp' => wfTimestampNow()
302 ];
303 }
304
311 public function getRootJobParams() {
312 return [
313 'rootJobSignature' => $this->params['rootJobSignature'] ?? null,
314 'rootJobTimestamp' => $this->params['rootJobTimestamp'] ?? null
315 ];
316 }
317
324 public function hasRootJobParams() {
325 return isset( $this->params['rootJobSignature'] )
326 && isset( $this->params['rootJobTimestamp'] );
327 }
328
334 public function isRootJob() {
335 return $this->hasRootJobParams() && !empty( $this->params['rootJobIsSelf'] );
336 }
337
344 protected function addTeardownCallback( $callback ) {
345 $this->teardownCallbacks[] = $callback;
346 }
347
352 public function teardown( $status ) {
353 foreach ( $this->teardownCallbacks as $callback ) {
354 $callback( $status );
355 }
356 }
357
362 public function toString() {
363 $paramString = '';
364 if ( $this->params ) {
365 foreach ( $this->params as $key => $value ) {
366 if ( $paramString != '' ) {
367 $paramString .= ' ';
368 }
369 if ( is_array( $value ) ) {
370 $filteredValue = [];
371 foreach ( $value as $k => $v ) {
372 $json = FormatJson::encode( $v );
373 if ( $json === false || mb_strlen( $json ) > 512 ) {
374 $filteredValue[$k] = get_debug_type( $v ) . '(...)';
375 } else {
376 $filteredValue[$k] = $v;
377 }
378 }
379 if ( count( $filteredValue ) <= 10 ) {
380 $value = FormatJson::encode( $filteredValue );
381 } else {
382 $value = "array(" . count( $value ) . ")";
383 }
384 } elseif ( is_object( $value ) && !method_exists( $value, '__toString' ) ) {
385 $value = get_debug_type( $value );
386 }
387
388 $flatValue = (string)$value;
389 if ( mb_strlen( $flatValue ) > 1024 ) {
390 $flatValue = "string(" . mb_strlen( $value ) . ")";
391 }
392
393 // Remove newline characters from the value, since
394 // newlines indicate new job lines in log files
395 $flatValue = preg_replace( '/\s+/', ' ', $flatValue );
396
397 $paramString .= "$key={$flatValue}";
398 }
399 }
400
401 $metaString = '';
402 foreach ( $this->metadata as $key => $value ) {
403 if ( is_scalar( $value ) && mb_strlen( $value ) < 1024 ) {
404 $metaString .= ( $metaString ? ",$key=$value" : "$key=$value" );
405 }
406 }
407
408 $s = $this->command;
409 if ( is_object( $this->title ) ) {
410 $s .= ' ' . $this->title->getPrefixedDBkey();
411 }
412 if ( $paramString != '' ) {
413 $s .= " $paramString";
414 }
415 if ( $metaString != '' ) {
416 $s .= " ($metaString)";
417 }
418
419 return $s;
420 }
421
425 protected function setLastError( $error ) {
426 $this->error = $error;
427 }
428
433 public function getLastError() {
434 return $this->error;
435 }
436}
437
439class_alias( Job::class, 'Job' );
const NS_SPECIAL
Definition Defines.php:40
wfTimestampOrNull( $outputtype=TS::UNIX, $ts=null)
Return a formatted timestamp, or null if input is null.
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
makeTitle( $linkId)
Convert a link ID to a Title.to override Title
Service for handling telemetry data.
Definition Telemetry.php:15
Describe and execute a background job.
Definition Job.php:28
setMetadata( $field, $value)
Definition Job.php:169
int $executionFlags
Bitfield of JOB_* class constants.
Definition Job.php:51
callable[] $teardownCallbacks
Definition Job.php:48
ignoreDuplicates()
Whether the queue should reject insertion of this job if a duplicate exists.
Definition Job.php:228
getRequestId()
string|null Id of the request that created this job. Follows jobs recursively, allowing to track the ...
Definition Job.php:203
array $params
Array of job parameters.
Definition Job.php:33
getDeduplicationInfo()
Subclasses may need to override this to make duplication detection work.
Definition Job.php:258
toString()
string Debugging string describing the job
Definition Job.php:362
getReadyTimestamp()
int|null UNIX timestamp of when the job was runnable, or null 1.26
Definition Job.php:211
addTeardownCallback( $callback)
Definition Job.php:344
teardown( $status)
Definition Job.php:352
getMetadata( $field=null)
Definition Job.php:154
getType()
string Job type that defines what sort of changes this job makes
Definition Job.php:129
allowRetries()
Whether to retry execution of this job if run() returned false or threw an exception....
Definition Job.php:236
setLastError( $error)
Definition Job.php:425
getParams()
array Parameters that specify sources, targets, and options for execution
Definition Job.php:144
hasExecutionFlag( $flag)
bool 1.31
Definition Job.php:121
bool $removeDuplicates
Expensive jobs may set this to true.
Definition Job.php:42
static newRootJobParams( $key)
Get "root job" parameters for a task.
Definition Job.php:297
getLastError()
string
Definition Job.php:433
static factory( $command, $params=[])
Create the appropriate object to handle a specific job.
Definition Job.php:62
array $metadata
Additional queue metadata.
Definition Job.php:36
__construct( $command, $params=null)
Definition Job.php:77
string $error
Text for error that occurred last.
Definition Job.php:45
JSON formatter wrapper class.
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
Represents a title within MediaWiki.
Definition Title.php:69
Job that has a run() method and metadata accessors for JobQueue::pop() and JobQueue::ack().
Interface for objects (potentially) representing a page that can be viewable and linked to on a wiki.