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