2004-02-18 02:15:00 +00:00
|
|
|
<?php
|
2004-01-25 13:27:53 +00:00
|
|
|
# Database load balancing object
|
|
|
|
|
|
|
|
|
|
class LoadBalancer {
|
|
|
|
|
/* private */ var $mServers, $mConnections, $mLoads;
|
|
|
|
|
/* private */ var $mUser, $mPassword, $mDbName, $mFailFunction;
|
|
|
|
|
/* private */ var $mForce, $mReadIndex;
|
|
|
|
|
|
|
|
|
|
function LoadBalancer()
|
|
|
|
|
{
|
|
|
|
|
$this->mServers = array();
|
|
|
|
|
$this->mLoads = array();
|
|
|
|
|
$this->mConnections = array();
|
|
|
|
|
$this->mUser = false;
|
|
|
|
|
$this->mPassword = false;
|
|
|
|
|
$this->mDbName = false;
|
|
|
|
|
$this->mFailFunction = false;
|
|
|
|
|
$this->mReadIndex = -1;
|
|
|
|
|
$this->mForce = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function newFromParams( $servers, $loads, $user, $password, $dbName, $failFunction = false )
|
|
|
|
|
{
|
|
|
|
|
$lb = new LoadBalancer;
|
|
|
|
|
$lb->initialise( $servers, $loads, $user, $password, $dbName, $failFunction = false );
|
|
|
|
|
return $lb;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function initialise( $servers, $loads, $user, $password, $dbName, $failFunction = false )
|
|
|
|
|
{
|
|
|
|
|
$this->mServers = $servers;
|
|
|
|
|
$this->mLoads = $loads;
|
|
|
|
|
$this->mUser = $user;
|
|
|
|
|
$this->mPassword = $password;
|
|
|
|
|
$this->mDbName = $dbName;
|
|
|
|
|
$this->mFailFunction = $failFunction;
|
|
|
|
|
$this->mReadIndex = -1;
|
|
|
|
|
$this->mWriteIndex = -1;
|
|
|
|
|
$this->mForce = -1;
|
|
|
|
|
$this->mConnections = array();
|
|
|
|
|
wfSeedRandom();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Given an array of non-normalised probabilities, this function will select
|
|
|
|
|
# an element and return the appropriate key
|
|
|
|
|
function pickRandom( $weights )
|
|
|
|
|
{
|
|
|
|
|
if ( !is_array( $weights ) || count( $weights ) == 0 ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$sum = 0;
|
|
|
|
|
foreach ( $weights as $w ) {
|
|
|
|
|
$sum += $w;
|
|
|
|
|
}
|
|
|
|
|
$rand = mt_rand() / RAND_MAX * $sum;
|
|
|
|
|
|
|
|
|
|
$sum = 0;
|
|
|
|
|
foreach ( $weights as $i => $w ) {
|
|
|
|
|
$sum += $w;
|
|
|
|
|
if ( $sum >= $rand ) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return $i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function &getReader()
|
|
|
|
|
{
|
|
|
|
|
if ( $this->mForce >= 0 ) {
|
|
|
|
|
$conn =& $this->getConnection( $this->mForce );
|
|
|
|
|
} else {
|
|
|
|
|
if ( $this->mReadIndex >= 0 ) {
|
|
|
|
|
$conn =& $this->getConnection( $this->mReadIndex );
|
|
|
|
|
} else {
|
|
|
|
|
# $loads is $this->mLoads except with elements knocked out if they
|
|
|
|
|
# don't work
|
|
|
|
|
$loads = $this->mLoads;
|
|
|
|
|
do {
|
|
|
|
|
$i = pickRandom( $loads );
|
|
|
|
|
if ( $i !== false ) {
|
|
|
|
|
$conn =& $this->getConnection( $i );
|
|
|
|
|
if ( !$conn->isOpen() ) {
|
|
|
|
|
unset( $loads[$i] );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} while ( $i !== false && !$conn->isOpen() );
|
|
|
|
|
if ( $conn->isOpen() ) {
|
|
|
|
|
$this->mReadIndex = $i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ( $conn === false || !$conn->isOpen() ) {
|
|
|
|
|
$this->reportConnectionError( $conn );
|
|
|
|
|
$conn = false;
|
|
|
|
|
}
|
|
|
|
|
return $conn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function &getConnection( $i, $fail = false )
|
|
|
|
|
{
|
|
|
|
|
if ( !array_key_exists( $i, $this->mConnections) || !$this->mConnections[$i]->isOpen() ) {
|
|
|
|
|
$this->mConnections[$i] = Database::newFromParams( $this->mServers[$i], $this->mUser,
|
|
|
|
|
$this->mPassword, $this->mDbName, 1 );
|
|
|
|
|
}
|
|
|
|
|
if ( !$this->mConnections[$i]->isOpen() ) {
|
|
|
|
|
wfDebug( "Failed to connect to database $i at {$this->mServers[$i]}\n" );
|
|
|
|
|
if ( $fail ) {
|
|
|
|
|
$this->reportConnectionError( $this->mConnections[$i] );
|
|
|
|
|
}
|
|
|
|
|
$this->mConnections[$i] = false;
|
|
|
|
|
}
|
|
|
|
|
return $this->mConnections[$i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function reportConnectionError( &$conn )
|
|
|
|
|
{
|
|
|
|
|
if ( !is_object( $conn ) ) {
|
|
|
|
|
$conn = new Database;
|
|
|
|
|
}
|
|
|
|
|
if ( $this->mFailFunction ) {
|
|
|
|
|
$conn->setFailFunction( $this->mFailFunction );
|
|
|
|
|
} else {
|
|
|
|
|
$conn->setFailFunction( "wfEmergencyAbort" );
|
|
|
|
|
}
|
|
|
|
|
$conn->reportConnectionError();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function &getWriter()
|
|
|
|
|
{
|
|
|
|
|
$c =& $this->getConnection( 0 );
|
|
|
|
|
if ( !$c->isOpen() ) {
|
|
|
|
|
reportConnectionError( $conn );
|
|
|
|
|
$c = false;
|
|
|
|
|
}
|
|
|
|
|
return $c;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function force( $i )
|
|
|
|
|
{
|
|
|
|
|
$this->mForce = $i;
|
|
|
|
|
}
|
|
|
|
|
}
|