MediaWiki  REL1_19
LBFactory.php
Go to the documentation of this file.
00001 <?php
00013 abstract class LBFactory {
00014 
00018         static $instance;
00019 
00024         public static function disableBackend() {
00025                 global $wgLBFactoryConf;
00026                 self::$instance = new LBFactory_Fake( $wgLBFactoryConf );
00027         }
00028 
00034         static function &singleton() {
00035                 if ( is_null( self::$instance ) ) {
00036                         global $wgLBFactoryConf;
00037                         $class = $wgLBFactoryConf['class'];
00038                         self::$instance = new $class( $wgLBFactoryConf );
00039                 }
00040                 return self::$instance;
00041         }
00042 
00046         static function destroyInstance() {
00047                 if ( self::$instance ) {
00048                         self::$instance->shutdown();
00049                         self::$instance->forEachLBCallMethod( 'closeAll' );
00050                         self::$instance = null;
00051                 }
00052         }
00053 
00059         static function setInstance( $instance ) {
00060                 self::destroyInstance();
00061                 self::$instance = $instance;
00062         }
00063 
00068         abstract function __construct( $conf );
00069 
00077         abstract function newMainLB( $wiki = false );
00078 
00085         abstract function getMainLB( $wiki = false );
00086 
00097         abstract function newExternalLB( $cluster, $wiki = false );
00098 
00107         abstract function &getExternalLB( $cluster, $wiki = false );
00108 
00116         abstract function forEachLB( $callback, $params = array() );
00117 
00122         function shutdown() {}
00123 
00129         function forEachLBCallMethod( $methodName, $args = array() ) {
00130                 $this->forEachLB( array( $this, 'callMethod' ), array( $methodName, $args ) );
00131         }
00132 
00139         function callMethod( $loadBalancer, $methodName, $args ) {
00140                 call_user_func_array( array( $loadBalancer, $methodName ), $args );
00141         }
00142 
00146         function commitMasterChanges() {
00147                 $this->forEachLBCallMethod( 'commitMasterChanges' );
00148         }
00149 }
00150 
00154 class LBFactory_Simple extends LBFactory {
00155 
00159         var $mainLB;
00160         var $extLBs = array();
00161 
00162         # Chronology protector
00163         var $chronProt;
00164 
00165         function __construct( $conf ) {
00166                 $this->chronProt = new ChronologyProtector;
00167         }
00168 
00173         function newMainLB( $wiki = false ) {
00174                 global $wgDBservers, $wgMasterWaitTimeout;
00175                 if ( $wgDBservers ) {
00176                         $servers = $wgDBservers;
00177                 } else {
00178                         global $wgDBserver, $wgDBuser, $wgDBpassword, $wgDBname, $wgDBtype, $wgDebugDumpSql;
00179                         $servers = array(array(
00180                                 'host' => $wgDBserver,
00181                                 'user' => $wgDBuser,
00182                                 'password' => $wgDBpassword,
00183                                 'dbname' => $wgDBname,
00184                                 'type' => $wgDBtype,
00185                                 'load' => 1,
00186                                 'flags' => ($wgDebugDumpSql ? DBO_DEBUG : 0) | DBO_DEFAULT
00187                         ));
00188                 }
00189 
00190                 return new LoadBalancer( array(
00191                         'servers' => $servers,
00192                         'masterWaitTimeout' => $wgMasterWaitTimeout
00193                 ));
00194         }
00195 
00200         function getMainLB( $wiki = false ) {
00201                 if ( !isset( $this->mainLB ) ) {
00202                         $this->mainLB = $this->newMainLB( $wiki );
00203                         $this->mainLB->parentInfo( array( 'id' => 'main' ) );
00204                         $this->chronProt->initLB( $this->mainLB );
00205                 }
00206                 return $this->mainLB;
00207         }
00208 
00215         function newExternalLB( $cluster, $wiki = false ) {
00216                 global $wgExternalServers;
00217                 if ( !isset( $wgExternalServers[$cluster] ) ) {
00218                         throw new MWException( __METHOD__.": Unknown cluster \"$cluster\"" );
00219                 }
00220                 return new LoadBalancer( array(
00221                         'servers' => $wgExternalServers[$cluster]
00222                 ));
00223         }
00224 
00230         function &getExternalLB( $cluster, $wiki = false ) {
00231                 if ( !isset( $this->extLBs[$cluster] ) ) {
00232                         $this->extLBs[$cluster] = $this->newExternalLB( $cluster, $wiki );
00233                         $this->extLBs[$cluster]->parentInfo( array( 'id' => "ext-$cluster" ) );
00234                 }
00235                 return $this->extLBs[$cluster];
00236         }
00237 
00245         function forEachLB( $callback, $params = array() ) {
00246                 if ( isset( $this->mainLB ) ) {
00247                         call_user_func_array( $callback, array_merge( array( $this->mainLB ), $params ) );
00248                 }
00249                 foreach ( $this->extLBs as $lb ) {
00250                         call_user_func_array( $callback, array_merge( array( $lb ), $params ) );
00251                 }
00252         }
00253 
00254         function shutdown() {
00255                 if ( $this->mainLB ) {
00256                         $this->chronProt->shutdownLB( $this->mainLB );
00257                 }
00258                 $this->chronProt->shutdown();
00259                 $this->commitMasterChanges();
00260         }
00261 }
00262 
00269 class LBFactory_Fake extends LBFactory {
00270         function __construct( $conf ) {}
00271 
00272         function newMainLB( $wiki = false) {
00273                 throw new DBAccessError;
00274         }
00275         function getMainLB( $wiki = false ) {
00276                 throw new DBAccessError;
00277         }
00278         function newExternalLB( $cluster, $wiki = false ) {
00279                 throw new DBAccessError;
00280         }
00281         function &getExternalLB( $cluster, $wiki = false ) {
00282                 throw new DBAccessError;
00283         }
00284         function forEachLB( $callback, $params = array() ) {}
00285 }
00286 
00290 class DBAccessError extends MWException {
00291         function __construct() {
00292                 parent::__construct( "Mediawiki tried to access the database via wfGetDB(). This is not allowed." );
00293         }
00294 }
00295 
00300 class ChronologyProtector {
00301         var $startupPos;
00302         var $shutdownPos = array();
00303 
00309         function initLB( $lb ) {
00310                 if ( $this->startupPos === null ) {
00311                         if ( !empty( $_SESSION[__CLASS__] ) ) {
00312                                 $this->startupPos = $_SESSION[__CLASS__];
00313                         }
00314                 }
00315                 if ( !$this->startupPos ) {
00316                         return;
00317                 }
00318                 $masterName = $lb->getServerName( 0 );
00319 
00320                 if ( $lb->getServerCount() > 1 && !empty( $this->startupPos[$masterName] ) ) {
00321                         $info = $lb->parentInfo();
00322                         $pos = $this->startupPos[$masterName];
00323                         wfDebug( __METHOD__.": LB " . $info['id'] . " waiting for master pos $pos\n" );
00324                         $lb->waitFor( $this->startupPos[$masterName] );
00325                 }
00326         }
00327 
00334         function shutdownLB( $lb ) {
00335                 // Don't start a session, don't bother with non-replicated setups
00336                 if ( strval( session_id() ) == '' || $lb->getServerCount() <= 1 ) {
00337                         return;
00338                 }
00339                 $masterName = $lb->getServerName( 0 );
00340                 if ( isset( $this->shutdownPos[$masterName] ) ) {
00341                         // Already done
00342                         return;
00343                 }
00344                 // Only save the position if writes have been done on the connection
00345                 $db = $lb->getAnyOpenConnection( 0 );
00346                 $info = $lb->parentInfo();
00347                 if ( !$db || !$db->doneWrites() ) {
00348                         wfDebug( __METHOD__.": LB {$info['id']}, no writes done\n" );
00349                         return;
00350                 }
00351                 $pos = $db->getMasterPos();
00352                 wfDebug( __METHOD__.": LB {$info['id']} has master pos $pos\n" );
00353                 $this->shutdownPos[$masterName] = $pos;
00354         }
00355 
00360         function shutdown() {
00361                 if ( session_id() != '' && count( $this->shutdownPos ) ) {
00362                         wfDebug( __METHOD__.": saving master pos for " .
00363                                 count( $this->shutdownPos ) . " master(s)\n" );
00364                         $_SESSION[__CLASS__] = $this->shutdownPos;
00365                 }
00366         }
00367 }