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
00055 $row = $dbw->selectRow( 'job', '*', array( 'job_cmd' => $type ), __METHOD__,
00056 array( 'LIMIT' => 1 ));
00057
00058 if ($row === false) {
00059 wfProfileOut( __METHOD__ );
00060 return false;
00061 }
00062
00063
00064 $dbw->delete( 'job', array( 'job_id' => $row->job_id ), __METHOD__ );
00065 $affected = $dbw->affectedRows();
00066
00067 if ($affected == 0) {
00068 wfProfileOut( __METHOD__ );
00069 return false;
00070 }
00071
00072 $namespace = $row->job_namespace;
00073 $dbkey = $row->job_title;
00074 $title = Title::makeTitleSafe( $namespace, $dbkey );
00075 $job = Job::factory( $row->job_cmd, $title, Job::extractBlob( $row->job_params ), $row->job_id );
00076
00077 $dbw->delete( 'job', $job->insertFields(), __METHOD__ );
00078 $dbw->immediateCommit();
00079
00080 wfProfileOut( __METHOD__ );
00081 return $job;
00082 }
00083
00090 static function pop($offset=0) {
00091 wfProfileIn( __METHOD__ );
00092
00093 $dbr = wfGetDB( DB_SLAVE );
00094
00095
00096
00097
00098
00099
00100
00101
00102 $row = $dbr->selectRow( 'job', '*', "job_id >= ${offset}", __METHOD__,
00103 array( 'ORDER BY' => 'job_id', 'LIMIT' => 1 ));
00104
00105
00106
00107
00108 if ( $row === false) {
00109 if ($offset!=0)
00110 $row = $dbr->selectRow( 'job', '*', '', __METHOD__,
00111 array( 'ORDER BY' => 'job_id', 'LIMIT' => 1 ));
00112
00113 if ($row === false ) {
00114 wfProfileOut( __METHOD__ );
00115 return false;
00116 }
00117 }
00118 $offset = $row->job_id;
00119
00120
00121 $dbw = wfGetDB( DB_MASTER );
00122 $dbw->delete( 'job', array( 'job_id' => $row->job_id ), __METHOD__ );
00123 $affected = $dbw->affectedRows();
00124 $dbw->immediateCommit();
00125
00126 if ( !$affected ) {
00127
00128
00129 $row = $dbw->selectRow( 'job', array( 'MIN(job_id) as minjob',
00130 'MAX(job_id) as maxjob' ), '1=1', __METHOD__ );
00131 if ( $row === false || is_null( $row->minjob ) || is_null( $row->maxjob ) ) {
00132
00133 wfProfileOut( __METHOD__ );
00134 return false;
00135 }
00136
00137 $row = $dbw->selectRow( 'job', '*',
00138 'job_id >= ' . mt_rand( $row->minjob, $row->maxjob ), __METHOD__ );
00139 if ( $row === false ) {
00140
00141
00142 wfProfileOut( __METHOD__ );
00143 return false;
00144 }
00145
00146 $dbw->delete( 'job', array( 'job_id' => $row->job_id ), __METHOD__ );
00147 $affected = $dbw->affectedRows();
00148 $dbw->immediateCommit();
00149
00150 if ( !$affected ) {
00151
00152
00153 wfProfileOut( __METHOD__ );
00154 return false;
00155 }
00156 }
00157
00158
00159
00160 $namespace = $row->job_namespace;
00161 $dbkey = $row->job_title;
00162 $title = Title::makeTitleSafe( $namespace, $dbkey );
00163 $job = Job::factory( $row->job_cmd, $title, Job::extractBlob( $row->job_params ), $row->job_id );
00164
00165
00166
00167 $dbw->begin();
00168 $dbw->delete( 'job', $job->insertFields(), __METHOD__ );
00169 $dbw->commit();
00170
00171 wfProfileOut( __METHOD__ );
00172 return $job;
00173 }
00174
00184 static function factory( $command, $title, $params = false, $id = 0 ) {
00185 global $wgJobClasses;
00186 if( isset( $wgJobClasses[$command] ) ) {
00187 $class = $wgJobClasses[$command];
00188 return new $class( $title, $params, $id );
00189 }
00190 throw new MWException( "Invalid job command `{$command}`" );
00191 }
00192
00193 static function makeBlob( $params ) {
00194 if ( $params !== false ) {
00195 return serialize( $params );
00196 } else {
00197 return '';
00198 }
00199 }
00200
00201 static function extractBlob( $blob ) {
00202 if ( (string)$blob !== '' ) {
00203 return unserialize( $blob );
00204 } else {
00205 return false;
00206 }
00207 }
00208
00218 static function batchInsert( $jobs ) {
00219 if( !count( $jobs ) ) {
00220 return;
00221 }
00222 $dbw = wfGetDB( DB_MASTER );
00223 $rows = array();
00224 foreach( $jobs as $job ) {
00225 $rows[] = $job->insertFields();
00226 if ( count( $rows ) >= 50 ) {
00227 # Do a small transaction to avoid slave lag
00228 $dbw->begin();
00229 $dbw->insert( 'job', $rows, __METHOD__, 'IGNORE' );
00230 $dbw->commit();
00231 $rows = array();
00232 }
00233 }
00234 if ( $rows ) {
00235 $dbw->begin();
00236 $dbw->insert( 'job', $rows, __METHOD__, 'IGNORE' );
00237 $dbw->commit();
00238 }
00239 }
00240
00241
00242
00243
00244
00245 function __construct( $command, $title, $params = false, $id = 0 ) {
00246 $this->command = $command;
00247 $this->title = $title;
00248 $this->params = $params;
00249 $this->id = $id;
00250
00251
00252
00253 $this->removeDuplicates = true;
00254 }
00255
00259 function insert() {
00260 $fields = $this->insertFields();
00261
00262 $dbw = wfGetDB( DB_MASTER );
00263
00264 if ( $this->removeDuplicates ) {
00265 $res = $dbw->select( 'job', array( '1' ), $fields, __METHOD__ );
00266 if ( $dbw->numRows( $res ) ) {
00267 return;
00268 }
00269 }
00270 $fields['job_id'] = $dbw->nextSequenceValue( 'job_job_id_seq' );
00271 $dbw->insert( 'job', $fields, __METHOD__ );
00272 }
00273
00274 protected function insertFields() {
00275 return array(
00276 'job_cmd' => $this->command,
00277 'job_namespace' => $this->title->getNamespace(),
00278 'job_title' => $this->title->getDBkey(),
00279 'job_params' => Job::makeBlob( $this->params )
00280 );
00281 }
00282
00283 function toString() {
00284 $paramString = '';
00285 if ( $this->params ) {
00286 foreach ( $this->params as $key => $value ) {
00287 if ( $paramString != '' ) {
00288 $paramString .= ' ';
00289 }
00290 $paramString .= "$key=$value";
00291 }
00292 }
00293
00294 if ( is_object( $this->title ) ) {
00295 $s = "{$this->command} " . $this->title->getPrefixedDBkey();
00296 if ( $paramString !== '' ) {
00297 $s .= ' ' . $paramString;
00298 }
00299 return $s;
00300 } else {
00301 return "{$this->command} $paramString";
00302 }
00303 }
00304
00305 protected function setLastError( $error ) {
00306 $this->error = $error;
00307 }
00308
00309 function getLastError() {
00310 return $this->error;
00311 }
00312 }