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