]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - maintenance/fixSlaveDesync.php
MediaWiki 1.17.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                 parent::__construct();
26                 $this->mDescription = "";
27         }
28
29         public function getDbType() {
30                 return Maintenance::DB_ADMIN;
31         }
32
33         public function execute() {
34                 $this->slaveIndexes = array();
35                 for ( $i = 1; $i < wfGetLB()->getServerCount(); $i++ ) {
36                         if ( wfGetLB()->isNonZeroLoad( $i ) ) {
37                                 $this->slaveIndexes[] = $i;
38                         }
39                 }
40
41                 if ( $this->hasArg() ) {
42                         $this->desyncFixPage( $this->getArg() );
43                 } else {
44                         $corrupt = $this->findPageLatestCorruption();
45                         foreach ( $corrupt as $id => $dummy ) {
46                                 $this->desyncFixPage( $id );
47                         }
48                 }
49         }
50
51         /**
52          * Find all pages that have a corrupted page_latest
53          * @return array
54          */
55         private function findPageLatestCorruption() {
56                 $desync = array();
57                 $n = 0;
58                 $dbw = wfGetDB( DB_MASTER );
59                 $masterIDs = array();
60                 $res = $dbw->select( 'page', array( 'page_id', 'page_latest' ), array( 'page_id<6054123' ), __METHOD__ );
61                 $this->output( "Number of pages: " . $dbw->numRows( $res ) . "\n" );
62                 foreach ( $res as $row ) {
63                         $masterIDs[$row->page_id] = $row->page_latest;
64                         if ( !( ++$n % 10000 ) ) {
65                                 $this->output( "$n\r" );
66                         }
67                 }
68                 $this->output( "\n" );
69
70                 foreach ( $this->slaveIndexes as $i ) {
71                         $db = wfGetDB( $i );
72                         $res = $db->select( 'page', array( 'page_id', 'page_latest' ), array( 'page_id<6054123' ), __METHOD__ );
73                         foreach ( $res as $row ) {
74                                 if ( isset( $masterIDs[$row->page_id] ) && $masterIDs[$row->page_id] != $row->page_latest ) {
75                                         $desync[$row->page_id] = true;
76                                         $this->output( $row->page_id . "\t" );
77                                 }
78                         }
79                 }
80                 $this->output( "\n" );
81                 return $desync;
82         }
83
84         /**
85          * Fix a broken page entry
86          * @param $pageID int The page_id to fix
87          */
88         private function desyncFixPage( $pageID ) {
89                 # Check for a corrupted page_latest
90                 $dbw = wfGetDB( DB_MASTER );
91                 $dbw->begin();
92                 $realLatest = $dbw->selectField( 'page', 'page_latest', array( 'page_id' => $pageID ),
93                         __METHOD__, 'FOR UPDATE' );
94                 # list( $masterFile, $masterPos ) = $dbw->getMasterPos();
95                 $found = false;
96                 foreach ( $this->slaveIndexes as $i ) {
97                         $db = wfGetDB( $i );
98                         /*
99                         if ( !$db->masterPosWait( $masterFile, $masterPos, 10 ) ) {
100                                    $this->output( "Slave is too lagged, aborting\n" );
101                                    $dbw->commit();
102                                    sleep(10);
103                                    return;
104                         }*/
105                         $latest = $db->selectField( 'page', 'page_latest', array( 'page_id' => $pageID ), __METHOD__ );
106                         $max = $db->selectField( 'revision', 'MAX(rev_id)', false, __METHOD__ );
107                         if ( $latest != $realLatest && $realLatest < $max ) {
108                                 $this->output( "page_latest corrupted in page $pageID, server $i\n" );
109                                 $found = true;
110                                 break;
111                         }
112                 }
113                 if ( !$found ) {
114                         $this->output( "page_id $pageID seems fine\n" );
115                         $dbw->commit();
116                         return;
117                 }
118
119                 # Find the missing revisions
120                 $res = $dbw->select( 'revision', array( 'rev_id' ), array( 'rev_page' => $pageID ),
121                         __METHOD__, 'FOR UPDATE' );
122                 $masterIDs = array();
123                 foreach ( $res as $row ) {
124                         $masterIDs[] = $row->rev_id;
125                 }
126
127                 $res = $db->select( 'revision', array( 'rev_id' ), array( 'rev_page' => $pageID ), __METHOD__ );
128                 $slaveIDs = array();
129                 foreach ( $res as $row ) {
130                         $slaveIDs[] = $row->rev_id;
131                 }
132                 if ( count( $masterIDs ) < count( $slaveIDs ) ) {
133                         $missingIDs = array_diff( $slaveIDs, $masterIDs );
134                         if ( count( $missingIDs ) ) {
135                                 $this->output( "Found " . count( $missingIDs ) . " lost in master, copying from slave... " );
136                                 $dbFrom = $db;
137                                 $found = true;
138                                 $toMaster = true;
139                         } else {
140                                 $found = false;
141                         }
142                 } else {
143                         $missingIDs = array_diff( $masterIDs, $slaveIDs );
144                         if ( count( $missingIDs ) ) {
145                                 $this->output( "Found " . count( $missingIDs ) . " missing revision(s), copying from master... " );
146                                 $dbFrom = $dbw;
147                                 $found = true;
148                                 $toMaster = false;
149                         } else {
150                                 $found = false;
151                         }
152                 }
153
154                 if ( $found ) {
155                         foreach ( $missingIDs as $rid ) {
156                                 $this->output( "$rid " );
157                                 # Revision
158                                 $row = $dbFrom->selectRow( 'revision', '*', array( 'rev_id' => $rid ), __METHOD__ );
159                                 if ( $toMaster ) {
160                                         $id = $dbw->selectField( 'revision', 'rev_id', array( 'rev_id' => $rid ),
161                                                 __METHOD__, 'FOR UPDATE' );
162                                         if ( $id ) {
163                                                 $this->output( "Revision already exists\n" );
164                                                 $found = false;
165                                                 break;
166                                         } else {
167                                                 $dbw->insert( 'revision', get_object_vars( $row ), __METHOD__, 'IGNORE' );
168                                         }
169                                 } else {
170                                         foreach ( $this->slaveIndexes as $i ) {
171                                                 $db = wfGetDB( $i );
172                                                 $db->insert( 'revision', get_object_vars( $row ), __METHOD__, 'IGNORE' );
173                                         }
174                                 }
175
176                                 # Text
177                                 $row = $dbFrom->selectRow( 'text', '*', array( 'old_id' => $row->rev_text_id ), __METHOD__ );
178                                 if ( $toMaster ) {
179                                         $dbw->insert( 'text', get_object_vars( $row ), __METHOD__, 'IGNORE' );
180                                 } else {
181                                         foreach ( $this->slaveIndexes as $i ) {
182                                                 $db = wfGetDB( $i );
183                                                 $db->insert( 'text', get_object_vars( $row ), __METHOD__, 'IGNORE' );
184                                         }
185                                 }
186                         }
187                         $this->output( "done\n" );
188                 }
189
190                 if ( $found ) {
191                         $this->output( "Fixing page_latest... " );
192                         if ( $toMaster ) {
193                                 # $dbw->update( 'page', array( 'page_latest' => $realLatest ), array( 'page_id' => $pageID ), __METHOD__ );
194                         } else {
195                                 foreach ( $this->slaveIndexes as $i ) {
196                                         $db = wfGetDB( $i );
197                                         $db->update( 'page', array( 'page_latest' => $realLatest ), array( 'page_id' => $pageID ), __METHOD__ );
198                                 }
199                         }
200                         $this->output( "done\n" );
201                 }
202                 $dbw->commit();
203         }
204 }
205
206 $maintClass = "FixSlaveDesync";
207 require_once( RUN_MAINTENANCE_IF_MAIN );