PageRenderTime 38ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 0ms

/includes/jobqueue/Job.php

https://gitlab.com/link233/bootmw
PHP | 398 lines | 179 code | 44 blank | 175 comment | 23 complexity | be8d6dacce48205ace387ad913208715 MD5 | raw file
  1. <?php
  2. /**
  3. * Job queue task base code.
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program; if not, write to the Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. * http://www.gnu.org/copyleft/gpl.html
  19. *
  20. * @file
  21. * @defgroup JobQueue JobQueue
  22. */
  23. /**
  24. * Class to both describe a background job and handle jobs.
  25. * The queue aspects of this class are now deprecated.
  26. * Using the class to push jobs onto queues is deprecated (use JobSpecification).
  27. *
  28. * @ingroup JobQueue
  29. */
  30. abstract class Job implements IJobSpecification {
  31. /** @var string */
  32. public $command;
  33. /** @var array Array of job parameters */
  34. public $params;
  35. /** @var array Additional queue metadata */
  36. public $metadata = [];
  37. /** @var Title */
  38. protected $title;
  39. /** @var bool Expensive jobs may set this to true */
  40. protected $removeDuplicates;
  41. /** @var string Text for error that occurred last */
  42. protected $error;
  43. /** @var callable[] */
  44. protected $teardownCallbacks = [];
  45. /**
  46. * Run the job
  47. * @return bool Success
  48. */
  49. abstract public function run();
  50. /**
  51. * Create the appropriate object to handle a specific job
  52. *
  53. * @param string $command Job command
  54. * @param Title $title Associated title
  55. * @param array $params Job parameters
  56. * @throws MWException
  57. * @return Job
  58. */
  59. public static function factory( $command, Title $title, $params = [] ) {
  60. global $wgJobClasses;
  61. if ( isset( $wgJobClasses[$command] ) ) {
  62. $class = $wgJobClasses[$command];
  63. $job = new $class( $title, $params );
  64. $job->command = $command;
  65. return $job;
  66. }
  67. throw new InvalidArgumentException( "Invalid job command '{$command}'" );
  68. }
  69. /**
  70. * @param string $command
  71. * @param Title $title
  72. * @param array|bool $params Can not be === true
  73. */
  74. public function __construct( $command, $title, $params = false ) {
  75. $this->command = $command;
  76. $this->title = $title;
  77. $this->params = is_array( $params ) ? $params : []; // sanity
  78. // expensive jobs may set this to true
  79. $this->removeDuplicates = false;
  80. if ( !isset( $this->params['requestId'] ) ) {
  81. $this->params['requestId'] = WebRequest::getRequestId();
  82. }
  83. }
  84. /**
  85. * Batch-insert a group of jobs into the queue.
  86. * This will be wrapped in a transaction with a forced commit.
  87. *
  88. * This may add duplicate at insert time, but they will be
  89. * removed later on, when the first one is popped.
  90. *
  91. * @param Job[] $jobs Array of Job objects
  92. * @return bool
  93. * @deprecated since 1.21
  94. */
  95. public static function batchInsert( $jobs ) {
  96. wfDeprecated( __METHOD__, '1.21' );
  97. JobQueueGroup::singleton()->push( $jobs );
  98. return true;
  99. }
  100. /**
  101. * @return string
  102. */
  103. public function getType() {
  104. return $this->command;
  105. }
  106. /**
  107. * @return Title
  108. */
  109. public function getTitle() {
  110. return $this->title;
  111. }
  112. /**
  113. * @return array
  114. */
  115. public function getParams() {
  116. return $this->params;
  117. }
  118. /**
  119. * @return int|null UNIX timestamp to delay running this job until, otherwise null
  120. * @since 1.22
  121. */
  122. public function getReleaseTimestamp() {
  123. return isset( $this->params['jobReleaseTimestamp'] )
  124. ? wfTimestampOrNull( TS_UNIX, $this->params['jobReleaseTimestamp'] )
  125. : null;
  126. }
  127. /**
  128. * @return int|null UNIX timestamp of when the job was queued, or null
  129. * @since 1.26
  130. */
  131. public function getQueuedTimestamp() {
  132. return isset( $this->metadata['timestamp'] )
  133. ? wfTimestampOrNull( TS_UNIX, $this->metadata['timestamp'] )
  134. : null;
  135. }
  136. /**
  137. * @return string|null Id of the request that created this job. Follows
  138. * jobs recursively, allowing to track the id of the request that started a
  139. * job when jobs insert jobs which insert other jobs.
  140. * @since 1.27
  141. */
  142. public function getRequestId() {
  143. return isset( $this->params['requestId'] )
  144. ? $this->params['requestId']
  145. : null;
  146. }
  147. /**
  148. * @return int|null UNIX timestamp of when the job was runnable, or null
  149. * @since 1.26
  150. */
  151. public function getReadyTimestamp() {
  152. return $this->getReleaseTimestamp() ?: $this->getQueuedTimestamp();
  153. }
  154. /**
  155. * Whether the queue should reject insertion of this job if a duplicate exists
  156. *
  157. * This can be used to avoid duplicated effort or combined with delayed jobs to
  158. * coalesce updates into larger batches. Claimed jobs are never treated as
  159. * duplicates of new jobs, and some queues may allow a few duplicates due to
  160. * network partitions and fail-over. Thus, additional locking is needed to
  161. * enforce mutual exclusion if this is really needed.
  162. *
  163. * @return bool
  164. */
  165. public function ignoreDuplicates() {
  166. return $this->removeDuplicates;
  167. }
  168. /**
  169. * @return bool Whether this job can be retried on failure by job runners
  170. * @since 1.21
  171. */
  172. public function allowRetries() {
  173. return true;
  174. }
  175. /**
  176. * @return int Number of actually "work items" handled in this job
  177. * @see $wgJobBackoffThrottling
  178. * @since 1.23
  179. */
  180. public function workItemCount() {
  181. return 1;
  182. }
  183. /**
  184. * Subclasses may need to override this to make duplication detection work.
  185. * The resulting map conveys everything that makes the job unique. This is
  186. * only checked if ignoreDuplicates() returns true, meaning that duplicate
  187. * jobs are supposed to be ignored.
  188. *
  189. * @return array Map of key/values
  190. * @since 1.21
  191. */
  192. public function getDeduplicationInfo() {
  193. $info = [
  194. 'type' => $this->getType(),
  195. 'namespace' => $this->getTitle()->getNamespace(),
  196. 'title' => $this->getTitle()->getDBkey(),
  197. 'params' => $this->getParams()
  198. ];
  199. if ( is_array( $info['params'] ) ) {
  200. // Identical jobs with different "root" jobs should count as duplicates
  201. unset( $info['params']['rootJobSignature'] );
  202. unset( $info['params']['rootJobTimestamp'] );
  203. // Likewise for jobs with different delay times
  204. unset( $info['params']['jobReleaseTimestamp'] );
  205. // Identical jobs from different requests should count as duplicates
  206. unset( $info['params']['requestId'] );
  207. // Queues pack and hash this array, so normalize the order
  208. ksort( $info['params'] );
  209. }
  210. return $info;
  211. }
  212. /**
  213. * Get "root job" parameters for a task
  214. *
  215. * This is used to no-op redundant jobs, including child jobs of jobs,
  216. * as long as the children inherit the root job parameters. When a job
  217. * with root job parameters and "rootJobIsSelf" set is pushed, the
  218. * deduplicateRootJob() method is automatically called on it. If the
  219. * root job is only virtual and not actually pushed (e.g. the sub-jobs
  220. * are inserted directly), then call deduplicateRootJob() directly.
  221. *
  222. * @see JobQueue::deduplicateRootJob()
  223. *
  224. * @param string $key A key that identifies the task
  225. * @return array Map of:
  226. * - rootJobIsSelf : true
  227. * - rootJobSignature : hash (e.g. SHA1) that identifies the task
  228. * - rootJobTimestamp : TS_MW timestamp of this instance of the task
  229. * @since 1.21
  230. */
  231. public static function newRootJobParams( $key ) {
  232. return [
  233. 'rootJobIsSelf' => true,
  234. 'rootJobSignature' => sha1( $key ),
  235. 'rootJobTimestamp' => wfTimestampNow()
  236. ];
  237. }
  238. /**
  239. * @see JobQueue::deduplicateRootJob()
  240. * @return array
  241. * @since 1.21
  242. */
  243. public function getRootJobParams() {
  244. return [
  245. 'rootJobSignature' => isset( $this->params['rootJobSignature'] )
  246. ? $this->params['rootJobSignature']
  247. : null,
  248. 'rootJobTimestamp' => isset( $this->params['rootJobTimestamp'] )
  249. ? $this->params['rootJobTimestamp']
  250. : null
  251. ];
  252. }
  253. /**
  254. * @see JobQueue::deduplicateRootJob()
  255. * @return bool
  256. * @since 1.22
  257. */
  258. public function hasRootJobParams() {
  259. return isset( $this->params['rootJobSignature'] )
  260. && isset( $this->params['rootJobTimestamp'] );
  261. }
  262. /**
  263. * @see JobQueue::deduplicateRootJob()
  264. * @return bool Whether this is job is a root job
  265. */
  266. public function isRootJob() {
  267. return $this->hasRootJobParams() && !empty( $this->params['rootJobIsSelf'] );
  268. }
  269. /**
  270. * @param callable $callback
  271. * @since 1.27
  272. */
  273. protected function addTeardownCallback( $callback ) {
  274. $this->teardownCallbacks[] = $callback;
  275. }
  276. /**
  277. * Do any final cleanup after run(), deferred updates, and all DB commits happen
  278. *
  279. * @since 1.27
  280. */
  281. public function teardown() {
  282. foreach ( $this->teardownCallbacks as $callback ) {
  283. call_user_func( $callback );
  284. }
  285. }
  286. /**
  287. * Insert a single job into the queue.
  288. * @return bool True on success
  289. * @deprecated since 1.21
  290. */
  291. public function insert() {
  292. JobQueueGroup::singleton()->push( $this );
  293. return true;
  294. }
  295. /**
  296. * @return string
  297. */
  298. public function toString() {
  299. $paramString = '';
  300. if ( $this->params ) {
  301. foreach ( $this->params as $key => $value ) {
  302. if ( $paramString != '' ) {
  303. $paramString .= ' ';
  304. }
  305. if ( is_array( $value ) ) {
  306. $filteredValue = [];
  307. foreach ( $value as $k => $v ) {
  308. $json = FormatJson::encode( $v );
  309. if ( $json === false || mb_strlen( $json ) > 512 ) {
  310. $filteredValue[$k] = gettype( $v ) . '(...)';
  311. } else {
  312. $filteredValue[$k] = $v;
  313. }
  314. }
  315. if ( count( $filteredValue ) <= 10 ) {
  316. $value = FormatJson::encode( $filteredValue );
  317. } else {
  318. $value = "array(" . count( $value ) . ")";
  319. }
  320. } elseif ( is_object( $value ) && !method_exists( $value, '__toString' ) ) {
  321. $value = "object(" . get_class( $value ) . ")";
  322. }
  323. $flatValue = (string)$value;
  324. if ( mb_strlen( $value ) > 1024 ) {
  325. $flatValue = "string(" . mb_strlen( $value ) . ")";
  326. }
  327. $paramString .= "$key={$flatValue}";
  328. }
  329. }
  330. $metaString = '';
  331. foreach ( $this->metadata as $key => $value ) {
  332. if ( is_scalar( $value ) && mb_strlen( $value ) < 1024 ) {
  333. $metaString .= ( $metaString ? ",$key=$value" : "$key=$value" );
  334. }
  335. }
  336. $s = $this->command;
  337. if ( is_object( $this->title ) ) {
  338. $s .= " {$this->title->getPrefixedDBkey()}";
  339. }
  340. if ( $paramString != '' ) {
  341. $s .= " $paramString";
  342. }
  343. if ( $metaString != '' ) {
  344. $s .= " ($metaString)";
  345. }
  346. return $s;
  347. }
  348. protected function setLastError( $error ) {
  349. $this->error = $error;
  350. }
  351. public function getLastError() {
  352. return $this->error;
  353. }
  354. }