MediaWiki
REL1_22
|
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;