Add missing visibility keywords to all LoadBalancer methods
[lhc/web/wiklou.git] / includes / db / LoadBalancer.php
index 0ccbd93..07645bd 100644 (file)
  * @ingroup Database
  */
 class LoadBalancer {
-       /** @var array Map of (server index => server config array) */
+       /** @var array[] Map of (server index => server config array) */
        private $mServers;
-       /** @var array Map of (local/foreignUsed/foreignFree => server index => DatabaseBase array) */
+       /** @var array[] Map of (local/foreignUsed/foreignFree => server index => DatabaseBase array) */
        private $mConns;
        /** @var array Map of (server index => weight) */
        private $mLoads;
-       /** @var array Map of (group => server index => weight) */
+       /** @var array[] Map of (group => server index => weight) */
        private $mGroupLoads;
        /** @var bool Whether to disregard slave lag as a factor in slave selection */
        private $mAllowLagged;
@@ -58,8 +58,8 @@ class LoadBalancer {
        private $mLaggedSlaveMode;
        /** @var string The last DB selection or connection error */
        private $mLastError = 'Unknown error';
-       /** @var array Process cache of LoadMonitor::getLagTimes() */
-       private $mLagTimes;
+       /** @var ProcessCacheLRU */
+       private $mProcCache;
 
        /**
         * @param array $params Array with keys:
@@ -67,7 +67,7 @@ class LoadBalancer {
         *   loadMonitor       Name of a class used to fetch server lag and load.
         * @throws MWException
         */
-       function __construct( $params ) {
+       public function __construct( array $params ) {
                if ( !isset( $params['servers'] ) ) {
                        throw new MWException( __CLASS__ . ': missing servers parameter' );
                }
@@ -108,6 +108,8 @@ class LoadBalancer {
                                }
                        }
                }
+
+               $this->mProcCache = new ProcessCacheLRU( 30 );
        }
 
        /**
@@ -115,7 +117,7 @@ class LoadBalancer {
         *
         * @return LoadMonitor
         */
-       function getLoadMonitor() {
+       private function getLoadMonitor() {
                if ( !isset( $this->mLoadMonitor ) ) {
                        $class = $this->mLoadMonitorClass;
                        $this->mLoadMonitor = new $class( $this );
@@ -129,7 +131,7 @@ class LoadBalancer {
         * @param mixed $x
         * @return mixed
         */
-       function parentInfo( $x = null ) {
+       public function parentInfo( $x = null ) {
                return wfSetVar( $this->mParentInfo, $x );
        }
 
@@ -142,7 +144,7 @@ class LoadBalancer {
         * @param array $weights
         * @return bool|int|string
         */
-       function pickRandom( $weights ) {
+       public function pickRandom( array $weights ) {
                return ArrayUtils::pickRandom( $weights );
        }
 
@@ -151,7 +153,7 @@ class LoadBalancer {
         * @param bool|string $wiki Wiki to get non-lagged for
         * @return bool|int|string
         */
-       function getRandomNonLagged( $loads, $wiki = false ) {
+       private function getRandomNonLagged( array $loads, $wiki = false ) {
                # Unset excessively lagged servers
                $lags = $this->getLagTimes( $wiki );
                foreach ( $lags as $i => $lag ) {
@@ -195,12 +197,12 @@ class LoadBalancer {
         * always return a consistent index during a given invocation
         *
         * Side effect: opens connections to databases
-        * @param bool|string $group
-        * @param bool|string $wiki
+        * @param string|bool $group Query group, or false for the generic reader
+        * @param string|bool $wiki Wiki ID, or false for the current wiki
         * @throws MWException
         * @return bool|int|string
         */
-       function getReaderIndex( $group = false, $wiki = false ) {
+       public function getReaderIndex( $group = false, $wiki = false ) {
                global $wgReadOnly, $wgDBtype;
 
                # @todo FIXME: For now, only go through all this for mysql databases
@@ -216,7 +218,7 @@ class LoadBalancer {
                        return $this->mReadIndex;
                }
 
-               $section = new ProfileSection( __METHOD__ );
+               new ProfileSection( __METHOD__ );
 
                # Find the relevant load array
                if ( $group !== false ) {
@@ -313,20 +315,6 @@ class LoadBalancer {
                return $i;
        }
 
-       /**
-        * Wait for a specified number of microseconds, and return the period waited
-        * @param int $t
-        * @return int
-        */
-       function sleep( $t ) {
-               wfProfileIn( __METHOD__ );
-               wfDebug( __METHOD__ . ": waiting $t us\n" );
-               usleep( $t );
-               wfProfileOut( __METHOD__ );
-
-               return $t;
-       }
-
        /**
         * Set the master wait position
         * If a DB_SLAVE connection has been opened already, waits
@@ -376,7 +364,7 @@ class LoadBalancer {
         * @param int $i
         * @return DatabaseBase|bool False on failure
         */
-       function getAnyOpenConnection( $i ) {
+       public function getAnyOpenConnection( $i ) {
                foreach ( $this->mConns as $conns ) {
                        if ( !empty( $conns[$i] ) ) {
                                return reset( $conns[$i] );
@@ -422,7 +410,10 @@ class LoadBalancer {
 
                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" );
+                       $server = $this->mServers[$index]['host'];
+                       $msg = __METHOD__ . ": Timed out waiting on $server pos {$this->mWaitForPos}";
+                       wfDebug( "$msg\n" );
+                       wfDebugLog( 'DBPerformance', "$msg:\n" . wfBacktrace( true ) );
                        $ok = false;
                } else {
                        wfDebug( __METHOD__ . ": Done\n" );
@@ -441,8 +432,8 @@ class LoadBalancer {
         * This is the main entry point for this class.
         *
         * @param int $i Server index
-        * @param array $groups Query groups
-        * @param bool|string $wiki Wiki ID
+        * @param array|string|bool $groups Query group(s), or false for the generic reader
+        * @param string|bool $wiki Wiki ID, or false for the current wiki
         *
         * @throws MWException
         * @return DatabaseBase
@@ -565,8 +556,8 @@ class LoadBalancer {
         * @see LoadBalancer::getConnection() for parameter information
         *
         * @param int $db
-        * @param mixed $groups
-        * @param bool|string $wiki
+        * @param array|string|bool $groups Query group(s), or false for the generic reader
+        * @param string|bool $wiki Wiki ID, or false for the current wiki
         * @return DBConnRef
         */
        public function getConnectionRef( $db, $groups = array(), $wiki = false ) {
@@ -581,8 +572,8 @@ class LoadBalancer {
         * @see LoadBalancer::getConnection() for parameter information
         *
         * @param int $db
-        * @param mixed $groups
-        * @param bool|string $wiki
+        * @param array|string|bool $groups Query group(s), or false for the generic reader
+        * @param string|bool $wiki Wiki ID, or false for the current wiki
         * @return DBConnRef
         */
        public function getLazyConnectionRef( $db, $groups = array(), $wiki = false ) {
@@ -598,12 +589,12 @@ class LoadBalancer {
         * error will be available via $this->mErrorConnection.
         *
         * @param int $i Server index
-        * @param bool|string $wiki Wiki ID to open
+        * @param string|bool $wiki Wiki ID, or false for the current wiki
         * @return DatabaseBase
         *
         * @access private
         */
-       function openConnection( $i, $wiki = false ) {
+       public function openConnection( $i, $wiki = false ) {
                wfProfileIn( __METHOD__ );
                if ( $wiki !== false ) {
                        $conn = $this->openForeignConnection( $i, $wiki );
@@ -649,7 +640,7 @@ class LoadBalancer {
         * @param string $wiki Wiki ID to open
         * @return DatabaseBase
         */
-       function openForeignConnection( $i, $wiki ) {
+       private function openForeignConnection( $i, $wiki ) {
                wfProfileIn( __METHOD__ );
                list( $dbName, $prefix ) = wfSplitWikiID( $wiki );
                if ( isset( $this->mConns['foreignUsed'][$i][$wiki] ) ) {
@@ -667,7 +658,9 @@ class LoadBalancer {
                        $conn = reset( $this->mConns['foreignFree'][$i] );
                        $oldWiki = key( $this->mConns['foreignFree'][$i] );
 
-                       if ( !$conn->selectDB( $dbName ) ) {
+                       // The empty string as a DB name means "don't care".
+                       // DatabaseMysqlBase::open() already handle this on connection.
+                       if ( $dbName !== '' && !$conn->selectDB( $dbName ) ) {
                                $this->mLastError = "Error selecting database $dbName on server " .
                                        $conn->getServer() . " from client host " . wfHostname() . "\n";
                                $this->mErrorConnection = $conn;
@@ -713,7 +706,7 @@ class LoadBalancer {
         * @access private
         * @return bool
         */
-       function isOpen( $index ) {
+       private function isOpen( $index ) {
                if ( !is_integer( $index ) ) {
                        return false;
                }
@@ -731,7 +724,7 @@ class LoadBalancer {
         * @throws MWException
         * @return DatabaseBase
         */
-       function reallyOpenConnection( $server, $dbNameOverride = false ) {
+       protected 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.' );
@@ -767,17 +760,27 @@ class LoadBalancer {
         */
        private function reportConnectionError() {
                $conn = $this->mErrorConnection; // The connection which caused the error
+               $context = array(
+                       'method' => __METHOD__,
+                       'last_error' => $this->mLastError,
+               );
 
                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}" );
+                       wfLogDBError(
+                               "LB failure with no last connection. Connection error: {last_error}",
+                               $context
+                       );
 
                        // If all servers were busy, mLastError will contain something sensible
                        throw new DBConnectionError( null, $this->mLastError );
                } else {
-                       $server = $conn->getProperty( 'mServer' );
-                       wfLogDBError( "Connection error: {$this->mLastError} ({$server})" );
-                       $conn->reportConnectionError( "{$this->mLastError} ({$server})" ); // throws DBConnectionError
+                       $context['db_server'] = $conn->getProperty( 'mServer' );
+                       wfLogDBError(
+                               "Connection error: {last_error} ({db_server})",
+                               $context
+                       );
+                       $conn->reportConnectionError( "{$this->mLastError} ({$context['db_server']})" ); // throws DBConnectionError
                }
 
                return false; /* not reached */
@@ -786,7 +789,7 @@ class LoadBalancer {
        /**
         * @return int
         */
-       function getWriterIndex() {
+       private function getWriterIndex() {
                return 0;
        }
 
@@ -796,7 +799,7 @@ class LoadBalancer {
         * @param string $i
         * @return bool
         */
-       function haveIndex( $i ) {
+       public function haveIndex( $i ) {
                return array_key_exists( $i, $this->mServers );
        }
 
@@ -806,7 +809,7 @@ class LoadBalancer {
         * @param string $i
         * @return bool
         */
-       function isNonZeroLoad( $i ) {
+       public function isNonZeroLoad( $i ) {
                return array_key_exists( $i, $this->mServers ) && $this->mLoads[$i] != 0;
        }
 
@@ -815,7 +818,7 @@ class LoadBalancer {
         *
         * @return int
         */
-       function getServerCount() {
+       public function getServerCount() {
                return count( $this->mServers );
        }
 
@@ -825,7 +828,7 @@ class LoadBalancer {
         * @param string $i
         * @return string
         */
-       function getServerName( $i ) {
+       public function getServerName( $i ) {
                if ( isset( $this->mServers[$i]['hostName'] ) ) {
                        return $this->mServers[$i]['hostName'];
                } elseif ( isset( $this->mServers[$i]['host'] ) ) {
@@ -840,7 +843,7 @@ class LoadBalancer {
         * @param int $i
         * @return array|bool
         */
-       function getServerInfo( $i ) {
+       public function getServerInfo( $i ) {
                if ( isset( $this->mServers[$i] ) ) {
                        return $this->mServers[$i];
                } else {
@@ -854,7 +857,7 @@ class LoadBalancer {
         * @param int $i
         * @param array $serverInfo
         */
-       function setServerInfo( $i, $serverInfo ) {
+       public function setServerInfo( $i, array $serverInfo ) {
                $this->mServers[$i] = $serverInfo;
        }
 
@@ -862,7 +865,7 @@ class LoadBalancer {
         * Get the current master position for chronology control purposes
         * @return mixed
         */
-       function getMasterPos() {
+       public function getMasterPos() {
                # If this entire request was served from a slave without opening a connection to the
                # master (however unlikely that may be), then we can fetch the position from the slave.
                $masterConn = $this->getAnyOpenConnection( 0 );
@@ -888,7 +891,7 @@ class LoadBalancer {
        /**
         * Close all open connections
         */
-       function closeAll() {
+       public function closeAll() {
                foreach ( $this->mConns as $conns2 ) {
                        foreach ( $conns2 as $conns3 ) {
                                /** @var DatabaseBase $conn */
@@ -910,7 +913,7 @@ class LoadBalancer {
         * If you use $conn->close() directly, the load balancer won't update its state.
         * @param DatabaseBase $conn
         */
-       function closeConnection( $conn ) {
+       public function closeConnection( $conn ) {
                $done = false;
                foreach ( $this->mConns as $i1 => $conns2 ) {
                        foreach ( $conns2 as $i2 => $conns3 ) {
@@ -932,7 +935,7 @@ class LoadBalancer {
        /**
         * Commit transactions on all open connections
         */
-       function commitAll() {
+       public function commitAll() {
                foreach ( $this->mConns as $conns2 ) {
                        foreach ( $conns2 as $conns3 ) {
                                /** @var DatabaseBase[] $conns3 */
@@ -948,7 +951,7 @@ class LoadBalancer {
        /**
         *  Issue COMMIT only on master, only if queries were done on connection
         */
-       function commitMasterChanges() {
+       public function commitMasterChanges() {
                // Always 0, but who knows.. :)
                $masterIndex = $this->getWriterIndex();
                foreach ( $this->mConns as $conns2 ) {
@@ -968,7 +971,7 @@ class LoadBalancer {
         * Issue ROLLBACK only on master, only if queries were done on connection
         * @since 1.23
         */
-       function rollbackMasterChanges() {
+       public function rollbackMasterChanges() {
                // Always 0, but who knows.. :)
                $masterIndex = $this->getWriterIndex();
                foreach ( $this->mConns as $conns2 ) {
@@ -988,7 +991,7 @@ class LoadBalancer {
         * @return bool Whether a master connection is already open
         * @since 1.24
         */
-       function hasMasterConnection() {
+       public function hasMasterConnection() {
                return $this->isOpen( $this->getWriterIndex() );
        }
 
@@ -998,7 +1001,7 @@ class LoadBalancer {
         * @since 1.23
         * @return bool
         */
-       function hasMasterChanges() {
+       public function hasMasterChanges() {
                // Always 0, but who knows.. :)
                $masterIndex = $this->getWriterIndex();
                foreach ( $this->mConns as $conns2 ) {
@@ -1019,14 +1022,14 @@ class LoadBalancer {
         * @param mixed $value
         * @return mixed
         */
-       function waitTimeout( $value = null ) {
+       public function waitTimeout( $value = null ) {
                return wfSetVar( $this->mWaitTimeout, $value );
        }
 
        /**
         * @return bool
         */
-       function getLaggedSlaveMode() {
+       public function getLaggedSlaveMode() {
                return $this->mLaggedSlaveMode;
        }
 
@@ -1035,7 +1038,7 @@ class LoadBalancer {
         * @param null|bool $mode
         * @return bool
         */
-       function allowLagged( $mode = null ) {
+       public function allowLagged( $mode = null ) {
                if ( $mode === null ) {
                        return $this->mAllowLagged;
                }
@@ -1047,7 +1050,7 @@ class LoadBalancer {
        /**
         * @return bool
         */
-       function pingAll() {
+       public function pingAll() {
                $success = true;
                foreach ( $this->mConns as $conns2 ) {
                        foreach ( $conns2 as $conns3 ) {
@@ -1068,7 +1071,7 @@ class LoadBalancer {
         * @param callable $callback
         * @param array $params
         */
-       function forEachOpenConnection( $callback, $params = array() ) {
+       public function forEachOpenConnection( $callback, array $params = array() ) {
                foreach ( $this->mConns as $conns2 ) {
                        foreach ( $conns2 as $conns3 ) {
                                foreach ( $conns3 as $conn ) {
@@ -1080,7 +1083,8 @@ class LoadBalancer {
        }
 
        /**
-        * Get the hostname and lag time of the most-lagged slave.
+        * 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. If there is
         * no lag, the maximum lag will be reported as -1.
@@ -1088,72 +1092,50 @@ class LoadBalancer {
         * @param bool|string $wiki Wiki ID, or false for the default database
         * @return array ( host, max lag, index of max lagged host )
         */
-       function getMaxLag( $wiki = false ) {
+       public function getMaxLag( $wiki = false ) {
                $maxLag = -1;
                $host = '';
                $maxIndex = 0;
 
-               if ( $this->getServerCount() <= 1 ) { // no replication = no lag
-                       return array( $host, $maxLag, $maxIndex );
+               if ( $this->getServerCount() <= 1 ) {
+                       return array( $host, $maxLag, $maxIndex ); // no replication = no lag
                }
 
-               // Try to get the max lag info from the server cache
-               $key = 'loadbalancer:maxlag:cluster:' . $this->mServers[0]['host'];
-               $cache = ObjectCache::newAccelerator( array(), 'hash' );
-               $maxLagInfo = $cache->get( $key ); // (host, lag, index)
-
-               // Fallback to connecting to each slave and getting the lag
-               if ( !$maxLagInfo ) {
-                       foreach ( $this->mServers as $i => $conn ) {
-                               if ( $i == $this->getWriterIndex() ) {
-                                       continue; // nothing to check
-                               }
-                               $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;
-                               }
+               $lagTimes = $this->getLagTimes( $wiki );
+               foreach ( $lagTimes as $i => $lag ) {
+                       if ( $lag > $maxLag ) {
+                               $maxLag = $lag;
+                               $host = $this->mServers[$i]['host'];
+                               $maxIndex = $i;
                        }
-                       $maxLagInfo = array( $host, $maxLag, $maxIndex );
-                       $cache->set( $key, $maxLagInfo, 5 );
                }
 
-               return $maxLagInfo;
+               return array( $host, $maxLag, $maxIndex );
        }
 
        /**
         * Get lag time for each server
-        * Results are cached for a short time in memcached, and indefinitely in the process cache
+        *
+        * Results are cached for a short time in memcached/process cache
         *
         * @param string|bool $wiki
-        * @return array Map of (server index => seconds)
+        * @return int[] Map of (server index => seconds)
         */
-       function getLagTimes( $wiki = false ) {
-               # Try process cache
-               if ( isset( $this->mLagTimes ) ) {
-                       return $this->mLagTimes;
+       public function getLagTimes( $wiki = false ) {
+               if ( $this->getServerCount() <= 1 ) {
+                       return array( 0 => 0 ); // no replication = no lag
                }
-               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 );
+
+               if ( $this->mProcCache->has( 'slave_lag', 'times', 1 ) ) {
+                       return $this->mProcCache->get( 'slave_lag', 'times' );
                }
 
-               return $this->mLagTimes;
+               # Send the request to the load monitor
+               $times = $this->getLoadMonitor()->getLagTimes( array_keys( $this->mServers ), $wiki );
+
+               $this->mProcCache->set( 'slave_lag', 'times', $times );
+
+               return $times;
        }
 
        /**
@@ -1170,7 +1152,7 @@ class LoadBalancer {
         * @param DatabaseBase $conn
         * @return int
         */
-       function safeGetLag( $conn ) {
+       public function safeGetLag( $conn ) {
                if ( $this->getServerCount() == 1 ) {
                        return 0;
                } else {
@@ -1179,10 +1161,10 @@ class LoadBalancer {
        }
 
        /**
-        * Clear the cache for getLagTimes
+        * Clear the cache for slag lag delay times
         */
-       function clearLagTimeCache() {
-               $this->mLagTimes = null;
+       public function clearLagTimeCache() {
+               $this->mProcCache->clear( 'slave_lag' );
        }
 }
 
@@ -1195,13 +1177,13 @@ class LoadBalancer {
  */
 class DBConnRef implements IDatabase {
        /** @var LoadBalancer */
-       protected $lb;
+       private $lb;
 
        /** @var DatabaseBase|null */
-       protected $conn;
+       private $conn;
 
        /** @var array|null */
-       protected $params;
+       private $params;
 
        /**
         * @param LoadBalancer $lb
@@ -1225,7 +1207,7 @@ class DBConnRef implements IDatabase {
                return call_user_func_array( array( $this->conn, $name ), $arguments );
        }
 
-       function __destruct() {
+       public function __destruct() {
                if ( $this->conn !== null ) {
                        $this->lb->reuseConnection( $this->conn );
                }