[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/maintenance/ -> fixSlaveDesync.php (source)

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


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1