MediaWiki  REL1_20
LBFactory.php
Go to the documentation of this file.
00001 <?php
00028 abstract class LBFactory {
00029 
00033         static $instance;
00034 
00039         public static function disableBackend() {
00040                 global $wgLBFactoryConf;
00041                 self::$instance = new LBFactory_Fake( $wgLBFactoryConf );
00042         }
00043 
00049         static function &singleton() {
00050                 if ( is_null( self::$instance ) ) {
00051                         global $wgLBFactoryConf;
00052                         $class = $wgLBFactoryConf['class'];
00053                         self::$instance = new $class( $wgLBFactoryConf );
00054                 }
00055                 return self::$instance;
00056         }
00057 
00061         static function destroyInstance() {
00062                 if ( self::$instance ) {
00063                         self::$instance->shutdown();
00064                         self::$instance->forEachLBCallMethod( 'closeAll' );
00065                         self::$instance = null;
00066                 }
00067         }
00068 
00074         static function setInstance( $instance ) {
00075                 self::destroyInstance();
00076                 self::$instance = $instance;
00077         }
00078 
00083         abstract function __construct( $conf );
00084 
00092         abstract function newMainLB( $wiki = false );
00093 
00100         abstract function getMainLB( $wiki = false );
00101 
00112         abstract function newExternalLB( $cluster, $wiki = false );
00113 
00122         abstract function &getExternalLB( $cluster, $wiki = false );
00123 
00131         abstract function forEachLB( $callback, $params = array() );
00132 
00137         function shutdown() {}
00138 
00144         function forEachLBCallMethod( $methodName, $args = array() ) {
00145                 $this->forEachLB( array( $this, 'callMethod' ), array( $methodName, $args ) );
00146         }
00147 
00154         function callMethod( $loadBalancer, $methodName, $args ) {
00155                 call_user_func_array( array( $loadBalancer, $methodName ), $args );
00156         }
00157 
00161         function commitMasterChanges() {
00162                 $this->forEachLBCallMethod( 'commitMasterChanges' );
00163         }
00164 }
00165 
00169 class LBFactory_Simple extends LBFactory {
00170 
00174         var $mainLB;
00175         var $extLBs = array();
00176 
00177         # Chronology protector
00178         var $chronProt;
00179 
00180         function __construct( $conf ) {
00181                 $this->chronProt = new ChronologyProtector;
00182         }
00183 
00188         function newMainLB( $wiki = false ) {
00189                 global $wgDBservers, $wgMasterWaitTimeout;
00190                 if ( $wgDBservers ) {
00191                         $servers = $wgDBservers;
00192                 } else {
00193                         global $wgDBserver, $wgDBuser, $wgDBpassword, $wgDBname, $wgDBtype, $wgDebugDumpSql;
00194                         global $wgDBssl, $wgDBcompress;
00195 
00196                         $flags = ( $wgDebugDumpSql ? DBO_DEBUG : 0 ) | DBO_DEFAULT;
00197                         if ( $wgDBssl ) {
00198                                 $flags |= DBO_SSL;
00199                         }
00200                         if ( $wgDBcompress ) {
00201                                 $flags |= DBO_COMPRESS;
00202                         }
00203 
00204                         $servers = array(array(
00205                                 'host' => $wgDBserver,
00206                                 'user' => $wgDBuser,
00207                                 'password' => $wgDBpassword,
00208                                 'dbname' => $wgDBname,
00209                                 'type' => $wgDBtype,
00210                                 'load' => 1,
00211                                 'flags' => $flags
00212                         ));
00213                 }
00214 
00215                 return new LoadBalancer( array(
00216                         'servers' => $servers,
00217                         'masterWaitTimeout' => $wgMasterWaitTimeout
00218                 ));
00219         }
00220 
00225         function getMainLB( $wiki = false ) {
00226                 if ( !isset( $this->mainLB ) ) {
00227                         $this->mainLB = $this->newMainLB( $wiki );
00228                         $this->mainLB->parentInfo( array( 'id' => 'main' ) );
00229                         $this->chronProt->initLB( $this->mainLB );
00230                 }
00231                 return $this->mainLB;
00232         }
00233 
00240         function newExternalLB( $cluster, $wiki = false ) {
00241                 global $wgExternalServers;
00242                 if ( !isset( $wgExternalServers[$cluster] ) ) {
00243                         throw new MWException( __METHOD__.": Unknown cluster \"$cluster\"" );
00244                 }
00245                 return new LoadBalancer( array(
00246                         'servers' => $wgExternalServers[$cluster]
00247                 ));
00248         }
00249 
00255         function &getExternalLB( $cluster, $wiki = false ) {
00256                 if ( !isset( $this->extLBs[$cluster] ) ) {
00257                         $this->extLBs[$cluster] = $this->newExternalLB( $cluster, $wiki );
00258                         $this->extLBs[$cluster]->parentInfo( array( 'id' => "ext-$cluster" ) );
00259                 }
00260                 return $this->extLBs[$cluster];
00261         }
00262 
00270         function forEachLB( $callback, $params = array() ) {
00271                 if ( isset( $this->mainLB ) ) {
00272                         call_user_func_array( $callback, array_merge( array( $this->mainLB ), $params ) );
00273                 }
00274                 foreach ( $this->extLBs as $lb ) {
00275                         call_user_func_array( $callback, array_merge( array( $lb ), $params ) );
00276                 }
00277         }
00278 
00279         function shutdown() {
00280                 if ( $this->mainLB ) {
00281                         $this->chronProt->shutdownLB( $this->mainLB );
00282                 }
00283                 $this->chronProt->shutdown();
00284                 $this->commitMasterChanges();
00285         }
00286 }
00287 
00294 class LBFactory_Fake extends LBFactory {
00295         function __construct( $conf ) {}
00296 
00297         function newMainLB( $wiki = false) {
00298                 throw new DBAccessError;
00299         }
00300         function getMainLB( $wiki = false ) {
00301                 throw new DBAccessError;
00302         }
00303         function newExternalLB( $cluster, $wiki = false ) {
00304                 throw new DBAccessError;
00305         }
00306         function &getExternalLB( $cluster, $wiki = false ) {
00307                 throw new DBAccessError;
00308         }
00309         function forEachLB( $callback, $params = array() ) {}
00310 }
00311 
00315 class DBAccessError extends MWException {
00316         function __construct() {
00317                 parent::__construct( "Mediawiki tried to access the database via wfGetDB(). This is not allowed." );
00318         }
00319 }
00320 
00325 class ChronologyProtector {
00326         var $startupPos;
00327         var $shutdownPos = array();
00328 
00334         function initLB( $lb ) {
00335                 if ( $this->startupPos === null ) {
00336                         if ( !empty( $_SESSION[__CLASS__] ) ) {
00337                                 $this->startupPos = $_SESSION[__CLASS__];
00338                         }
00339                 }
00340                 if ( !$this->startupPos ) {
00341                         return;
00342                 }
00343                 $masterName = $lb->getServerName( 0 );
00344 
00345                 if ( $lb->getServerCount() > 1 && !empty( $this->startupPos[$masterName] ) ) {
00346                         $info = $lb->parentInfo();
00347                         $pos = $this->startupPos[$masterName];
00348                         wfDebug( __METHOD__.": LB " . $info['id'] . " waiting for master pos $pos\n" );
00349                         $lb->waitFor( $this->startupPos[$masterName] );
00350                 }
00351         }
00352 
00359         function shutdownLB( $lb ) {
00360                 // Don't start a session, don't bother with non-replicated setups
00361                 if ( strval( session_id() ) == '' || $lb->getServerCount() <= 1 ) {
00362                         return;
00363                 }
00364                 $masterName = $lb->getServerName( 0 );
00365                 if ( isset( $this->shutdownPos[$masterName] ) ) {
00366                         // Already done
00367                         return;
00368                 }
00369                 // Only save the position if writes have been done on the connection
00370                 $db = $lb->getAnyOpenConnection( 0 );
00371                 $info = $lb->parentInfo();
00372                 if ( !$db || !$db->doneWrites() ) {
00373                         wfDebug( __METHOD__.": LB {$info['id']}, no writes done\n" );
00374                         return;
00375                 }
00376                 $pos = $db->getMasterPos();
00377                 wfDebug( __METHOD__.": LB {$info['id']} has master pos $pos\n" );
00378                 $this->shutdownPos[$masterName] = $pos;
00379         }
00380 
00385         function shutdown() {
00386                 if ( session_id() != '' && count( $this->shutdownPos ) ) {
00387                         wfDebug( __METHOD__.": saving master pos for " .
00388                                 count( $this->shutdownPos ) . " master(s)\n" );
00389                         $_SESSION[__CLASS__] = $this->shutdownPos;
00390                 }
00391         }
00392 }