WARNING: HUGE COMMIT
[lhc/web/wiklou.git] / maintenance / fixSlaveDesync.php
1 <?php
2 /**
3 * @file
4 * @ingroup Maintenance
5 */
6
7 $wgUseRootUser = true;
8 require_once( 'commandLine.inc' );
9
10 //$wgDebugLogFile = '/dev/stdout';
11
12 $slaveIndexes = array();
13 for ( $i = 1; $i < count( $wgDBservers ); $i++ ) {
14 if ( wfGetLB()->isNonZeroLoad( $i ) ) {
15 $slaveIndexes[] = $i;
16 }
17 }
18 /*
19 foreach ( wfGetLB()->mServers as $i => $server ) {
20 wfGetLB()->mServers[$i]['flags'] |= DBO_DEBUG;
21 }*/
22 $reportingInterval = 1000;
23
24 if ( isset( $args[0] ) ) {
25 desyncFixPage( $args[0] );
26 } else {
27 $dbw = wfGetDB( DB_MASTER );
28 $maxPage = $dbw->selectField( 'page', 'MAX(page_id)', false, 'fixDesync.php' );
29 $corrupt = findPageLatestCorruption();
30 foreach ( $corrupt as $id => $dummy ) {
31 desyncFixPage( $id );
32 }
33 /*
34 for ( $i=1; $i <= $maxPage; $i++ ) {
35 desyncFixPage( $i );
36 if ( !($i % $reportingInterval) ) {
37 print "$i\n";
38 }
39 }*/
40 }
41
42 function findPageLatestCorruption() {
43 $desync = array();
44 $n = 0;
45 $dbw = wfGetDB( DB_MASTER );
46 $masterIDs = array();
47 $res = $dbw->select( 'page', array( 'page_id', 'page_latest' ), array( 'page_id<6054123' ), __METHOD__ );
48 print "Number of pages: " . $dbw->numRows( $res ) . "\n";
49 while ( $row = $dbw->fetchObject( $res ) ) {
50 $masterIDs[$row->page_id] = $row->page_latest;
51 if ( !( ++$n % 10000 ) ) {
52 print "$n\r";
53 }
54 }
55 print "\n";
56 $dbw->freeResult( $res );
57
58 global $slaveIndexes;
59 foreach ( $slaveIndexes as $i ) {
60 $db = wfGetDB( $i );
61 $res = $db->select( 'page', array( 'page_id', 'page_latest' ), array( 'page_id<6054123' ), __METHOD__ );
62 while ( $row = $db->fetchObject( $res ) ) {
63 if ( isset( $masterIDs[$row->page_id] ) && $masterIDs[$row->page_id] != $row->page_latest ) {
64 $desync[$row->page_id] = true;
65 print $row->page_id . "\t";
66 }
67 }
68 $db->freeResult( $res );
69 }
70 print "\n";
71 return $desync;
72 }
73
74 function desyncFixPage( $pageID ) {
75 global $slaveIndexes;
76 $fname = 'desyncFixPage';
77
78 # Check for a corrupted page_latest
79 $dbw = wfGetDB( DB_MASTER );
80 $dbw->begin();
81 $realLatest = $dbw->selectField( 'page', 'page_latest', array( 'page_id' => $pageID ),
82 $fname, 'FOR UPDATE' );
83 #list( $masterFile, $masterPos ) = $dbw->getMasterPos();
84 $found = false;
85 foreach ( $slaveIndexes as $i ) {
86 $db = wfGetDB( $i );
87 /*
88 if ( !$db->masterPosWait( $masterFile, $masterPos, 10 ) ) {
89 echo "Slave is too lagged, aborting\n";
90 $dbw->commit();
91 sleep(10);
92 return;
93 }*/
94 $latest = $db->selectField( 'page', 'page_latest', array( 'page_id' => $pageID ), $fname );
95 $max = $db->selectField( 'revision', 'MAX(rev_id)', false, $fname );
96 if ( $latest != $realLatest && $realLatest < $max ) {
97 print "page_latest corrupted in page $pageID, server $i\n";
98 $found = true;
99 break;
100 }
101 }
102 if ( !$found ) {
103 print "page_id $pageID seems fine\n";
104 $dbw->commit();
105 return;
106 }
107
108 # Find the missing revisions
109 $res = $dbw->select( 'revision', array( 'rev_id' ), array( 'rev_page' => $pageID ),
110 $fname, 'FOR UPDATE' );
111 $masterIDs = array();
112 while ( $row = $dbw->fetchObject( $res ) ) {
113 $masterIDs[] = $row->rev_id;
114 }
115 $dbw->freeResult( $res );
116
117 $res = $db->select( 'revision', array( 'rev_id' ), array( 'rev_page' => $pageID ), $fname );
118 $slaveIDs = array();
119 while ( $row = $db->fetchObject( $res ) ) {
120 $slaveIDs[] = $row->rev_id;
121 }
122 $db->freeResult( $res );
123 if ( count( $masterIDs ) < count( $slaveIDs ) ) {
124 $missingIDs = array_diff( $slaveIDs, $masterIDs );
125 if ( count( $missingIDs ) ) {
126 print "Found " . count( $missingIDs ) . " lost in master, copying from slave... ";
127 $dbFrom = $db;
128 $found = true;
129 $toMaster = true;
130 } else {
131 $found = false;
132 }
133 } else {
134 $missingIDs = array_diff( $masterIDs, $slaveIDs );
135 if ( count( $missingIDs ) ) {
136 print "Found " . count( $missingIDs ) . " missing revision(s), copying from master... ";
137 $dbFrom = $dbw;
138 $found = true;
139 $toMaster = false;
140 } else {
141 $found = false;
142 }
143 }
144
145 if ( $found ) {
146 foreach ( $missingIDs as $rid ) {
147 print "$rid ";
148 # Revision
149 $row = $dbFrom->selectRow( 'revision', '*', array( 'rev_id' => $rid ), $fname );
150 if ( $toMaster ) {
151 $id = $dbw->selectField( 'revision', 'rev_id', array( 'rev_id' => $rid ),
152 $fname, 'FOR UPDATE' );
153 if ( $id ) {
154 echo "Revision already exists\n";
155 $found = false;
156 break;
157 } else {
158 $dbw->insert( 'revision', get_object_vars( $row ), $fname, 'IGNORE' );
159 }
160 } else {
161 foreach ( $slaveIndexes as $i ) {
162 $db = wfGetDB( $i );
163 $db->insert( 'revision', get_object_vars( $row ), $fname, 'IGNORE' );
164 }
165 }
166
167 # Text
168 $row = $dbFrom->selectRow( 'text', '*', array( 'old_id' => $row->rev_text_id ), $fname );
169 if ( $toMaster ) {
170 $dbw->insert( 'text', get_object_vars( $row ), $fname, 'IGNORE' );
171 } else {
172 foreach ( $slaveIndexes as $i ) {
173 $db = wfGetDB( $i );
174 $db->insert( 'text', get_object_vars( $row ), $fname, 'IGNORE' );
175 }
176 }
177 }
178 print "done\n";
179 }
180
181 if ( $found ) {
182 print "Fixing page_latest... ";
183 if ( $toMaster ) {
184 #$dbw->update( 'page', array( 'page_latest' => $realLatest ), array( 'page_id' => $pageID ), $fname );
185 } else {
186 foreach ( $slaveIndexes as $i ) {
187 $db = wfGetDB( $i );
188 $db->update( 'page', array( 'page_latest' => $realLatest ), array( 'page_id' => $pageID ), $fname );
189 }
190 }
191 print "done\n";
192 }
193 $dbw->commit();
194 }
195
196