Merge "updater now shows the SQLite file being used"
[lhc/web/wiklou.git] / maintenance / nextJobDB.php
1 <?php
2 /**
3 * Pick a database that has pending jobs
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 * @todo Make this work on PostgreSQL and maybe other database servers
22 * @ingroup Maintenance
23 */
24
25 require_once( __DIR__ . '/Maintenance.php' );
26
27 /**
28 * Maintenance script that picks a database that has pending jobs.
29 *
30 * @ingroup Maintenance
31 */
32 class nextJobDB extends Maintenance {
33 public function __construct() {
34 parent::__construct();
35 $this->mDescription = "Pick a database that has pending jobs";
36 $this->addOption( 'type', "The type of job to search for", false, true );
37 }
38
39 public function execute() {
40 global $wgMemc;
41
42 $type = $this->getOption( 'type', false );
43
44 $memcKey = 'jobqueue:dbs:v3';
45 $pendingDbInfo = $wgMemc->get( $memcKey );
46
47 // If the cache entry wasn't present, or in 1% of cases otherwise,
48 // regenerate the cache. Use any available stale cache if another
49 // process is currently regenerating the pending DB information.
50 if ( !$pendingDbInfo || mt_rand( 0, 100 ) == 0 ) {
51 $lock = $wgMemc->add( 'jobqueue:dbs:v3:lock', 1, 1800 ); // lock
52 if ( $lock ) {
53 $pendingDbInfo = array(
54 'pendingDBs' => $this->getPendingDbs(),
55 'timestamp' => time()
56 );
57 $wgMemc->set( $memcKey, $pendingDbInfo );
58 $wgMemc->delete( 'jobqueue:dbs:v3:lock' ); // unlock
59 }
60 }
61
62 if ( !$pendingDbInfo || !$pendingDbInfo['pendingDBs'] ) {
63 return; // no DBs with jobs or cache is both empty and locked
64 }
65 $pendingDBs = $pendingDbInfo['pendingDBs'];
66
67 do {
68 $again = false;
69
70 if ( $type === false ) {
71 $candidates = call_user_func_array( 'array_merge', $pendingDBs );
72 } elseif ( isset( $pendingDBs[$type] ) ) {
73 $candidates = $pendingDBs[$type];
74 } else {
75 $candidates = array();
76 }
77 if ( !$candidates ) {
78 return;
79 }
80
81 $candidates = array_values( $candidates );
82 $db = $candidates[ mt_rand( 0, count( $candidates ) - 1 ) ];
83 if ( !$this->checkJob( $type, $db ) ) {
84 if ( $type === false ) {
85 // There are no jobs available in the current database
86 foreach ( $pendingDBs as $type2 => $dbs ) {
87 $pendingDBs[$type2] = array_diff( $pendingDBs[$type2], array( $db ) );
88 }
89 } else {
90 // There are no jobs of this type available in the current database
91 $pendingDBs[$type] = array_diff( $pendingDBs[$type], array( $db ) );
92 }
93 // Update the cache to remove the outdated information
94 $pendingDbInfo['pendingDBs'] = $pendingDBs;
95 // @TODO: fix race condition with these updates
96 $wgMemc->set( $memcKey, $pendingDbInfo );
97 $again = true;
98 }
99 } while ( $again );
100
101 $this->output( $db . "\n" );
102 }
103
104 /**
105 * Check if the specified database has a job of the specified type in it.
106 * The type may be false to indicate "all".
107 * @param $type string
108 * @param $dbName string
109 * @return bool
110 */
111 function checkJob( $type, $dbName ) {
112 $group = JobQueueGroup::singleton( $dbName );
113 if ( $type === false ) {
114 foreach ( $group->getDefaultQueueTypes() as $type ) {
115 if ( !$group->get( $type )->isEmpty() ) {
116 return true;
117 }
118 }
119 return false;
120 } else {
121 return !$group->get( $type )->isEmpty();
122 }
123 }
124
125 /**
126 * Get all databases that have a pending job
127 * @return array
128 */
129 private function getPendingDbs() {
130 global $wgLocalDatabases;
131
132 $pendingDBs = array(); // (job type => (db list))
133 foreach ( $wgLocalDatabases as $db ) {
134 $types = JobQueueGroup::singleton( $db )->getQueuesWithJobs();
135 foreach ( $types as $type ) {
136 $pendingDBs[$type][] = $db;
137 }
138 }
139
140 return $pendingDBs;
141 }
142 }
143
144 $maintClass = "nextJobDb";
145 require_once( RUN_MAINTENANCE_IF_MAIN );