summaryrefslogtreecommitdiff
path: root/includes/db/LoadBalancer.php
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2013-08-12 09:28:15 +0200
committerPierre Schmitz <pierre@archlinux.de>2013-08-12 09:28:15 +0200
commit08aa4418c30cfc18ccc69a0f0f9cb9e17be6c196 (patch)
tree577a29fb579188d16003a209ce2a2e9c5b0aa2bd /includes/db/LoadBalancer.php
parentcacc939b34e315b85e2d72997811eb6677996cc1 (diff)
Update to MediaWiki 1.21.1
Diffstat (limited to 'includes/db/LoadBalancer.php')
-rw-r--r--includes/db/LoadBalancer.php150
1 files changed, 65 insertions, 85 deletions
diff --git a/includes/db/LoadBalancer.php b/includes/db/LoadBalancer.php
index 0e455e0c..1e859278 100644
--- a/includes/db/LoadBalancer.php
+++ b/includes/db/LoadBalancer.php
@@ -37,14 +37,15 @@ class LoadBalancer {
private $mLoadMonitorClass, $mLoadMonitor;
/**
- * @param $params Array with keys:
+ * @param array $params with keys:
* servers Required. Array of server info structures.
* masterWaitTimeout Replication lag wait timeout
* loadMonitor Name of a class used to fetch server lag and load.
+ * @throws MWException
*/
function __construct( $params ) {
if ( !isset( $params['servers'] ) ) {
- throw new MWException( __CLASS__.': missing servers parameter' );
+ throw new MWException( __CLASS__ . ': missing servers parameter' );
}
$this->mServers = $params['servers'];
@@ -116,34 +117,14 @@ class LoadBalancer {
* Given an array of non-normalised probabilities, this function will select
* an element and return the appropriate key
*
+ * @deprecated 1.21, use ArrayUtils::pickRandom()
+ *
* @param $weights array
*
- * @return int
+ * @return bool|int|string
*/
function pickRandom( $weights ) {
- if ( !is_array( $weights ) || count( $weights ) == 0 ) {
- return false;
- }
-
- $sum = array_sum( $weights );
- if ( $sum == 0 ) {
- # No loads on any of them
- # In previous versions, this triggered an unweighted random selection,
- # but this feature has been removed as of April 2006 to allow for strict
- # separation of query groups.
- return false;
- }
- $max = mt_getrandmax();
- $rand = mt_rand( 0, $max ) / $max * $sum;
-
- $sum = 0;
- foreach ( $weights as $i => $w ) {
- $sum += $w;
- if ( $sum >= $rand ) {
- break;
- }
- }
- return $i;
+ return ArrayUtils::pickRandom( $weights );
}
/**
@@ -197,17 +178,18 @@ class LoadBalancer {
* Side effect: opens connections to databases
* @param $group bool
* @param $wiki bool
+ * @throws MWException
* @return bool|int|string
*/
function getReaderIndex( $group = false, $wiki = false ) {
global $wgReadOnly, $wgDBClusterTimeout, $wgDBAvgStatusPoll, $wgDBtype;
# @todo FIXME: For now, only go through all this for mysql databases
- if ($wgDBtype != 'mysql') {
+ if ( $wgDBtype != 'mysql' ) {
return $this->getWriterIndex();
}
- if ( count( $this->mServers ) == 1 ) {
+ if ( count( $this->mServers ) == 1 ) {
# Skip the load balancing if there's only one server
return 0;
} elseif ( $group === false and $this->mReadIndex >= 0 ) {
@@ -228,7 +210,7 @@ class LoadBalancer {
$nonErrorLoads = $this->mGroupLoads[$group];
} else {
# No loads for this group, return false and the caller can use some other group
- wfDebug( __METHOD__.": no loads for group $group\n" );
+ wfDebug( __METHOD__ . ": no loads for group $group\n" );
wfProfileOut( __METHOD__ );
return false;
}
@@ -256,7 +238,7 @@ class LoadBalancer {
$i = $this->pickRandom( $currentLoads );
} else {
$i = $this->getRandomNonLagged( $currentLoads, $wiki );
- if ( $i === false && count( $currentLoads ) != 0 ) {
+ if ( $i === false && count( $currentLoads ) != 0 ) {
# All slaves lagged. Switch to read-only mode
wfDebugLog( 'replication', "All slaves lagged. Switch to read-only mode\n" );
$wgReadOnly = 'The database has been automatically locked ' .
@@ -270,16 +252,16 @@ class LoadBalancer {
# pickRandom() returned false
# This is permanent and means the configuration or the load monitor
# wants us to return false.
- wfDebugLog( 'connect', __METHOD__.": pickRandom() returned false\n" );
+ wfDebugLog( 'connect', __METHOD__ . ": pickRandom() returned false\n" );
wfProfileOut( __METHOD__ );
return false;
}
- wfDebugLog( 'connect', __METHOD__.": Using reader #$i: {$this->mServers[$i]['host']}...\n" );
+ wfDebugLog( 'connect', __METHOD__ . ": Using reader #$i: {$this->mServers[$i]['host']}...\n" );
$conn = $this->openConnection( $i, $wiki );
if ( !$conn ) {
- wfDebugLog( 'connect', __METHOD__.": Failed connecting to $i/$wiki\n" );
+ wfDebugLog( 'connect', __METHOD__ . ": Failed connecting to $i/$wiki\n" );
unset( $nonErrorLoads[$i] );
unset( $currentLoads[$i] );
continue;
@@ -318,7 +300,7 @@ class LoadBalancer {
# Some servers must have been overloaded
if ( $overloadedServers == 0 ) {
- throw new MWException( __METHOD__.": unexpectedly found no overloaded servers" );
+ throw new MWException( __METHOD__ . ": unexpectedly found no overloaded servers" );
}
# Back off for a while
# Scale the sleep time by the number of connected threads, to produce a
@@ -341,7 +323,7 @@ class LoadBalancer {
$this->mServers[$i]['slave pos'] = $conn->getSlavePos();
}
}
- if ( $this->mReadIndex <=0 && $this->mLoads[$i]>0 && $i !== false ) {
+ if ( $this->mReadIndex <= 0 && $this->mLoads[$i] > 0 && $i !== false ) {
$this->mReadIndex = $i;
}
}
@@ -356,7 +338,7 @@ class LoadBalancer {
*/
function sleep( $t ) {
wfProfileIn( __METHOD__ );
- wfDebug( __METHOD__.": waiting $t us\n" );
+ wfDebug( __METHOD__ . ": waiting $t us\n" );
usleep( $t );
wfProfileOut( __METHOD__ );
return $t;
@@ -390,7 +372,7 @@ class LoadBalancer {
wfProfileIn( __METHOD__ );
$this->mWaitForPos = $pos;
for ( $i = 1; $i < count( $this->mServers ); $i++ ) {
- $this->doWait( $i , true );
+ $this->doWait( $i, true );
}
wfProfileOut( __METHOD__ );
}
@@ -433,15 +415,15 @@ class LoadBalancer {
}
}
- wfDebug( __METHOD__.": Waiting for slave #$index to catch up...\n" );
+ wfDebug( __METHOD__ . ": Waiting for slave #$index to catch up...\n" );
$result = $conn->masterPosWait( $this->mWaitForPos, $this->mWaitTimeout );
if ( $result == -1 || is_null( $result ) ) {
# Timed out waiting for slave, use master instead
- wfDebug( __METHOD__.": Timed out waiting for slave #$index pos {$this->mWaitForPos}\n" );
+ wfDebug( __METHOD__ . ": Timed out waiting for slave #$index pos {$this->mWaitForPos}\n" );
return false;
} else {
- wfDebug( __METHOD__.": Done\n" );
+ wfDebug( __METHOD__ . ": Done\n" );
return true;
}
}
@@ -451,9 +433,10 @@ class LoadBalancer {
* This is the main entry point for this class.
*
* @param $i Integer: server index
- * @param $groups Array: query groups
- * @param $wiki String: wiki ID
+ * @param array $groups query groups
+ * @param bool|string $wiki Wiki ID
*
+ * @throws MWException
* @return DatabaseBase
*/
public function &getConnection( $i, $groups = array(), $wiki = false ) {
@@ -476,7 +459,7 @@ class LoadBalancer {
$groupIndex = $this->getReaderIndex( $groups, $wiki );
if ( $groupIndex !== false ) {
$serverName = $this->getServerName( $groupIndex );
- wfDebug( __METHOD__.": using server $serverName for group $groups\n" );
+ wfDebug( __METHOD__ . ": using server $serverName for group $groups\n" );
$i = $groupIndex;
}
} else {
@@ -484,7 +467,7 @@ class LoadBalancer {
$groupIndex = $this->getReaderIndex( $group, $wiki );
if ( $groupIndex !== false ) {
$serverName = $this->getServerName( $groupIndex );
- wfDebug( __METHOD__.": using server $serverName for group $group\n" );
+ wfDebug( __METHOD__ . ": using server $serverName for group $group\n" );
$i = $groupIndex;
break;
}
@@ -497,16 +480,16 @@ class LoadBalancer {
# Couldn't find a working server in getReaderIndex()?
if ( $i === false ) {
$this->mLastError = 'No working slave server: ' . $this->mLastError;
- $this->reportConnectionError( $this->mErrorConnection );
wfProfileOut( __METHOD__ );
- return false;
+ return $this->reportConnectionError();
}
}
# Now we have an explicit index into the servers array
$conn = $this->openConnection( $i, $wiki );
if ( !$conn ) {
- $this->reportConnectionError( $this->mErrorConnection );
+ wfProfileOut( __METHOD__ );
+ return $this->reportConnectionError();
}
wfProfileOut( __METHOD__ );
@@ -519,10 +502,11 @@ class LoadBalancer {
* the same number of times as getConnection() to work.
*
* @param DatabaseBase $conn
+ * @throws MWException
*/
public function reuseConnection( $conn ) {
- $serverIndex = $conn->getLBInfo('serverIndex');
- $refCount = $conn->getLBInfo('foreignPoolRefCount');
+ $serverIndex = $conn->getLBInfo( 'serverIndex' );
+ $refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
$dbName = $conn->getDBname();
$prefix = $conn->tablePrefix();
if ( strval( $prefix ) !== '' ) {
@@ -531,7 +515,7 @@ class LoadBalancer {
$wiki = $dbName;
}
if ( $serverIndex === null || $refCount === null ) {
- wfDebug( __METHOD__.": this connection was not opened as a foreign connection\n" );
+ wfDebug( __METHOD__ . ": this connection was not opened as a foreign connection\n" );
/**
* This can happen in code like:
* foreach ( $dbs as $db ) {
@@ -545,15 +529,15 @@ class LoadBalancer {
return;
}
if ( $this->mConns['foreignUsed'][$serverIndex][$wiki] !== $conn ) {
- throw new MWException( __METHOD__.": connection not found, has the connection been freed already?" );
+ throw new MWException( __METHOD__ . ": connection not found, has the connection been freed already?" );
}
$conn->setLBInfo( 'foreignPoolRefCount', --$refCount );
if ( $refCount <= 0 ) {
$this->mConns['foreignFree'][$serverIndex][$wiki] = $conn;
unset( $this->mConns['foreignUsed'][$serverIndex][$wiki] );
- wfDebug( __METHOD__.": freed connection $serverIndex/$wiki\n" );
+ wfDebug( __METHOD__ . ": freed connection $serverIndex/$wiki\n" );
} else {
- wfDebug( __METHOD__.": reference count for $serverIndex/$wiki reduced to $refCount\n" );
+ wfDebug( __METHOD__ . ": reference count for $serverIndex/$wiki reduced to $refCount\n" );
}
}
@@ -566,7 +550,7 @@ class LoadBalancer {
* error will be available via $this->mErrorConnection.
*
* @param $i Integer server index
- * @param $wiki String wiki ID to open
+ * @param string $wiki wiki ID to open
* @return DatabaseBase
*
* @access private
@@ -575,7 +559,7 @@ class LoadBalancer {
wfProfileIn( __METHOD__ );
if ( $wiki !== false ) {
$conn = $this->openForeignConnection( $i, $wiki );
- wfProfileOut( __METHOD__);
+ wfProfileOut( __METHOD__ );
return $conn;
}
if ( isset( $this->mConns['local'][$i][0] ) ) {
@@ -585,6 +569,7 @@ class LoadBalancer {
$server['serverIndex'] = $i;
$conn = $this->reallyOpenConnection( $server, false );
if ( $conn->isOpen() ) {
+ wfDebug( "Connected to database $i at {$this->mServers[$i]['host']}\n" );
$this->mConns['local'][$i][0] = $conn;
} else {
wfDebug( "Failed to connect to database $i at {$this->mServers[$i]['host']}\n" );
@@ -611,22 +596,22 @@ class LoadBalancer {
* error will be available via $this->mErrorConnection.
*
* @param $i Integer: server index
- * @param $wiki String: wiki ID to open
+ * @param string $wiki wiki ID to open
* @return DatabaseBase
*/
function openForeignConnection( $i, $wiki ) {
- wfProfileIn(__METHOD__);
+ wfProfileIn( __METHOD__ );
list( $dbName, $prefix ) = wfSplitWikiID( $wiki );
if ( isset( $this->mConns['foreignUsed'][$i][$wiki] ) ) {
// Reuse an already-used connection
$conn = $this->mConns['foreignUsed'][$i][$wiki];
- wfDebug( __METHOD__.": reusing connection $i/$wiki\n" );
+ wfDebug( __METHOD__ . ": reusing connection $i/$wiki\n" );
} elseif ( isset( $this->mConns['foreignFree'][$i][$wiki] ) ) {
// Reuse a free connection for the same wiki
$conn = $this->mConns['foreignFree'][$i][$wiki];
unset( $this->mConns['foreignFree'][$i][$wiki] );
$this->mConns['foreignUsed'][$i][$wiki] = $conn;
- wfDebug( __METHOD__.": reusing free connection $i/$wiki\n" );
+ wfDebug( __METHOD__ . ": reusing free connection $i/$wiki\n" );
} elseif ( !empty( $this->mConns['foreignFree'][$i] ) ) {
// Reuse a connection from another wiki
$conn = reset( $this->mConns['foreignFree'][$i] );
@@ -641,7 +626,7 @@ class LoadBalancer {
$conn->tablePrefix( $prefix );
unset( $this->mConns['foreignFree'][$i][$oldWiki] );
$this->mConns['foreignUsed'][$i][$wiki] = $conn;
- wfDebug( __METHOD__.": reusing free connection from $oldWiki for $wiki\n" );
+ wfDebug( __METHOD__ . ": reusing free connection from $oldWiki for $wiki\n" );
}
} else {
// Open a new connection
@@ -650,13 +635,13 @@ class LoadBalancer {
$server['foreignPoolRefCount'] = 0;
$conn = $this->reallyOpenConnection( $server, $dbName );
if ( !$conn->isOpen() ) {
- wfDebug( __METHOD__.": error opening connection for $i/$wiki\n" );
+ wfDebug( __METHOD__ . ": error opening connection for $i/$wiki\n" );
$this->mErrorConnection = $conn;
$conn = false;
} else {
$conn->tablePrefix( $prefix );
$this->mConns['foreignUsed'][$i][$wiki] = $conn;
- wfDebug( __METHOD__.": opened new connection for $i/$wiki\n" );
+ wfDebug( __METHOD__ . ": opened new connection for $i/$wiki\n" );
}
}
@@ -665,7 +650,7 @@ class LoadBalancer {
$refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
$conn->setLBInfo( 'foreignPoolRefCount', $refCount + 1 );
}
- wfProfileOut(__METHOD__);
+ wfProfileOut( __METHOD__ );
return $conn;
}
@@ -690,6 +675,7 @@ class LoadBalancer {
*
* @param $server
* @param $dbNameOverride bool
+ * @throws MWException
* @return DatabaseBase
*/
function reallyOpenConnection( $server, $dbNameOverride = false ) {
@@ -698,15 +684,11 @@ class LoadBalancer {
'See DefaultSettings.php entry for $wgDBservers.' );
}
- $host = $server['host'];
- $dbname = $server['dbname'];
-
if ( $dbNameOverride !== false ) {
- $server['dbname'] = $dbname = $dbNameOverride;
+ $server['dbname'] = $dbNameOverride;
}
# Create object
- wfDebug( "Connecting to $host $dbname...\n" );
try {
$db = DatabaseBase::factory( $server['type'], $server );
} catch ( DBConnectionError $e ) {
@@ -715,11 +697,6 @@ class LoadBalancer {
$db = $e->db;
}
- if ( $db->isOpen() ) {
- wfDebug( "Connected to $host $dbname.\n" );
- } else {
- wfDebug( "Connection failed to $host $dbname.\n" );
- }
$db->setLBInfo( $server );
if ( isset( $server['fakeSlaveLag'] ) ) {
$db->setFakeSlaveLag( $server['fakeSlaveLag'] );
@@ -731,24 +708,24 @@ class LoadBalancer {
}
/**
- * @param $conn
* @throws DBConnectionError
+ * @return bool
*/
- function reportConnectionError( &$conn ) {
- wfProfileIn( __METHOD__ );
+ private function reportConnectionError() {
+ $conn = $this->mErrorConnection; // The connection which caused the error
if ( !is_object( $conn ) ) {
// No last connection, probably due to all servers being too busy
wfLogDBError( "LB failure with no last connection. Connection error: {$this->mLastError}\n" );
- $conn = new Database;
+
// If all servers were busy, mLastError will contain something sensible
- throw new DBConnectionError( $conn, $this->mLastError );
+ throw new DBConnectionError( null, $this->mLastError );
} else {
$server = $conn->getProperty( 'mServer' );
wfLogDBError( "Connection error: {$this->mLastError} ({$server})\n" );
- $conn->reportConnectionError( "{$this->mLastError} ({$server})" );
+ $conn->reportConnectionError( "{$this->mLastError} ({$server})" ); // throws DBConnectionError
}
- wfProfileOut( __METHOD__ );
+ return false; /* not reached */
}
/**
@@ -909,7 +886,9 @@ class LoadBalancer {
foreach ( $this->mConns as $conns2 ) {
foreach ( $conns2 as $conns3 ) {
foreach ( $conns3 as $conn ) {
- $conn->commit( __METHOD__ );
+ if ( $conn->trxLevel() ) {
+ $conn->commit( __METHOD__, 'flush' );
+ }
}
}
}
@@ -926,8 +905,8 @@ class LoadBalancer {
continue;
}
foreach ( $conns2[$masterIndex] as $conn ) {
- if ( $conn->writesOrCallbacksPending() ) {
- $conn->commit( __METHOD__ );
+ if ( $conn->trxLevel() && $conn->writesOrCallbacksPending() ) {
+ $conn->commit( __METHOD__, 'flush' );
}
}
}
@@ -954,10 +933,11 @@ class LoadBalancer {
* @return bool
*/
function allowLagged( $mode = null ) {
- if ( $mode === null) {
+ if ( $mode === null ) {
return $this->mAllowLagged;
}
$this->mAllowLagged = $mode;
+ return $this->mAllowLagged;
}
/**
@@ -999,7 +979,7 @@ class LoadBalancer {
* May attempt to open connections to slaves on the default DB. If there is
* no lag, the maximum lag will be reported as -1.
*
- * @param $wiki string Wiki ID, or false for the default database
+ * @param string $wiki Wiki ID, or false for the default database
*
* @return array ( host, max lag, index of max lagged host )
*/