00001 <?php
00002
# Database load balancing object
00003
00004
# Valid database indexes
00005
# Operation-based indexes
00006 define(
"DB_READ", -1 ); # Read from the slave (or only server)
00007 define(
"DB_WRITE", -2 ); # Write to master (or only server)
00008 define(
"DB_LAST", -3 ); # Whatever
database was used last
00009
00010
# Task-based indexes
00011
# ***NOT USED YET, EXPERIMENTAL***
00012
# These may be defined in $wgDBservers. If they aren't, the default reader or writer will be used
00013
# Even numbers are always readers, odd numbers are writers
00014 define(
"DB_TASK_FIRST", 1000 ); # First in list
00015 define(
"DB_SEARCH_R", 1000 ); # Search read
00016 define(
"DB_SEARCH_W", 1001 ); # Search write
00017 define(
"DB_ASKSQL_R", 1002 ); # Special:Asksql read
00018 define(
"DB_WATCHLIST_R", 1004 ); # Watchlist read
00019 define(
"DB_TASK_LAST", 1004) ; # Last in list
00020
00021 class LoadBalancer {
00022 var
$mServers,
$mConnections,
$mLoads;
00023 var
$mUser,
$mPassword,
$mDbName,
$mFailFunction;
00024 var
$mForce,
$mReadIndex,
$mLastConn;
00025
00026 function
LoadBalancer()
00027 {
00028 $this->mServers = array();
00029 $this->mLoads = array();
00030 $this->mConnections = array();
00031 $this->mUser =
false;
00032 $this->mPassword =
false;
00033 $this->mDbName =
false;
00034 $this->mFailFunction =
false;
00035 $this->mReadIndex = -1;
00036 $this->mForce = -1;
00037 $this->mLastConn =
false;
00038 }
00039
00040 function
newFromParams( $servers, $loads, $user, $password, $dbName, $failFunction =
false )
00041 {
00042 $lb =
new LoadBalancer;
00043 $lb->initialise( $servers, $loads, $
user, $
password, $dbName, $failFunction =
false );
00044
return $lb;
00045 }
00046
00047 function
initialise( $servers, $loads, $user, $password, $dbName, $failFunction =
false )
00048 {
00049 $this->mServers = $servers;
00050 $this->mLoads = $loads;
00051 $this->mUser =
$user;
00052 $this->mPassword = $password;
00053 $this->mDbName = $dbName;
00054 $this->mFailFunction = $failFunction;
00055 $this->mReadIndex = -1;
00056 $this->mWriteIndex = -1;
00057 $this->mForce = -1;
00058 $this->mConnections = array();
00059 $this->mLastConn =
false;
00060
wfSeedRandom();
00061 }
00062
00063
# Given an array of non-normalised probabilities, this function will select
00064
# an element and return the appropriate key
00065 function
pickRandom( $weights )
00066 {
00067
if ( !is_array( $weights ) || count( $weights ) == 0 ) {
00068
return false;
00069 }
00070
00071 $sum = 0;
00072 foreach ( $weights as $w ) {
00073 $sum += $w;
00074 }
00075 $rand = mt_rand() / RAND_MAX * $sum;
00076
00077 $sum = 0;
00078 foreach ( $weights as $i => $w ) {
00079 $sum += $w;
00080
if ( $sum >= $rand ) {
00081
break;
00082 }
00083 }
00084
return $i;
00085 }
00086
00087 function &
getReader()
00088 {
00089
if ( $this->mForce >= 0 ) {
00090
$conn =& $this->
getConnection( $this->mForce );
00091 }
else {
00092
if ( $this->mReadIndex >= 0 ) {
00093
$conn =& $this->
getConnection( $this->mReadIndex );
00094 }
else {
00095
# $loads is $this->mLoads except with elements knocked out if they
00096
# don't work
00097
$loads = $this->mLoads;
00098
do {
00099
$i = pickRandom( $loads );
00100
if (
$i !==
false ) {
00101
$conn =& $this->
getConnection( $i );
00102
if ( !
$conn->isOpen() ) {
00103 unset( $loads[$i] );
00104 }
00105 }
00106 }
while (
$i !==
false && !
$conn->isOpen() );
00107
if (
$conn->isOpen() ) {
00108 $this->mReadIndex =
$i;
00109 }
00110 }
00111 }
00112
if (
$conn ===
false || !
$conn->isOpen() ) {
00113 $this->
reportConnectionError( $conn );
00114
$conn =
false;
00115 }
00116
return $conn;
00117 }
00118
00119 function &
getConnection( $i, $fail =
false )
00120 {
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
# Operation-based index
00134
# Note, getReader() and getWriter() will re-enter this function
00135
if (
$i ==
DB_READ ) {
00136 $this->mLastConn =& $this->
getReader();
00137 } elseif ( $i ==
DB_WRITE ) {
00138 $this->mLastConn =& $this->
getWriter();
00139 } elseif ( $i ==
DB_LAST ) {
00140
# Just use $this->mLastConn, which should already be set
00141
if ( $this->mLastConn ===
false ) {
00142
# Oh dear, not set, best to use the writer for safety
00143
$this->mLastConn =& $this->
getWriter();
00144 }
00145 }
else {
00146
# Explicit index
00147
if ( !array_key_exists( $i, $this->mConnections) || !$this->mConnections[
$i]->isOpen() ) {
00148 $this->mConnections[
$i] = Database::newFromParams( $this->mServers[$i], $this->mUser,
00149 $this->mPassword, $this->mDbName, 1 );
00150 }
00151
if ( !$this->mConnections[
$i]->isOpen() ) {
00152
wfDebug(
"Failed to connect to database $i at {$this->mServers[$i]}\n" );
00153
if ( $fail ) {
00154 $this->
reportConnectionError( $this->mConnections[$i] );
00155 }
00156 $this->mConnections[
$i] =
false;
00157 }
00158 $this->mLastConn =& $this->mConnections[
$i];
00159 }
00160
return $this->mLastConn;
00161 }
00162
00163 function
reportConnectionError( &$conn )
00164 {
00165
if ( !is_object( $conn ) ) {
00166
$conn =
new Database;
00167 }
00168
if ( $this->mFailFunction ) {
00169
$conn->setFailFunction( $this->mFailFunction );
00170 }
else {
00171
$conn->setFailFunction(
"wfEmergencyAbort" );
00172 }
00173
$conn->reportConnectionError();
00174 }
00175
00176 function &
getWriter()
00177 {
00178 $c =& $this->getConnection( 0 );
00179
if ( $c ===
false || !$c->isOpen() ) {
00180 reportConnectionError( $conn );
00181 $c =
false;
00182 }
00183
return $c;
00184 }
00185
00186 function
force( $i )
00187 {
00188 $this->mForce =
$i;
00189 }
00190
00191 function
haveIndex( $i )
00192 {
00193
return array_key_exists( $i, $this->mServers );
00194 }
00195 }