Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
44.44% covered (danger)
44.44%
16 / 36
50.00% covered (danger)
50.00%
2 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
ManageJobs
44.44% covered (danger)
44.44%
16 / 36
50.00% covered (danger)
50.00%
2 / 4
31.75
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 execute
88.89% covered (warning)
88.89%
8 / 9
0.00% covered (danger)
0.00%
0 / 1
3.01
 delete
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 repushAbandoned
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
42
1<?php
2/**
3 * Maintenance script that handles managing job queue admin tasks
4 *
5 * @license GPL-2.0-or-later
6 * @file
7 * @ingroup Maintenance
8 */
9
10use MediaWiki\JobQueue\Job;
11use MediaWiki\JobQueue\JobQueue;
12use MediaWiki\Maintenance\Maintenance;
13use Wikimedia\Timestamp\TimestampFormat as TS;
14
15// @codeCoverageIgnoreStart
16require_once __DIR__ . '/Maintenance.php';
17// @codeCoverageIgnoreEnd
18
19/**
20 * Maintenance script that handles managing job queue admin tasks (re-push, delete, ...)
21 *
22 * @ingroup Maintenance
23 */
24class ManageJobs extends Maintenance {
25    public function __construct() {
26        parent::__construct();
27        $this->addDescription( 'Perform administrative tasks on a job queue' );
28        $this->addOption( 'type', 'Job type', true, true );
29        $this->addOption( 'action', 'Queue operation ("delete", "repush-abandoned")', true, true );
30        $this->setBatchSize( 100 );
31    }
32
33    public function execute() {
34        $type = $this->getOption( 'type' );
35        $action = $this->getOption( 'action' );
36
37        $group = $this->getServiceContainer()->getJobQueueGroup();
38        $queue = $group->get( $type );
39
40        if ( $action === 'delete' ) {
41            $this->delete( $queue );
42        } elseif ( $action === 'repush-abandoned' ) {
43            $this->repushAbandoned( $queue );
44        } else {
45            $this->fatalError( "Invalid action '$action'." );
46        }
47    }
48
49    private function delete( JobQueue $queue ) {
50        $this->output( "Queue has {$queue->getSize()} job(s); deleting...\n" );
51        $queue->delete();
52        $this->output( "Done; current size is {$queue->getSize()} job(s).\n" );
53    }
54
55    private function repushAbandoned( JobQueue $queue ) {
56        $cache = $this->getServiceContainer()->getObjectCacheFactory()->getInstance( CACHE_DB );
57        $key = $cache->makeGlobalKey( 'last-job-repush', $queue->getDomain(), $queue->getType() );
58
59        $now = wfTimestampNow();
60        $lastRepushTime = $cache->get( $key );
61        if ( $lastRepushTime === false ) {
62            $lastRepushTime = wfTimestamp( TS::MW, 1 ); // include all jobs
63        }
64
65        $this->output( "Last re-push time: $lastRepushTime; current time: $now\n" );
66
67        $count = 0;
68        $skipped = 0;
69        foreach ( $queue->getAllAbandonedJobs() as $job ) {
70            /** @var Job $job */
71            if ( $job instanceof Job && $job->getQueuedTimestamp() < wfTimestamp( TS::UNIX, $lastRepushTime ) ) {
72                ++$skipped;
73                continue; // already re-pushed in prior round
74            }
75
76            $queue->push( $job );
77            ++$count;
78
79            if ( ( $count % $this->getBatchSize() ) == 0 ) {
80                $queue->waitForBackups();
81            }
82        }
83
84        $cache->set( $key, $now ); // next run will ignore these jobs
85
86        $this->output( "Re-pushed $count job(s) [$skipped skipped].\n" );
87    }
88}
89
90// @codeCoverageIgnoreStart
91$maintClass = ManageJobs::class;
92require_once RUN_MAINTENANCE_IF_MAIN;
93// @codeCoverageIgnoreEnd