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