00001 <?php
00013 class LoadBalancer {
00014 var $mServers, $mConns, $mLoads, $mGroupLoads;
00015 var $mFailFunction, $mErrorConnection;
00016 var $mReadIndex, $mAllowLagged;
00017 var $mWaitForPos, $mWaitTimeout;
00018 var $mLaggedSlaveMode, $mLastError = 'Unknown error';
00019 var $mParentInfo, $mLagTimes;
00020 var $mLoadMonitorClass, $mLoadMonitor;
00021
00029 function __construct( $params )
00030 {
00031 if ( !isset( $params['servers'] ) ) {
00032 throw new MWException( __CLASS__.': missing servers parameter' );
00033 }
00034 $this->mServers = $params['servers'];
00035
00036 if ( isset( $params['failFunction'] ) ) {
00037 $this->mFailFunction = $params['failFunction'];
00038 } else {
00039 $this->mFailFunction = false;
00040 }
00041 if ( isset( $params['waitTimeout'] ) ) {
00042 $this->mWaitTimeout = $params['waitTimeout'];
00043 } else {
00044 $this->mWaitTimeout = 10;
00045 }
00046
00047 $this->mReadIndex = -1;
00048 $this->mWriteIndex = -1;
00049 $this->mConns = array(
00050 'local' => array(),
00051 'foreignUsed' => array(),
00052 'foreignFree' => array() );
00053 $this->mLoads = array();
00054 $this->mWaitForPos = false;
00055 $this->mLaggedSlaveMode = false;
00056 $this->mErrorConnection = false;
00057 $this->mAllowLag = false;
00058 $this->mLoadMonitorClass = isset( $params['loadMonitor'] )
00059 ? $params['loadMonitor'] : 'LoadMonitor_MySQL';
00060
00061 foreach( $params['servers'] as $i => $server ) {
00062 $this->mLoads[$i] = $server['load'];
00063 if ( isset( $server['groupLoads'] ) ) {
00064 foreach ( $server['groupLoads'] as $group => $ratio ) {
00065 if ( !isset( $this->mGroupLoads[$group] ) ) {
00066 $this->mGroupLoads[$group] = array();
00067 }
00068 $this->mGroupLoads[$group][$i] = $ratio;
00069 }
00070 }
00071 }
00072 }
00073
00074 static function newFromParams( $servers, $failFunction = false, $waitTimeout = 10 )
00075 {
00076 return new LoadBalancer( $servers, $failFunction, $waitTimeout );
00077 }
00078
00082 function getLoadMonitor() {
00083 if ( !isset( $this->mLoadMonitor ) ) {
00084 $class = $this->mLoadMonitorClass;
00085 $this->mLoadMonitor = new $class( $this );
00086 }
00087 return $this->mLoadMonitor;
00088 }
00089
00093 function parentInfo( $x = null ) {
00094 return wfSetVar( $this->mParentInfo, $x );
00095 }
00096
00101 function pickRandom( $weights )
00102 {
00103 if ( !is_array( $weights ) || count( $weights ) == 0 ) {
00104 return false;
00105 }
00106
00107 $sum = array_sum( $weights );
00108 if ( $sum == 0 ) {
00109 # No loads on any of them
00110 # In previous versions, this triggered an unweighted random selection,
00111 # but this feature has been removed as of April 2006 to allow for strict
00112 # separation of query groups.
00113 return false;
00114 }
00115 $max = mt_getrandmax();
00116 $rand = mt_rand(0, $max) / $max * $sum;
00117
00118 $sum = 0;
00119 foreach ( $weights as $i => $w ) {
00120 $sum += $w;
00121 if ( $sum >= $rand ) {
00122 break;
00123 }
00124 }
00125 return $i;
00126 }
00127
00128 function getRandomNonLagged( $loads, $wiki = false ) {
00129 # Unset excessively lagged servers
00130 $lags = $this->getLagTimes( $wiki );
00131 foreach ( $lags as $i => $lag ) {
00132 if ( $i != 0 && isset( $this->mServers[$i]['max lag'] ) ) {
00133 if ( $lag === false ) {
00134 wfDebug( "Server #$i is not replicating\n" );
00135 unset( $loads[$i] );
00136 } elseif ( $lag > $this->mServers[$i]['max lag'] ) {
00137 wfDebug( "Server #$i is excessively lagged ($lag seconds)\n" );
00138 unset( $loads[$i] );
00139 }
00140 }
00141 }
00142
00143 # Find out if all the slaves with non-zero load are lagged
00144 $sum = 0;
00145 foreach ( $loads as $load ) {
00146 $sum += $load;
00147 }
00148 if ( $sum == 0 ) {
00149 # No appropriate DB servers except maybe the master and some slaves with zero load
00150 # Do NOT use the master
00151 # Instead, this function will return false, triggering read-only mode,
00152 # and a lagged slave will be used instead.
00153 return false;
00154 }
00155
00156 if ( count( $loads ) == 0 ) {
00157 return false;
00158 }
00159
00160 #wfDebugLog( 'connect', var_export( $loads, true ) );
00161
00162 # Return a random representative of the remainder
00163 return $this->pickRandom( $loads );
00164 }
00165
00173 function getReaderIndex( $group = false, $wiki = false ) {
00174 global $wgReadOnly, $wgDBClusterTimeout, $wgDBAvgStatusPoll, $wgDBtype;
00175
00176 # FIXME: For now, only go through all this for mysql databases
00177 if ($wgDBtype != 'mysql') {
00178 return $this->getWriterIndex();
00179 }
00180
00181 if ( count( $this->mServers ) == 1 ) {
00182 # Skip the load balancing if there's only one server
00183 return 0;
00184 } elseif ( $group === false and $this->mReadIndex >= 0 ) {
00185 # Shortcut if generic reader exists already
00186 return $this->mReadIndex;
00187 }
00188
00189 wfProfileIn( __METHOD__ );
00190
00191 $totalElapsed = 0;
00192
00193 # convert from seconds to microseconds
00194 $timeout = $wgDBClusterTimeout * 1e6;
00195
00196 # Find the relevant load array
00197 if ( $group !== false ) {
00198 if ( isset( $this->mGroupLoads[$group] ) ) {
00199 $nonErrorLoads = $this->mGroupLoads[$group];
00200 } else {
00201 # No loads for this group, return false and the caller can use some other group
00202 wfDebug( __METHOD__.": no loads for group $group\n" );
00203 wfProfileOut( __METHOD__ );
00204 return false;
00205 }
00206 } else {
00207 $nonErrorLoads = $this->mLoads;
00208 }
00209
00210 if ( !$nonErrorLoads ) {
00211 throw new MWException( "Empty server array given to LoadBalancer" );
00212 }
00213
00214 # Scale the configured load ratios according to the dynamic load (if the load monitor supports it)
00215 $this->getLoadMonitor()->scaleLoads( $nonErrorLoads, $group, $wiki );
00216
00217 $i = false;
00218 $found = false;
00219 $laggedSlaveMode = false;
00220
00221 # First try quickly looking through the available servers for a server that
00222 # meets our criteria
00223 do {
00224 $totalThreadsConnected = 0;
00225 $overloadedServers = 0;
00226 $currentLoads = $nonErrorLoads;
00227 while ( count( $currentLoads ) ) {
00228 if ( $wgReadOnly || $this->mAllowLagged || $laggedSlaveMode ) {
00229 $i = $this->pickRandom( $currentLoads );
00230 } else {
00231 $i = $this->getRandomNonLagged( $currentLoads, $wiki );
00232 if ( $i === false && count( $currentLoads ) != 0 ) {
00233 # All slaves lagged. Switch to read-only mode
00234 $wgReadOnly = wfMsgNoDBForContent( 'readonly_lag' );
00235 $i = $this->pickRandom( $currentLoads );
00236 $laggedSlaveMode = true;
00237 }
00238 }
00239
00240 if ( $i === false ) {
00241 # pickRandom() returned false
00242 # This is permanent and means the configuration or the load monitor
00243 # wants us to return false.
00244 wfDebugLog( 'connect', __METHOD__.": pickRandom() returned false\n" );
00245 wfProfileOut( __METHOD__ );
00246 return false;
00247 }
00248
00249 wfDebugLog( 'connect', __METHOD__.": Using reader #$i: {$this->mServers[$i]['host']}...\n" );
00250 $conn = $this->openConnection( $i, $wiki );
00251
00252 if ( !$conn ) {
00253 wfDebugLog( 'connect', __METHOD__.": Failed connecting to $i/$wiki\n" );
00254 unset( $nonErrorLoads[$i] );
00255 unset( $currentLoads[$i] );
00256 continue;
00257 }
00258
00259
00260 $threshold = isset( $this->mServers[$i]['max threads'] )
00261 ? $this->mServers[$i]['max threads'] : false;
00262 $backoff = $this->getLoadMonitor()->postConnectionBackoff( $conn, $threshold );
00263
00264
00265
00266 if ( $wiki !== false ) {
00267 $this->reuseConnection( $conn );
00268 }
00269
00270 if ( $backoff ) {
00271 # Post-connection overload, don't use this server for now
00272 $totalThreadsConnected += $backoff;
00273 $overloadedServers++;
00274 unset( $currentLoads[$i] );
00275 } else {
00276 # Return this server
00277 break 2;
00278 }
00279 }
00280
00281 # No server found yet
00282 $i = false;
00283
00284 # If all servers were down, quit now
00285 if ( !count( $nonErrorLoads ) ) {
00286 wfDebugLog( 'connect', "All servers down\n" );
00287 break;
00288 }
00289
00290 # Some servers must have been overloaded
00291 if ( $overloadedServers == 0 ) {
00292 throw new MWException( __METHOD__.": unexpectedly found no overloaded servers" );
00293 }
00294 # Back off for a while
00295 # Scale the sleep time by the number of connected threads, to produce a
00296 # roughly constant global poll rate
00297 $avgThreads = $totalThreadsConnected / $overloadedServers;
00298 $totalElapsed += $this->sleep( $wgDBAvgStatusPoll * $avgThreads );
00299 } while ( $totalElapsed < $timeout );
00300
00301 if ( $totalElapsed >= $timeout ) {
00302 wfDebugLog( 'connect', "All servers busy\n" );
00303 $this->mErrorConnection = false;
00304 $this->mLastError = 'All servers busy';
00305 }
00306
00307 if ( $i !== false ) {
00308 # Slave connection successful
00309 # Wait for the session master pos for a short time
00310 if ( $this->mWaitForPos && $i > 0 ) {
00311 if ( !$this->doWait( $i ) ) {
00312 $this->mServers[$i]['slave pos'] = $conn->getSlavePos();
00313 }
00314 }
00315 if ( $this->mReadIndex <=0 && $this->mLoads[$i]>0 && $i !== false ) {
00316 $this->mReadIndex = $i;
00317 }
00318 }
00319 wfProfileOut( __METHOD__ );
00320 return $i;
00321 }
00322
00326 function sleep( $t ) {
00327 wfProfileIn( __METHOD__ );
00328 wfDebug( __METHOD__.": waiting $t us\n" );
00329 usleep( $t );
00330 wfProfileOut( __METHOD__ );
00331 return $t;
00332 }
00333
00338 function getGroupIndex( $group ) {
00339 return $this->getReaderIndex( $group );
00340 }
00341
00347 public function waitFor( $pos ) {
00348 wfProfileIn( __METHOD__ );
00349 $this->mWaitForPos = $pos;
00350 $i = $this->mReadIndex;
00351
00352 if ( $i > 0 ) {
00353 if ( !$this->doWait( $i ) ) {
00354 $this->mServers[$i]['slave pos'] = $this->getAnyOpenConnection( $i )->getSlavePos();
00355 $this->mLaggedSlaveMode = true;
00356 }
00357 }
00358 wfProfileOut( __METHOD__ );
00359 }
00360
00365 function getAnyOpenConnection( $i ) {
00366 foreach ( $this->mConns as $type => $conns ) {
00367 if ( !empty( $conns[$i] ) ) {
00368 return reset( $conns[$i] );
00369 }
00370 }
00371 return false;
00372 }
00373
00377 function doWait( $index ) {
00378 # Find a connection to wait on
00379 $conn = $this->getAnyOpenConnection( $index );
00380 if ( !$conn ) {
00381 wfDebug( __METHOD__ . ": no connection open\n" );
00382 return false;
00383 }
00384
00385 wfDebug( __METHOD__.": Waiting for slave #$index to catch up...\n" );
00386 $result = $conn->masterPosWait( $this->mWaitForPos, $this->mWaitTimeout );
00387
00388 if ( $result == -1 || is_null( $result ) ) {
00389 # Timed out waiting for slave, use master instead
00390 wfDebug( __METHOD__.": Timed out waiting for slave #$index pos {$this->mWaitForPos}\n" );
00391 return false;
00392 } else {
00393 wfDebug( __METHOD__.": Done\n" );
00394 return true;
00395 }
00396 }
00397
00405 public function &getConnection( $i, $groups = array(), $wiki = false ) {
00406 global $wgDBtype;
00407 wfProfileIn( __METHOD__ );
00408
00409 if ( $i == DB_LAST ) {
00410 throw new MWException( 'Attempt to call ' . __METHOD__ . ' with deprecated server index DB_LAST' );
00411 } elseif ( $i === null || $i === false ) {
00412 throw new MWException( 'Attempt to call ' . __METHOD__ . ' with invalid server index' );
00413 }
00414
00415 if ( $wiki === wfWikiID() ) {
00416 $wiki = false;
00417 }
00418
00419 # Query groups
00420 if ( $i == DB_MASTER ) {
00421 $i = $this->getWriterIndex();
00422 } elseif ( !is_array( $groups ) ) {
00423 $groupIndex = $this->getReaderIndex( $groups, $wiki );
00424 if ( $groupIndex !== false ) {
00425 $serverName = $this->getServerName( $groupIndex );
00426 wfDebug( __METHOD__.": using server $serverName for group $groups\n" );
00427 $i = $groupIndex;
00428 }
00429 } else {
00430 foreach ( $groups as $group ) {
00431 $groupIndex = $this->getReaderIndex( $group, $wiki );
00432 if ( $groupIndex !== false ) {
00433 $serverName = $this->getServerName( $groupIndex );
00434 wfDebug( __METHOD__.": using server $serverName for group $group\n" );
00435 $i = $groupIndex;
00436 break;
00437 }
00438 }
00439 }
00440
00441 # Operation-based index
00442 if ( $i == DB_SLAVE ) {
00443 $i = $this->getReaderIndex( false, $wiki );
00444 # Couldn't find a working server in getReaderIndex()?
00445 if ( $i === false ) {
00446 $this->mLastError = 'No working slave server: ' . $this->mLastError;
00447 $this->reportConnectionError( $this->mErrorConnection );
00448 return false;
00449 }
00450 }
00451
00452 # Now we have an explicit index into the servers array
00453 $conn = $this->openConnection( $i, $wiki );
00454 if ( !$conn ) {
00455 $this->reportConnectionError( $this->mErrorConnection );
00456 }
00457
00458 wfProfileOut( __METHOD__ );
00459 return $conn;
00460 }
00461
00467 public function reuseConnection( $conn ) {
00468 $serverIndex = $conn->getLBInfo('serverIndex');
00469 $refCount = $conn->getLBInfo('foreignPoolRefCount');
00470 $dbName = $conn->getDBname();
00471 $prefix = $conn->tablePrefix();
00472 if ( strval( $prefix ) !== '' ) {
00473 $wiki = "$dbName-$prefix";
00474 } else {
00475 $wiki = $dbName;
00476 }
00477 if ( $serverIndex === null || $refCount === null ) {
00478 wfDebug( __METHOD__.": this connection was not opened as a foreign connection\n" );
00489 return;
00490 }
00491 if ( $this->mConns['foreignUsed'][$serverIndex][$wiki] !== $conn ) {
00492 throw new MWException( __METHOD__.": connection not found, has the connection been freed already?" );
00493 }
00494 $conn->setLBInfo( 'foreignPoolRefCount', --$refCount );
00495 if ( $refCount <= 0 ) {
00496 $this->mConns['foreignFree'][$serverIndex][$wiki] = $conn;
00497 unset( $this->mConns['foreignUsed'][$serverIndex][$wiki] );
00498 wfDebug( __METHOD__.": freed connection $serverIndex/$wiki\n" );
00499 } else {
00500 wfDebug( __METHOD__.": reference count for $serverIndex/$wiki reduced to $refCount\n" );
00501 }
00502 }
00503
00518 function openConnection( $i, $wiki = false ) {
00519 wfProfileIn( __METHOD__ );
00520 if ( $wiki !== false ) {
00521 $conn = $this->openForeignConnection( $i, $wiki );
00522 wfProfileOut( __METHOD__);
00523 return $conn;
00524 }
00525 if ( isset( $this->mConns['local'][$i][0] ) ) {
00526 $conn = $this->mConns['local'][$i][0];
00527 } else {
00528 $server = $this->mServers[$i];
00529 $server['serverIndex'] = $i;
00530 $conn = $this->reallyOpenConnection( $server, false );
00531 if ( $conn->isOpen() ) {
00532 $this->mConns['local'][$i][0] = $conn;
00533 } else {
00534 wfDebug( "Failed to connect to database $i at {$this->mServers[$i]['host']}\n" );
00535 $this->mErrorConnection = $conn;
00536 $conn = false;
00537 }
00538 }
00539 wfProfileOut( __METHOD__ );
00540 return $conn;
00541 }
00542
00561 function openForeignConnection( $i, $wiki ) {
00562 wfProfileIn(__METHOD__);
00563 list( $dbName, $prefix ) = wfSplitWikiID( $wiki );
00564 if ( isset( $this->mConns['foreignUsed'][$i][$wiki] ) ) {
00565
00566 $conn = $this->mConns['foreignUsed'][$i][$wiki];
00567 wfDebug( __METHOD__.": reusing connection $i/$wiki\n" );
00568 } elseif ( isset( $this->mConns['foreignFree'][$i][$wiki] ) ) {
00569
00570 $conn = $this->mConns['foreignFree'][$i][$wiki];
00571 unset( $this->mConns['foreignFree'][$i][$wiki] );
00572 $this->mConns['foreignUsed'][$i][$wiki] = $conn;
00573 wfDebug( __METHOD__.": reusing free connection $i/$wiki\n" );
00574 } elseif ( !empty( $this->mConns['foreignFree'][$i] ) ) {
00575
00576 $conn = reset( $this->mConns['foreignFree'][$i] );
00577 $oldWiki = key( $this->mConns['foreignFree'][$i] );
00578
00579 if ( !$conn->selectDB( $dbName ) ) {
00580 $this->mLastError = "Error selecting database $dbName on server " .
00581 $conn->getServer() . " from client host " . wfHostname() . "\n";
00582 $this->mErrorConnection = $conn;
00583 $conn = false;
00584 } else {
00585 $conn->tablePrefix( $prefix );
00586 unset( $this->mConns['foreignFree'][$i][$oldWiki] );
00587 $this->mConns['foreignUsed'][$i][$wiki] = $conn;
00588 wfDebug( __METHOD__.": reusing free connection from $oldWiki for $wiki\n" );
00589 }
00590 } else {
00591
00592 $server = $this->mServers[$i];
00593 $server['serverIndex'] = $i;
00594 $server['foreignPoolRefCount'] = 0;
00595 $conn = $this->reallyOpenConnection( $server, $dbName );
00596 if ( !$conn->isOpen() ) {
00597 wfDebug( __METHOD__.": error opening connection for $i/$wiki\n" );
00598 $this->mErrorConnection = $conn;
00599 $conn = false;
00600 } else {
00601 $conn->tablePrefix( $prefix );
00602 $this->mConns['foreignUsed'][$i][$wiki] = $conn;
00603 wfDebug( __METHOD__.": opened new connection for $i/$wiki\n" );
00604 }
00605 }
00606
00607
00608 if ( $conn ) {
00609 $refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
00610 $conn->setLBInfo( 'foreignPoolRefCount', $refCount + 1 );
00611 }
00612 wfProfileOut(__METHOD__);
00613 return $conn;
00614 }
00615
00620 function isOpen( $index ) {
00621 if( !is_integer( $index ) ) {
00622 return false;
00623 }
00624 return (bool)$this->getAnyOpenConnection( $index );
00625 }
00626
00632 function reallyOpenConnection( $server, $dbNameOverride = false ) {
00633 if( !is_array( $server ) ) {
00634 throw new MWException( 'You must update your load-balancing configuration. See DefaultSettings.php entry for $wgDBservers.' );
00635 }
00636
00637 extract( $server );
00638 if ( $dbNameOverride !== false ) {
00639 $dbname = $dbNameOverride;
00640 }
00641
00642 # Get class for this database type
00643 $class = 'Database' . ucfirst( $type );
00644
00645 # Create object
00646 wfDebug( "Connecting to $host $dbname...\n" );
00647 $db = new $class( $host, $user, $password, $dbname, 1, $flags );
00648 if ( $db->isOpen() ) {
00649 wfDebug( "Connected\n" );
00650 } else {
00651 wfDebug( "Failed\n" );
00652 }
00653 $db->setLBInfo( $server );
00654 if ( isset( $server['fakeSlaveLag'] ) ) {
00655 $db->setFakeSlaveLag( $server['fakeSlaveLag'] );
00656 }
00657 if ( isset( $server['fakeMaster'] ) ) {
00658 $db->setFakeMaster( true );
00659 }
00660 return $db;
00661 }
00662
00663 function reportConnectionError( &$conn ) {
00664 wfProfileIn( __METHOD__ );
00665
00666 if ( !is_object( $conn ) ) {
00667
00668 wfLogDBError( "LB failure with no last connection\n" );
00669 $conn = new Database;
00670 if ( $this->mFailFunction ) {
00671 $conn->failFunction( $this->mFailFunction );
00672 $conn->reportConnectionError( $this->mLastError );
00673 } else {
00674
00675 throw new DBConnectionError( $conn, $this->mLastError );
00676 }
00677 } else {
00678 if ( $this->mFailFunction ) {
00679 $conn->failFunction( $this->mFailFunction );
00680 } else {
00681 $conn->failFunction( false );
00682 }
00683 $server = $conn->getProperty( 'mServer' );
00684 wfLogDBError( "Connection error: {$this->mLastError} ({$server})\n" );
00685 $conn->reportConnectionError( "{$this->mLastError} ({$server})" );
00686 }
00687 wfProfileOut( __METHOD__ );
00688 }
00689
00690 function getWriterIndex() {
00691 return 0;
00692 }
00693
00697 function haveIndex( $i ) {
00698 return array_key_exists( $i, $this->mServers );
00699 }
00700
00704 function isNonZeroLoad( $i ) {
00705 return array_key_exists( $i, $this->mServers ) && $this->mLoads[$i] != 0;
00706 }
00707
00711 function getServerCount() {
00712 return count( $this->mServers );
00713 }
00714
00719 function getServerName( $i ) {
00720 if ( isset( $this->mServers[$i]['hostName'] ) ) {
00721 return $this->mServers[$i]['hostName'];
00722 } elseif ( isset( $this->mServers[$i]['host'] ) ) {
00723 return $this->mServers[$i]['host'];
00724 } else {
00725 return '';
00726 }
00727 }
00728
00732 function getServerInfo( $i ) {
00733 if ( isset( $this->mServers[$i] ) ) {
00734 return $this->mServers[$i];
00735 } else {
00736 return false;
00737 }
00738 }
00739
00744 function getMasterPos() {
00745 # If this entire request was served from a slave without opening a connection to the
00746 # master (however unlikely that may be), then we can fetch the position from the slave.
00747 $masterConn = $this->getAnyOpenConnection( 0 );
00748 if ( !$masterConn ) {
00749 for ( $i = 1; $i < count( $this->mServers ); $i++ ) {
00750 $conn = $this->getAnyOpenConnection( $i );
00751 if ( $conn ) {
00752 wfDebug( "Master pos fetched from slave\n" );
00753 return $conn->getSlavePos();
00754 }
00755 }
00756 } else {
00757 wfDebug( "Master pos fetched from master\n" );
00758 return $masterConn->getMasterPos();
00759 }
00760 return false;
00761 }
00762
00766 function closeAll() {
00767 foreach ( $this->mConns as $conns2 ) {
00768 foreach ( $conns2 as $conns3 ) {
00769 foreach ( $conns3 as $conn ) {
00770 $conn->close();
00771 }
00772 }
00773 }
00774 $this->mConns = array(
00775 'local' => array(),
00776 'foreignFree' => array(),
00777 'foreignUsed' => array(),
00778 );
00779 }
00780
00786 function closeConnecton( $conn ) {
00787 $done = false;
00788 foreach ( $this->mConns as $i1 => $conns2 ) {
00789 foreach ( $conns2 as $i2 => $conns3 ) {
00790 foreach ( $conns3 as $i3 => $candidateConn ) {
00791 if ( $conn === $candidateConn ) {
00792 $conn->close();
00793 unset( $this->mConns[$i1][$i2][$i3] );
00794 $done = true;
00795 break;
00796 }
00797 }
00798 }
00799 }
00800 if ( !$done ) {
00801 $conn->close();
00802 }
00803 }
00804
00808 function commitAll() {
00809 foreach ( $this->mConns as $conns2 ) {
00810 foreach ( $conns2 as $conns3 ) {
00811 foreach ( $conns3 as $conn ) {
00812 $conn->immediateCommit();
00813 }
00814 }
00815 }
00816 }
00817
00818
00819 function commitMasterChanges() {
00820
00821 $masterIndex = $this->getWriterIndex();
00822 foreach ( $this->mConns as $type => $conns2 ) {
00823 if ( empty( $conns2[$masterIndex] ) ) {
00824 continue;
00825 }
00826 foreach ( $conns2[$masterIndex] as $conn ) {
00827 if ( $conn->doneWrites() ) {
00828 $conn->commit();
00829 }
00830 }
00831 }
00832 }
00833
00834 function waitTimeout( $value = NULL ) {
00835 return wfSetVar( $this->mWaitTimeout, $value );
00836 }
00837
00838 function getLaggedSlaveMode() {
00839 return $this->mLaggedSlaveMode;
00840 }
00841
00842
00843 function allowLagged($mode=null) {
00844 if ($mode===null)
00845 return $this->mAllowLagged;
00846 $this->mAllowLagged=$mode;
00847 }
00848
00849 function pingAll() {
00850 $success = true;
00851 foreach ( $this->mConns as $conns2 ) {
00852 foreach ( $conns2 as $conns3 ) {
00853 foreach ( $conns3 as $conn ) {
00854 if ( !$conn->ping() ) {
00855 $success = false;
00856 }
00857 }
00858 }
00859 }
00860 return $success;
00861 }
00862
00866 function forEachOpenConnection( $callback, $params = array() ) {
00867 foreach ( $this->mConns as $conns2 ) {
00868 foreach ( $conns2 as $conns3 ) {
00869 foreach ( $conns3 as $conn ) {
00870 $mergedParams = array_merge( array( $conn ), $params );
00871 call_user_func_array( $callback, $mergedParams );
00872 }
00873 }
00874 }
00875 }
00876
00882 function getMaxLag() {
00883 $maxLag = -1;
00884 $host = '';
00885 foreach ( $this->mServers as $i => $conn ) {
00886 $conn = $this->getAnyOpenConnection( $i );
00887 if ( !$conn ) {
00888 $conn = $this->openConnection( $i );
00889 }
00890 if ( !$conn ) {
00891 continue;
00892 }
00893 $lag = $conn->getLag();
00894 if ( $lag > $maxLag ) {
00895 $maxLag = $lag;
00896 $host = $this->mServers[$i]['host'];
00897 }
00898 }
00899 return array( $host, $maxLag );
00900 }
00901
00906 function getLagTimes( $wiki = false ) {
00907 # Try process cache
00908 if ( isset( $this->mLagTimes ) ) {
00909 return $this->mLagTimes;
00910 }
00911 # No, send the request to the load monitor
00912 $this->mLagTimes = $this->getLoadMonitor()->getLagTimes( array_keys( $this->mServers ), $wiki );
00913 return $this->mLagTimes;
00914 }
00915 }