MediaWiki  REL1_24
fixSlaveDesync.php
Go to the documentation of this file.
00001 <?php
00024 require_once __DIR__ . '/Maintenance.php';
00025 
00032 class FixSlaveDesync extends Maintenance {
00034     private $slaveIndexes;
00035 
00036     public function __construct() {
00037         parent::__construct();
00038         $this->mDescription = "";
00039     }
00040 
00041     public function getDbType() {
00042         return Maintenance::DB_ADMIN;
00043     }
00044 
00045     public function execute() {
00046         $this->slaveIndexes = array();
00047         $serverCount = wfGetLB()->getServerCount();
00048         for ( $i = 1; $i < $serverCount; $i++ ) {
00049             if ( wfGetLB()->isNonZeroLoad( $i ) ) {
00050                 $this->slaveIndexes[] = $i;
00051             }
00052         }
00053 
00054         if ( $this->hasArg() ) {
00055             $this->desyncFixPage( $this->getArg() );
00056         } else {
00057             $corrupt = $this->findPageLatestCorruption();
00058             foreach ( $corrupt as $id => $dummy ) {
00059                 $this->desyncFixPage( $id );
00060             }
00061         }
00062     }
00063 
00068     private function findPageLatestCorruption() {
00069         $desync = array();
00070         $n = 0;
00071         $dbw = wfGetDB( DB_MASTER );
00072         $masterIDs = array();
00073         $res = $dbw->select(
00074             'page',
00075             array( 'page_id', 'page_latest' ),
00076             array( 'page_id<6054123' ),
00077             __METHOD__
00078         );
00079         $this->output( "Number of pages: " . $res->numRows() . "\n" );
00080         foreach ( $res as $row ) {
00081             $masterIDs[$row->page_id] = $row->page_latest;
00082             if ( !( ++$n % 10000 ) ) {
00083                 $this->output( "$n\r" );
00084             }
00085         }
00086         $this->output( "\n" );
00087 
00088         foreach ( $this->slaveIndexes as $i ) {
00089             $db = wfGetDB( $i );
00090             $res = $db->select(
00091                 'page',
00092                 array( 'page_id', 'page_latest' ),
00093                 array( 'page_id<6054123' ),
00094                 __METHOD__
00095             );
00096             foreach ( $res as $row ) {
00097                 if ( isset( $masterIDs[$row->page_id] ) && $masterIDs[$row->page_id] != $row->page_latest ) {
00098                     $desync[$row->page_id] = true;
00099                     $this->output( $row->page_id . "\t" );
00100                 }
00101             }
00102         }
00103         $this->output( "\n" );
00104 
00105         return $desync;
00106     }
00107 
00112     private function desyncFixPage( $pageID ) {
00113         # Check for a corrupted page_latest
00114         $dbw = wfGetDB( DB_MASTER );
00115         $dbw->begin( __METHOD__ );
00116         $realLatest = $dbw->selectField( 'page', 'page_latest', array( 'page_id' => $pageID ),
00117             __METHOD__, 'FOR UPDATE' );
00118         # list( $masterFile, $masterPos ) = $dbw->getMasterPos();
00119         $found = false;
00120         foreach ( $this->slaveIndexes as $i ) {
00121             $db = wfGetDB( $i );
00122             /*
00123             if ( !$db->masterPosWait( $masterFile, $masterPos, 10 ) ) {
00124                 $this->output( "Slave is too lagged, aborting\n" );
00125                 $dbw->commit( __METHOD__ );
00126                 sleep(10);
00127                 return;
00128             }*/
00129             $latest = $db->selectField( 'page', 'page_latest', array( 'page_id' => $pageID ), __METHOD__ );
00130             $max = $db->selectField( 'revision', 'MAX(rev_id)', false, __METHOD__ );
00131             if ( $latest != $realLatest && $realLatest < $max ) {
00132                 $this->output( "page_latest corrupted in page $pageID, server $i\n" );
00133                 $found = true;
00134                 break;
00135             }
00136         }
00137         if ( !$found ) {
00138             $this->output( "page_id $pageID seems fine\n" );
00139             $dbw->commit( __METHOD__ );
00140 
00141             return;
00142         }
00143 
00144         # Find the missing revisions
00145         $res = $dbw->select( 'revision', array( 'rev_id' ), array( 'rev_page' => $pageID ),
00146             __METHOD__, 'FOR UPDATE' );
00147         $masterIDs = array();
00148         foreach ( $res as $row ) {
00149             $masterIDs[] = $row->rev_id;
00150         }
00151 
00152         $res = $dbw->select( 'revision', array( 'rev_id' ), array( 'rev_page' => $pageID ), __METHOD__ );
00153         $slaveIDs = array();
00154         foreach ( $res as $row ) {
00155             $slaveIDs[] = $row->rev_id;
00156         }
00157         if ( count( $masterIDs ) < count( $slaveIDs ) ) {
00158             $missingIDs = array_diff( $slaveIDs, $masterIDs );
00159             if ( count( $missingIDs ) ) {
00160                 $this->output( "Found " . count( $missingIDs )
00161                     . " lost in master, copying from slave... " );
00162                 $dbFrom = $dbw;
00163                 $found = true;
00164                 $toMaster = true;
00165             } else {
00166                 $found = false;
00167             }
00168         } else {
00169             $missingIDs = array_diff( $masterIDs, $slaveIDs );
00170             if ( count( $missingIDs ) ) {
00171                 $this->output( "Found " . count( $missingIDs )
00172                     . " missing revision(s), copying from master... " );
00173                 $dbFrom = $dbw;
00174                 $found = true;
00175                 $toMaster = false;
00176             } else {
00177                 $found = false;
00178             }
00179         }
00180 
00181         if ( $found ) {
00182             foreach ( $missingIDs as $rid ) {
00183                 $this->output( "$rid " );
00184                 # Revision
00185                 $row = $dbFrom->selectRow( 'revision', '*', array( 'rev_id' => $rid ), __METHOD__ );
00186                 if ( $toMaster ) {
00187                     $id = $dbw->selectField( 'revision', 'rev_id', array( 'rev_id' => $rid ),
00188                         __METHOD__, 'FOR UPDATE' );
00189                     if ( $id ) {
00190                         $this->output( "Revision already exists\n" );
00191                         $found = false;
00192                         break;
00193                     } else {
00194                         $dbw->insert( 'revision', get_object_vars( $row ), __METHOD__, 'IGNORE' );
00195                     }
00196                 } else {
00197                     foreach ( $this->slaveIndexes as $i ) {
00198                         $db = wfGetDB( $i );
00199                         $db->insert( 'revision', get_object_vars( $row ), __METHOD__, 'IGNORE' );
00200                     }
00201                 }
00202 
00203                 # Text
00204                 $row = $dbFrom->selectRow( 'text', '*', array( 'old_id' => $row->rev_text_id ), __METHOD__ );
00205                 if ( $toMaster ) {
00206                     $dbw->insert( 'text', get_object_vars( $row ), __METHOD__, 'IGNORE' );
00207                 } else {
00208                     foreach ( $this->slaveIndexes as $i ) {
00209                         $db = wfGetDB( $i );
00210                         $db->insert( 'text', get_object_vars( $row ), __METHOD__, 'IGNORE' );
00211                     }
00212                 }
00213             }
00214             $this->output( "done\n" );
00215         }
00216 
00217         if ( $found ) {
00218             $this->output( "Fixing page_latest... " );
00219             if ( $toMaster ) {
00220                 /*
00221                 $dbw->update(
00222                     'page',
00223                     array( 'page_latest' => $realLatest ),
00224                     array( 'page_id' => $pageID ),
00225                     __METHOD__
00226                 );
00227                 */
00228             } else {
00229                 foreach ( $this->slaveIndexes as $i ) {
00230                     $db = wfGetDB( $i );
00231                     $db->update(
00232                         'page',
00233                         array( 'page_latest' => $realLatest ),
00234                         array( 'page_id' => $pageID ),
00235                         __METHOD__
00236                     );
00237                 }
00238             }
00239             $this->output( "done\n" );
00240         }
00241         $dbw->commit( __METHOD__ );
00242     }
00243 }
00244 
00245 $maintClass = "FixSlaveDesync";
00246 require_once RUN_MAINTENANCE_IF_MAIN;