[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/db/ -> LBFactoryMulti.php (source)

   1  <?php
   2  /**
   3   * Advanced generator of database load balancing objects for wiki farms.
   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 Database
  22   */
  23  
  24  /**
  25   * A multi-wiki, multi-master factory for Wikimedia and similar installations.
  26   * Ignores the old configuration globals
  27   *
  28   * Configuration:
  29   *     sectionsByDB                A map of database names to section names.
  30   *
  31   *     sectionLoads                A 2-d map. For each section, gives a map of server names to
  32   *                                 load ratios. For example:
  33   *                                 array(
  34   *                                     'section1' => array(
  35   *                                         'db1' => 100,
  36   *                                         'db2' => 100
  37   *                                     )
  38   *                                 )
  39   *
  40   *     serverTemplate              A server info associative array as documented for $wgDBservers.
  41   *                                 The host, hostName and load entries will be overridden.
  42   *
  43   *     groupLoadsBySection         A 3-d map giving server load ratios for each section and group.
  44   *                                 For example:
  45   *                                 array(
  46   *                                     'section1' => array(
  47   *                                         'group1' => array(
  48   *                                             'db1' => 100,
  49   *                                             'db2' => 100
  50   *                                         )
  51   *                                     )
  52   *                                 )
  53   *
  54   *     groupLoadsByDB              A 3-d map giving server load ratios by DB name.
  55   *
  56   *     hostsByName                 A map of hostname to IP address.
  57   *
  58   *     externalLoads               A map of external storage cluster name to server load map.
  59   *
  60   *     externalTemplateOverrides   A set of server info keys overriding serverTemplate for external
  61   *                                 storage.
  62   *
  63   *     templateOverridesByServer   A 2-d map overriding serverTemplate and
  64   *                                 externalTemplateOverrides on a server-by-server basis. Applies
  65   *                                 to both core and external storage.
  66   *
  67   *     templateOverridesByCluster  A 2-d map overriding the server info by external storage cluster.
  68   *
  69   *     masterTemplateOverrides     An override array for all master servers.
  70   *
  71   *     readOnlyBySection           A map of section name to read-only message.
  72   *                                 Missing or false for read/write.
  73   *
  74   * @ingroup Database
  75   */
  76  class LBFactoryMulti extends LBFactory {
  77      // Required settings
  78  
  79      /** @var array A map of database names to section names */
  80      protected $sectionsByDB;
  81  
  82      /**
  83       * @var array A 2-d map. For each section, gives a map of server names to
  84       * load ratios
  85       */
  86      protected $sectionLoads;
  87  
  88      /**
  89       * @var array A server info associative array as documented for
  90       * $wgDBservers. The host, hostName and load entries will be
  91       * overridden
  92       */
  93      protected $serverTemplate;
  94  
  95      // Optional settings
  96  
  97      /** @var array A 3-d map giving server load ratios for each section and group */
  98      protected $groupLoadsBySection = array();
  99  
 100      /** @var array A 3-d map giving server load ratios by DB name */
 101      protected $groupLoadsByDB = array();
 102  
 103      /** @var array A map of hostname to IP address */
 104      protected $hostsByName = array();
 105  
 106      /** @var array A map of external storage cluster name to server load map */
 107      protected $externalLoads = array();
 108  
 109      /**
 110       * @var array A set of server info keys overriding serverTemplate for
 111       * external storage
 112       */
 113      protected $externalTemplateOverrides;
 114  
 115      /**
 116       * @var array A 2-d map overriding serverTemplate and
 117       * externalTemplateOverrides on a server-by-server basis. Applies to both
 118       * core and external storage
 119       */
 120      protected $templateOverridesByServer;
 121  
 122      /** @var array A 2-d map overriding the server info by external storage cluster */
 123      protected $templateOverridesByCluster;
 124  
 125      /** @var array An override array for all master servers */
 126      protected $masterTemplateOverrides;
 127  
 128      /**
 129       * @var array|bool A map of section name to read-only message. Missing or
 130       * false for read/write
 131       */
 132      protected $readOnlyBySection = array();
 133  
 134      // Other stuff
 135  
 136      /** @var array Load balancer factory configuration */
 137      protected $conf;
 138  
 139      /** @var LoadBalancer[] */
 140      protected $mainLBs = array();
 141  
 142      /** @var LoadBalancer[] */
 143      protected $extLBs = array();
 144  
 145      /** @var string */
 146      protected $lastWiki;
 147  
 148      /** @var string */
 149      protected $lastSection;
 150  
 151      /**
 152       * @param array $conf
 153       * @throws MWException
 154       */
 155  	function __construct( $conf ) {
 156          $this->chronProt = new ChronologyProtector;
 157          $this->conf = $conf;
 158          $required = array( 'sectionsByDB', 'sectionLoads', 'serverTemplate' );
 159          $optional = array( 'groupLoadsBySection', 'groupLoadsByDB', 'hostsByName',
 160              'externalLoads', 'externalTemplateOverrides', 'templateOverridesByServer',
 161              'templateOverridesByCluster', 'masterTemplateOverrides',
 162              'readOnlyBySection' );
 163  
 164          foreach ( $required as $key ) {
 165              if ( !isset( $conf[$key] ) ) {
 166                  throw new MWException( __CLASS__ . ": $key is required in configuration" );
 167              }
 168              $this->$key = $conf[$key];
 169          }
 170  
 171          foreach ( $optional as $key ) {
 172              if ( isset( $conf[$key] ) ) {
 173                  $this->$key = $conf[$key];
 174              }
 175          }
 176  
 177          // Check for read-only mode
 178          $section = $this->getSectionForWiki();
 179          if ( !empty( $this->readOnlyBySection[$section] ) ) {
 180              global $wgReadOnly;
 181              $wgReadOnly = $this->readOnlyBySection[$section];
 182          }
 183      }
 184  
 185      /**
 186       * @param bool|string $wiki
 187       * @return string
 188       */
 189  	function getSectionForWiki( $wiki = false ) {
 190          if ( $this->lastWiki === $wiki ) {
 191              return $this->lastSection;
 192          }
 193          list( $dbName, ) = $this->getDBNameAndPrefix( $wiki );
 194          if ( isset( $this->sectionsByDB[$dbName] ) ) {
 195              $section = $this->sectionsByDB[$dbName];
 196          } else {
 197              $section = 'DEFAULT';
 198          }
 199          $this->lastSection = $section;
 200          $this->lastWiki = $wiki;
 201  
 202          return $section;
 203      }
 204  
 205      /**
 206       * @param bool|string $wiki
 207       * @return LoadBalancer
 208       */
 209  	function newMainLB( $wiki = false ) {
 210          list( $dbName, ) = $this->getDBNameAndPrefix( $wiki );
 211          $section = $this->getSectionForWiki( $wiki );
 212          $groupLoads = array();
 213          if ( isset( $this->groupLoadsByDB[$dbName] ) ) {
 214              $groupLoads = $this->groupLoadsByDB[$dbName];
 215          }
 216  
 217          if ( isset( $this->groupLoadsBySection[$section] ) ) {
 218              $groupLoads = array_merge_recursive( $groupLoads, $this->groupLoadsBySection[$section] );
 219          }
 220  
 221          return $this->newLoadBalancer(
 222              $this->serverTemplate,
 223              $this->sectionLoads[$section],
 224              $groupLoads
 225          );
 226      }
 227  
 228      /**
 229       * @param bool|string $wiki
 230       * @return LoadBalancer
 231       */
 232  	function getMainLB( $wiki = false ) {
 233          $section = $this->getSectionForWiki( $wiki );
 234          if ( !isset( $this->mainLBs[$section] ) ) {
 235              $lb = $this->newMainLB( $wiki, $section );
 236              $lb->parentInfo( array( 'id' => "main-$section" ) );
 237              $this->chronProt->initLB( $lb );
 238              $this->mainLBs[$section] = $lb;
 239          }
 240  
 241          return $this->mainLBs[$section];
 242      }
 243  
 244      /**
 245       * @param string $cluster
 246       * @param bool|string $wiki
 247       * @throws MWException
 248       * @return LoadBalancer
 249       */
 250  	function newExternalLB( $cluster, $wiki = false ) {
 251          if ( !isset( $this->externalLoads[$cluster] ) ) {
 252              throw new MWException( __METHOD__ . ": Unknown cluster \"$cluster\"" );
 253          }
 254          $template = $this->serverTemplate;
 255          if ( isset( $this->externalTemplateOverrides ) ) {
 256              $template = $this->externalTemplateOverrides + $template;
 257          }
 258          if ( isset( $this->templateOverridesByCluster[$cluster] ) ) {
 259              $template = $this->templateOverridesByCluster[$cluster] + $template;
 260          }
 261  
 262          return $this->newLoadBalancer( $template, $this->externalLoads[$cluster], array() );
 263      }
 264  
 265      /**
 266       * @param string $cluster External storage cluster, or false for core
 267       * @param bool|string $wiki Wiki ID, or false for the current wiki
 268       * @return LoadBalancer
 269       */
 270      function &getExternalLB( $cluster, $wiki = false ) {
 271          if ( !isset( $this->extLBs[$cluster] ) ) {
 272              $this->extLBs[$cluster] = $this->newExternalLB( $cluster, $wiki );
 273              $this->extLBs[$cluster]->parentInfo( array( 'id' => "ext-$cluster" ) );
 274              $this->chronProt->initLB( $this->extLBs[$cluster] );
 275          }
 276  
 277          return $this->extLBs[$cluster];
 278      }
 279  
 280      /**
 281       * Make a new load balancer object based on template and load array
 282       *
 283       * @param array $template
 284       * @param array $loads
 285       * @param array $groupLoads
 286       * @return LoadBalancer
 287       */
 288  	function newLoadBalancer( $template, $loads, $groupLoads ) {
 289          $servers = $this->makeServerArray( $template, $loads, $groupLoads );
 290          $lb = new LoadBalancer( array(
 291              'servers' => $servers,
 292          ) );
 293  
 294          return $lb;
 295      }
 296  
 297      /**
 298       * Make a server array as expected by LoadBalancer::__construct, using a template and load array
 299       *
 300       * @param array $template
 301       * @param array $loads
 302       * @param array $groupLoads
 303       * @return array
 304       */
 305  	function makeServerArray( $template, $loads, $groupLoads ) {
 306          $servers = array();
 307          $master = true;
 308          $groupLoadsByServer = $this->reindexGroupLoads( $groupLoads );
 309          foreach ( $groupLoadsByServer as $server => $stuff ) {
 310              if ( !isset( $loads[$server] ) ) {
 311                  $loads[$server] = 0;
 312              }
 313          }
 314          foreach ( $loads as $serverName => $load ) {
 315              $serverInfo = $template;
 316              if ( $master ) {
 317                  $serverInfo['master'] = true;
 318                  if ( isset( $this->masterTemplateOverrides ) ) {
 319                      $serverInfo = $this->masterTemplateOverrides + $serverInfo;
 320                  }
 321                  $master = false;
 322              }
 323              if ( isset( $this->templateOverridesByServer[$serverName] ) ) {
 324                  $serverInfo = $this->templateOverridesByServer[$serverName] + $serverInfo;
 325              }
 326              if ( isset( $groupLoadsByServer[$serverName] ) ) {
 327                  $serverInfo['groupLoads'] = $groupLoadsByServer[$serverName];
 328              }
 329              if ( isset( $this->hostsByName[$serverName] ) ) {
 330                  $serverInfo['host'] = $this->hostsByName[$serverName];
 331              } else {
 332                  $serverInfo['host'] = $serverName;
 333              }
 334              $serverInfo['hostName'] = $serverName;
 335              $serverInfo['load'] = $load;
 336              $servers[] = $serverInfo;
 337          }
 338  
 339          return $servers;
 340      }
 341  
 342      /**
 343       * Take a group load array indexed by group then server, and reindex it by server then group
 344       * @param array $groupLoads
 345       * @return array
 346       */
 347  	function reindexGroupLoads( $groupLoads ) {
 348          $reindexed = array();
 349          foreach ( $groupLoads as $group => $loads ) {
 350              foreach ( $loads as $server => $load ) {
 351                  $reindexed[$server][$group] = $load;
 352              }
 353          }
 354  
 355          return $reindexed;
 356      }
 357  
 358      /**
 359       * Get the database name and prefix based on the wiki ID
 360       * @param bool|string $wiki
 361       * @return array
 362       */
 363  	function getDBNameAndPrefix( $wiki = false ) {
 364          if ( $wiki === false ) {
 365              global $wgDBname, $wgDBprefix;
 366  
 367              return array( $wgDBname, $wgDBprefix );
 368          } else {
 369              return wfSplitWikiID( $wiki );
 370          }
 371      }
 372  
 373      /**
 374       * Execute a function for each tracked load balancer
 375       * The callback is called with the load balancer as the first parameter,
 376       * and $params passed as the subsequent parameters.
 377       * @param callable $callback
 378       * @param array $params
 379       */
 380  	function forEachLB( $callback, $params = array() ) {
 381          foreach ( $this->mainLBs as $lb ) {
 382              call_user_func_array( $callback, array_merge( array( $lb ), $params ) );
 383          }
 384          foreach ( $this->extLBs as $lb ) {
 385              call_user_func_array( $callback, array_merge( array( $lb ), $params ) );
 386          }
 387      }
 388  
 389  	function shutdown() {
 390          foreach ( $this->mainLBs as $lb ) {
 391              $this->chronProt->shutdownLB( $lb );
 392          }
 393          foreach ( $this->extLBs as $extLB ) {
 394              $this->chronProt->shutdownLB( $extLB );
 395          }
 396          $this->chronProt->shutdown();
 397          $this->commitMasterChanges();
 398      }
 399  }


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