MediaWiki  REL1_22
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: " . $res->numRows() . "\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;