00001 <?php
00006 if ( !defined( 'MEDIAWIKI' ) ) {
00007 die( "This file is part of MediaWiki, it is not a valid entry point\n" );
00008 }
00009
00015 abstract class Job {
00016 var $command,
00017 $title,
00018 $params,
00019 $id,
00020 $removeDuplicates,
00021 $error;
00022
00023
00024
00025
00026
00031 abstract function run();
00032
00033
00034
00035
00036
00049 static function pop_type( $type ) {
00050 wfProfilein( __METHOD__ );
00051
00052 $dbw = wfGetDB( DB_MASTER );
00053
00054 $row = $dbw->selectRow(
00055 'job',
00056 '*',
00057 array( 'job_cmd' => $type ),
00058 __METHOD__,
00059 array( 'LIMIT' => 1 )
00060 );
00061
00062 if ( $row === false ) {
00063 wfProfileOut( __METHOD__ );
00064 return false;
00065 }
00066
00067
00068 $dbw->delete( 'job', array( 'job_id' => $row->job_id ), __METHOD__ );
00069 $affected = $dbw->affectedRows();
00070
00071 if ( $affected == 0 ) {
00072 wfProfileOut( __METHOD__ );
00073 return false;
00074 }
00075
00076 $namespace = $row->job_namespace;
00077 $dbkey = $row->job_title;
00078 $title = Title::makeTitleSafe( $namespace, $dbkey );
00079 $job = Job::factory( $row->job_cmd, $title, Job::extractBlob( $row->job_params ), $row->job_id );
00080
00081 $dbw->delete( 'job', $job->insertFields(), __METHOD__ );
00082 $dbw->commit();
00083
00084 wfProfileOut( __METHOD__ );
00085 return $job;
00086 }
00087
00094 static function pop( $offset = 0 ) {
00095 wfProfileIn( __METHOD__ );
00096
00097 $dbr = wfGetDB( DB_SLAVE );
00098
00099
00100
00101
00102
00103
00104
00105
00106 $row = $dbr->selectRow( 'job', '*', "job_id >= ${offset}", __METHOD__,
00107 array( 'ORDER BY' => 'job_id', 'LIMIT' => 1 ) );
00108
00109
00110
00111
00112 if ( $row === false ) {
00113 if ( $offset != 0 ) {
00114 $row = $dbr->selectRow( 'job', '*', '', __METHOD__,
00115 array( 'ORDER BY' => 'job_id', 'LIMIT' => 1 ) );
00116 }
00117
00118 if ( $row === false ) {
00119 wfProfileOut( __METHOD__ );
00120 return false;
00121 }
00122 }
00123 $offset = $row->job_id;
00124
00125
00126 $dbw = wfGetDB( DB_MASTER );
00127 $dbw->delete( 'job', array( 'job_id' => $row->job_id ), __METHOD__ );
00128 $affected = $dbw->affectedRows();
00129 $dbw->commit();
00130
00131 if ( !$affected ) {
00132
00133
00134 $row = $dbw->selectRow( 'job', array( 'MIN(job_id) as minjob',
00135 'MAX(job_id) as maxjob' ), '1=1', __METHOD__ );
00136 if ( $row === false || is_null( $row->minjob ) || is_null( $row->maxjob ) ) {
00137
00138 wfProfileOut( __METHOD__ );
00139 return false;
00140 }
00141
00142 $row = $dbw->selectRow( 'job', '*',
00143 'job_id >= ' . mt_rand( $row->minjob, $row->maxjob ), __METHOD__ );
00144 if ( $row === false ) {
00145
00146
00147 wfProfileOut( __METHOD__ );
00148 return false;
00149 }
00150
00151 $dbw->delete( 'job', array( 'job_id' => $row->job_id ), __METHOD__ );
00152 $affected = $dbw->affectedRows();
00153 $dbw->commit();
00154
00155 if ( !$affected ) {
00156
00157
00158 wfProfileOut( __METHOD__ );
00159 return false;
00160 }
00161 }
00162
00163
00164
00165 $namespace = $row->job_namespace;
00166 $dbkey = $row->job_title;
00167 $title = Title::makeTitleSafe( $namespace, $dbkey );
00168 $job = Job::factory( $row->job_cmd, $title, Job::extractBlob( $row->job_params ), $row->job_id );
00169
00170
00171
00172 $dbw->begin();
00173 $dbw->delete( 'job', $job->insertFields(), __METHOD__ );
00174 $dbw->commit();
00175
00176 wfProfileOut( __METHOD__ );
00177 return $job;
00178 }
00179
00189 static function factory( $command, $title, $params = false, $id = 0 ) {
00190 global $wgJobClasses;
00191 if( isset( $wgJobClasses[$command] ) ) {
00192 $class = $wgJobClasses[$command];
00193 return new $class( $title, $params, $id );
00194 }
00195 throw new MWException( "Invalid job command `{$command}`" );
00196 }
00197
00198 static function makeBlob( $params ) {
00199 if ( $params !== false ) {
00200 return serialize( $params );
00201 } else {
00202 return '';
00203 }
00204 }
00205
00206 static function extractBlob( $blob ) {
00207 if ( (string)$blob !== '' ) {
00208 return unserialize( $blob );
00209 } else {
00210 return false;
00211 }
00212 }
00213
00223 static function batchInsert( $jobs ) {
00224 if( !count( $jobs ) ) {
00225 return;
00226 }
00227 $dbw = wfGetDB( DB_MASTER );
00228 $rows = array();
00229 foreach( $jobs as $job ) {
00230 $rows[] = $job->insertFields();
00231 if ( count( $rows ) >= 50 ) {
00232 # Do a small transaction to avoid slave lag
00233 $dbw->begin();
00234 $dbw->insert( 'job', $rows, __METHOD__, 'IGNORE' );
00235 $dbw->commit();
00236 $rows = array();
00237 }
00238 }
00239 if ( $rows ) {
00240 $dbw->begin();
00241 $dbw->insert( 'job', $rows, __METHOD__, 'IGNORE' );
00242 $dbw->commit();
00243 }
00244 }
00245
00246
00247
00248
00249
00250 function __construct( $command, $title, $params = false, $id = 0 ) {
00251 $this->command = $command;
00252 $this->title = $title;
00253 $this->params = $params;
00254 $this->id = $id;
00255
00256
00257
00258 $this->removeDuplicates = true;
00259 }
00260
00264 function insert() {
00265 $fields = $this->insertFields();
00266
00267 $dbw = wfGetDB( DB_MASTER );
00268
00269 if ( $this->removeDuplicates ) {
00270 $res = $dbw->select( 'job', array( '1' ), $fields, __METHOD__ );
00271 if ( $dbw->numRows( $res ) ) {
00272 return;
00273 }
00274 }
00275 $dbw->insert( 'job', $fields, __METHOD__ );
00276 }
00277
00278 protected function insertFields() {
00279 $dbw = wfGetDB( DB_MASTER );
00280 return array(
00281 'job_id' => $dbw->nextSequenceValue( 'job_job_id_seq' ),
00282 'job_cmd' => $this->command,
00283 'job_namespace' => $this->title->getNamespace(),
00284 'job_title' => $this->title->getDBkey(),
00285 'job_params' => Job::makeBlob( $this->params )
00286 );
00287 }
00288
00289 function toString() {
00290 $paramString = '';
00291 if ( $this->params ) {
00292 foreach ( $this->params as $key => $value ) {
00293 if ( $paramString != '' ) {
00294 $paramString .= ' ';
00295 }
00296 $paramString .= "$key=$value";
00297 }
00298 }
00299
00300 if ( is_object( $this->title ) ) {
00301 $s = "{$this->command} " . $this->title->getPrefixedDBkey();
00302 if ( $paramString !== '' ) {
00303 $s .= ' ' . $paramString;
00304 }
00305 return $s;
00306 } else {
00307 return "{$this->command} $paramString";
00308 }
00309 }
00310
00311 protected function setLastError( $error ) {
00312 $this->error = $error;
00313 }
00314
00315 function getLastError() {
00316 return $this->error;
00317 }
00318 }