<?php
/**
- * Database load balancing
+ * Database load balancing.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
*
* @file
* @ingroup Database
/**
* Get or set arbitrary data used by the parent object, usually an LBFactory
+ * @param $x
+ * @return Mixed
*/
function parentInfo( $x = null ) {
return wfSetVar( $this->mParentInfo, $x );
* Given an array of non-normalised probabilities, this function will select
* an element and return the appropriate key
*
- * @param $weights
+ * @param $weights array
*
* @return int
*/
return false;
}
$max = mt_getrandmax();
- $rand = mt_rand(0, $max) / $max * $sum;
+ $rand = mt_rand( 0, $max ) / $max * $sum;
$sum = 0;
foreach ( $weights as $i => $w ) {
}
/**
- * @param $loads
+ * @param $loads array
* @param $wiki bool
* @return bool|int|string
*/
foreach ( $lags as $i => $lag ) {
if ( $i != 0 ) {
if ( $lag === false ) {
- wfDebug( "Server #$i is not replicating\n" );
+ wfDebugLog( 'replication', "Server #$i is not replicating\n" );
unset( $loads[$i] );
} elseif ( isset( $this->mServers[$i]['max lag'] ) && $lag > $this->mServers[$i]['max lag'] ) {
- wfDebug( "Server #$i is excessively lagged ($lag seconds)\n" );
+ wfDebugLog( 'replication', "Server #$i is excessively lagged ($lag seconds)\n" );
unset( $loads[$i] );
}
}
$i = $this->getRandomNonLagged( $currentLoads, $wiki );
if ( $i === false && count( $currentLoads ) != 0 ) {
# All slaves lagged. Switch to read-only mode
- $wgReadOnly = wfMessage( 'readonly_lag' )->useDatabase( false )->plain();
+ wfDebugLog( 'replication', "All slaves lagged. Switch to read-only mode\n" );
+ $wgReadOnly = 'The database has been automatically locked ' .
+ 'while the slave database servers catch up to the master';
$i = $this->pickRandom( $currentLoads );
$laggedSlaveMode = true;
}
/**
* Wait for a specified number of microseconds, and return the period waited
+ * @param $t int
+ * @return int
*/
function sleep( $t ) {
wfProfileIn( __METHOD__ );
* Set the master wait position
* If a DB_SLAVE connection has been opened already, waits
* Otherwise sets a variable telling it to wait if such a connection is opened
+ * @param $pos int
*/
public function waitFor( $pos ) {
wfProfileIn( __METHOD__ );
/**
* Set the master wait position and wait for ALL slaves to catch up to it
+ * @param $pos int
*/
public function waitForAll( $pos ) {
wfProfileIn( __METHOD__ );
* Get any open connection to a given server index, local or foreign
* Returns false if there is no connection open
*
- * @return DatabaseBase
+ * @param $i int
+ * @return DatabaseBase|bool False on failure
*/
function getAnyOpenConnection( $i ) {
foreach ( $this->mConns as $conns ) {
/**
* Wait for a given slave to catch up to the master pos stored in $this
+ * @param $index
+ * @param $open bool
+ * @return bool
*/
function doWait( $index, $open = false ) {
# Find a connection to wait on
* Returns a Database object whether or not the connection was successful.
* @access private
*
+ * @param $server
+ * @param $dbNameOverride bool
* @return DatabaseBase
*/
function reallyOpenConnection( $server, $dbNameOverride = false ) {
if( !is_array( $server ) ) {
throw new MWException( 'You must update your load-balancing configuration. ' .
- 'See DefaultSettings.php entry for $wgDBservers.' );
+ 'See DefaultSettings.php entry for $wgDBservers.' );
}
$host = $server['host'];
# Create object
wfDebug( "Connecting to $host $dbname...\n" );
- $db = DatabaseBase::factory( $server['type'], $server );
+ try {
+ $db = DatabaseBase::factory( $server['type'], $server );
+ } catch ( DBConnectionError $e ) {
+ // FIXME: This is probably the ugliest thing I have ever done to
+ // PHP. I'm half-expecting it to segfault, just out of disgust. -- TS
+ $db = $e->db;
+ }
+
if ( $db->isOpen() ) {
wfDebug( "Connected to $host $dbname.\n" );
} else {
return $db;
}
+ /**
+ * @param $conn
+ * @throws DBConnectionError
+ */
function reportConnectionError( &$conn ) {
wfProfileIn( __METHOD__ );
wfProfileOut( __METHOD__ );
}
+ /**
+ * @return int
+ */
function getWriterIndex() {
return 0;
}
/**
* Returns true if the specified index is a valid server index
*
+ * @param $i
* @return bool
*/
function haveIndex( $i ) {
/**
* Returns true if the specified index is valid and has non-zero load
*
+ * @param $i
* @return bool
*/
function isNonZeroLoad( $i ) {
/**
* Get the host name or IP address of the server with the specified index
* Prefer a readable name if available.
+ * @param $i
+ * @return string
*/
function getServerName( $i ) {
if ( isset( $this->mServers[$i]['hostName'] ) ) {
/**
* Return the server info structure for a given index, or false if the index is invalid.
+ * @param $i
+ * @return bool
*/
function getServerInfo( $i ) {
if ( isset( $this->mServers[$i] ) ) {
/**
* Sets the server info structure for the given index. Entry at index $i is created if it doesn't exist
+ * @param $i
+ * @param $serverInfo
*/
function setServerInfo( $i, $serverInfo ) {
$this->mServers[$i] = $serverInfo;
* Deprecated function, typo in function name
*
* @deprecated in 1.18
+ * @param $conn
*/
function closeConnecton( $conn ) {
+ wfDeprecated( __METHOD__, '1.18' );
$this->closeConnection( $conn );
}
* Close a connection
* Using this function makes sure the LoadBalancer knows the connection is closed.
* If you use $conn->close() directly, the load balancer won't update its state.
- * @param $conn
- * @return void
+ * @param $conn DatabaseBase
*/
function closeConnection( $conn ) {
$done = false;
foreach ( $this->mConns as $conns2 ) {
foreach ( $conns2 as $conns3 ) {
foreach ( $conns3 as $conn ) {
- $conn->commit();
+ $conn->commit( __METHOD__ );
}
}
}
}
foreach ( $conns2[$masterIndex] as $conn ) {
if ( $conn->doneWrites() ) {
- $conn->commit();
+ $conn->commit( __METHOD__ );
}
}
}
}
+ /**
+ * @param $value null
+ * @return Mixed
+ */
function waitTimeout( $value = null ) {
return wfSetVar( $this->mWaitTimeout, $value );
}
+ /**
+ * @return bool
+ */
function getLaggedSlaveMode() {
return $this->mLaggedSlaveMode;
}
- /* Disables/enables lag checks */
+ /**
+ * Disables/enables lag checks
+ * @param $mode null
+ * @return bool
+ */
function allowLagged( $mode = null ) {
if ( $mode === null) {
return $this->mAllowLagged;
$this->mAllowLagged = $mode;
}
+ /**
+ * @return bool
+ */
function pingAll() {
$success = true;
foreach ( $this->mConns as $conns2 ) {
/**
* Call a function with each open connection object
+ * @param $callback
+ * @param array $params
*/
function forEachOpenConnection( $callback, $params = array() ) {
foreach ( $this->mConns as $conns2 ) {
/**
* Get the hostname and lag time of the most-lagged slave.
* This is useful for maintenance scripts that need to throttle their updates.
- * May attempt to open connections to slaves on the default DB.
+ * 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
*
* @return array ( host, max lag, index of max lagged host )
$maxLag = -1;
$host = '';
$maxIndex = 0;
- foreach ( $this->mServers as $i => $conn ) {
- $conn = false;
- if ( $wiki === false ) {
- $conn = $this->getAnyOpenConnection( $i );
- }
- if ( !$conn ) {
- $conn = $this->openConnection( $i, $wiki );
- }
- if ( !$conn ) {
- continue;
- }
- $lag = $conn->getLag();
- if ( $lag > $maxLag ) {
- $maxLag = $lag;
- $host = $this->mServers[$i]['host'];
- $maxIndex = $i;
+ if ( $this->getServerCount() > 1 ) { // no replication = no lag
+ foreach ( $this->mServers as $i => $conn ) {
+ $conn = false;
+ if ( $wiki === false ) {
+ $conn = $this->getAnyOpenConnection( $i );
+ }
+ if ( !$conn ) {
+ $conn = $this->openConnection( $i, $wiki );
+ }
+ if ( !$conn ) {
+ continue;
+ }
+ $lag = $conn->getLag();
+ if ( $lag > $maxLag ) {
+ $maxLag = $lag;
+ $host = $this->mServers[$i]['host'];
+ $maxIndex = $i;
+ }
}
}
return array( $host, $maxLag, $maxIndex );
if ( isset( $this->mLagTimes ) ) {
return $this->mLagTimes;
}
- # No, send the request to the load monitor
- $this->mLagTimes = $this->getLoadMonitor()->getLagTimes( array_keys( $this->mServers ), $wiki );
+ if ( $this->getServerCount() == 1 ) {
+ # No replication
+ $this->mLagTimes = array( 0 => 0 );
+ } else {
+ # Send the request to the load monitor
+ $this->mLagTimes = $this->getLoadMonitor()->getLagTimes(
+ array_keys( $this->mServers ), $wiki );
+ }
return $this->mLagTimes;
}
+ /**
+ * Get the lag in seconds for a given connection, or zero if this load
+ * balancer does not have replication enabled.
+ *
+ * This should be used in preference to Database::getLag() in cases where
+ * replication may not be in use, since there is no way to determine if
+ * replication is in use at the connection level without running
+ * potentially restricted queries such as SHOW SLAVE STATUS. Using this
+ * function instead of Database::getLag() avoids a fatal error in this
+ * case on many installations.
+ *
+ * @param $conn DatabaseBase
+ *
+ * @return int
+ */
+ function safeGetLag( $conn ) {
+ if ( $this->getServerCount() == 1 ) {
+ return 0;
+ } else {
+ return $conn->getLag();
+ }
+ }
+
/**
* Clear the cache for getLagTimes
*/