]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - maintenance/fixSlaveDesync.php
MediaWiki 1.16.4
[autoinstalls/mediawiki.git] / maintenance / fixSlaveDesync.php
1 <?php
2 /**
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16  * http://www.gnu.org/copyleft/gpl.html
17  *
18  * @ingroup Maintenance
19  */
20
21 require_once( dirname(__FILE__) . '/Maintenance.php' );
22
23 class FixSlaveDesync extends Maintenance {
24         public function __construct() {
25                 global $wgUseRootUser;
26                 $wgUseRootUser = true;
27
28                 parent::__construct();
29                 $this->mDescription = "";
30                 
31         }
32         
33         public function execute() {
34                 global $slaveIndexes, $wgDBservers;
35                 $slaveIndexes = array();
36                 for ( $i = 1; $i < count( $wgDBservers ); $i++ ) {
37                         if ( wfGetLB()->isNonZeroLoad( $i ) ) {
38                                 $slaveIndexes[] = $i;
39                         }
40                 }
41
42                 if ( $this->hasArg() ) {
43                         $this->desyncFixPage( $this->getArg() );
44                 } else {
45                         $dbw = wfGetDB( DB_MASTER );
46                         $maxPage = $dbw->selectField( 'page', 'MAX(page_id)', false, __METHOD__ );
47                         $corrupt = $this->findPageLatestCorruption();
48                         foreach ( $corrupt as $id => $dummy ) {
49                                 $this->desyncFixPage( $id );
50                         }
51                 }
52         }
53
54         /**
55          * Find all pages that have a corrupted page_latest
56          * @return array
57          */
58         private function findPageLatestCorruption() {
59                 $desync = array();
60                 $n = 0;
61                 $dbw = wfGetDB( DB_MASTER );
62                 $masterIDs = array();
63                 $res = $dbw->select( 'page', array( 'page_id', 'page_latest' ), array( 'page_id<6054123' ), __METHOD__ );
64                 $this->output( "Number of pages: " . $dbw->numRows( $res ) . "\n" );
65                 foreach ( $res as $row ) {
66                         $masterIDs[$row->page_id] = $row->page_latest;
67                         if ( !( ++$n % 10000 ) ) {
68                                 $this->output( "$n\r" );
69                         }
70                 }
71                 $this->output( "\n" );
72                 $dbw->freeResult( $res );
73
74                 global $slaveIndexes;
75                 foreach ( $slaveIndexes as $i ) {
76                         $db = wfGetDB( $i );
77                         $res = $db->select( 'page', array( 'page_id', 'page_latest' ), array( 'page_id<6054123' ), __METHOD__ );
78                         foreach ( $res as $row ) {
79                                 if ( isset( $masterIDs[$row->page_id] ) && $masterIDs[$row->page_id] != $row->page_latest ) {
80                                         $desync[$row->page_id] = true;
81                                         $this->output( $row->page_id . "\t" );
82                                 }
83                         }
84                         $db->freeResult( $res );
85                 }
86                 $this->output( "\n" );
87                 return $desync;
88         }
89
90         /**
91          * Fix a broken page entry
92          * @param $pageID int The page_id to fix
93          */
94         private function desyncFixPage( $pageID ) {
95                 global $slaveIndexes;
96
97                 # Check for a corrupted page_latest
98                 $dbw = wfGetDB( DB_MASTER );
99                 $dbw->begin();
100                 $realLatest = $dbw->selectField( 'page', 'page_latest', array( 'page_id' => $pageID ), 
101                         __METHOD__, 'FOR UPDATE' );
102                 #list( $masterFile, $masterPos ) = $dbw->getMasterPos();
103                 $found = false;
104                 foreach ( $slaveIndexes as $i ) {
105                         $db = wfGetDB( $i );
106                         /*
107                         if ( !$db->masterPosWait( $masterFile, $masterPos, 10 ) ) {
108                                    $this->output( "Slave is too lagged, aborting\n" );
109                                    $dbw->commit();
110                                    sleep(10);
111                                    return;
112                         }*/            
113                         $latest = $db->selectField( 'page', 'page_latest', array( 'page_id' => $pageID ), __METHOD__ );
114                         $max = $db->selectField( 'revision', 'MAX(rev_id)', false, __METHOD__ );
115                         if ( $latest != $realLatest && $realLatest < $max ) {
116                                 $this->output( "page_latest corrupted in page $pageID, server $i\n" );
117                                 $found = true;
118                                 break;
119                         }
120                 }
121                 if ( !$found ) {
122                         $this->output( "page_id $pageID seems fine\n" );
123                         $dbw->commit();
124                         return;
125                 }
126
127                 # Find the missing revisions
128                 $res = $dbw->select( 'revision', array( 'rev_id' ), array( 'rev_page' => $pageID ), 
129                         __METHOD__, 'FOR UPDATE' );
130                 $masterIDs = array();
131                 foreach ( $res as $row ) {
132                         $masterIDs[] = $row->rev_id;
133                 }
134                 $dbw->freeResult( $res );
135
136                 $res = $db->select( 'revision', array( 'rev_id' ), array( 'rev_page' => $pageID ), __METHOD__ );
137                 $slaveIDs = array();
138                 foreach ( $res as $row ) {
139                         $slaveIDs[] = $row->rev_id;
140                 }
141                 $db->freeResult( $res );
142                 if ( count( $masterIDs ) < count( $slaveIDs ) ) {
143                         $missingIDs = array_diff( $slaveIDs, $masterIDs );
144                         if ( count( $missingIDs ) ) {
145                                 $this->output( "Found " . count( $missingIDs ) . " lost in master, copying from slave... " );
146                                 $dbFrom = $db;
147                                 $found = true;
148                                 $toMaster = true;
149                         } else {
150                                 $found = false;
151                         }
152                 } else {
153                         $missingIDs = array_diff( $masterIDs, $slaveIDs );
154                         if ( count( $missingIDs ) ) {
155                                 $this->output( "Found " . count( $missingIDs ) . " missing revision(s), copying from master... " );
156                                 $dbFrom = $dbw;
157                                 $found = true;
158                                 $toMaster = false;
159                         } else {
160                                 $found = false;
161                         }
162                 }
163
164                 if ( $found ) {
165                         foreach ( $missingIDs as $rid ) {
166                                 $this->output( "$rid " );
167                                 # Revision
168                                 $row = $dbFrom->selectRow( 'revision', '*', array( 'rev_id' => $rid ), __METHOD__ );
169                                 if ( $toMaster ) {
170                                         $id = $dbw->selectField( 'revision', 'rev_id', array( 'rev_id' => $rid ), 
171                                                 __METHOD__, 'FOR UPDATE' );
172                                         if ( $id ) {
173                                                 $this->output( "Revision already exists\n" );
174                                                 $found = false;
175                                                 break;
176                                         } else {
177                                                 $dbw->insert( 'revision', get_object_vars( $row ), __METHOD__, 'IGNORE' );
178                                         }
179                                 } else {
180                                         foreach ( $slaveIndexes as $i ) {
181                                                 $db = wfGetDB( $i );
182                                                 $db->insert( 'revision', get_object_vars( $row ), __METHOD__, 'IGNORE' );
183                                         }
184                                 }
185
186                                 # Text
187                                 $row = $dbFrom->selectRow( 'text', '*', array( 'old_id' => $row->rev_text_id ), __METHOD__ );
188                                 if ( $toMaster ) {
189                                         $dbw->insert( 'text', get_object_vars( $row ), __METHOD__, 'IGNORE' );
190                                 } else {
191                                         foreach ( $slaveIndexes as $i ) {
192                                                 $db = wfGetDB( $i );
193                                                 $db->insert( 'text', get_object_vars( $row ), __METHOD__, 'IGNORE' );
194                                         }
195                                 }
196                         }
197                         $this->output( "done\n" );
198                 }
199
200                 if ( $found ) {
201                         $this->output( "Fixing page_latest... " );
202                         if ( $toMaster ) {
203                                 #$dbw->update( 'page', array( 'page_latest' => $realLatest ), array( 'page_id' => $pageID ), __METHOD__ );
204                         } else {
205                                 foreach ( $slaveIndexes as $i ) {
206                                         $db = wfGetDB( $i );
207                                         $db->update( 'page', array( 'page_latest' => $realLatest ), array( 'page_id' => $pageID ), __METHOD__ );
208                                 }
209                         }
210                         $this->output( "done\n" );
211                 }
212                 $dbw->commit();
213         }
214 }
215
216 $maintClass = "FixSlaveDesync";
217 require_once( DO_MAINTENANCE );