Main Page | Namespace List | Class Hierarchy | Class List | File List | Class Members | File Members | Related Pages

LoadBalancer.php

Go to the documentation of this file.
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 /* private */ var $mServers, $mConnections, $mLoads; 00023 /* private */ var $mUser, $mPassword, $mDbName, $mFailFunction; 00024 /* private */ 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 # Task-based index 00123 if ( $i >= DB_TASK_FIRST && $i < DB_TASK_LAST ) { 00124 if ( $i % 2 ) { 00125 # Odd index use writer 00126 $i = DB_WRITE; 00127 } else { 00128 # Even index use reader 00129 $i = DB_READ; 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 }

Generated on Tue Jun 29 23:40:05 2004 for Mediawiki by doxygen 1.3.7