X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Fdb%2Floadbalancer%2FLoadBalancer.php;h=bc9465ba60a0fa38a529e8e021d09c5e5dc18127;hb=52010e6d21d8bdefa1c89fbc9421850185cc5011;hp=3fcd34929c16a92395fe37414860bb881ad10317;hpb=f22024c7c2ce02f1304ca84040831a0f13bd9cb9;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/db/loadbalancer/LoadBalancer.php b/includes/db/loadbalancer/LoadBalancer.php index 3fcd34929c..bc9465ba60 100644 --- a/includes/db/loadbalancer/LoadBalancer.php +++ b/includes/db/loadbalancer/LoadBalancer.php @@ -55,19 +55,26 @@ class LoadBalancer { /** @var bool|DBMasterPos False if not set */ private $mWaitForPos; /** @var bool Whether the generic reader fell back to a lagged slave */ - private $mLaggedSlaveMode; + private $laggedSlaveMode = false; + /** @var bool Whether the generic reader fell back to a lagged slave */ + private $slavesDownMode = false; /** @var string The last DB selection or connection error */ private $mLastError = 'Unknown error'; + /** @var string|bool Reason the LB is read-only or false if not */ + private $readOnlyReason = false; /** @var integer Total connections opened */ private $connsOpened = 0; /** @var integer Warn when this many connection are held */ const CONN_HELD_WARN_THRESHOLD = 10; + /** @var integer Default 'max lag' when unspecified */ + const MAX_LAG = 30; /** * @param array $params Array with keys: - * servers Required. Array of server info structures. - * loadMonitor Name of a class used to fetch server lag and load. + * - servers : Required. Array of server info structures. + * - loadMonitor : Name of a class used to fetch server lag and load. + * - readOnlyReason : Reason the master DB is read-only if so [optional] * @throws MWException */ public function __construct( array $params ) { @@ -85,10 +92,13 @@ class LoadBalancer { 'foreignFree' => array() ); $this->mLoads = array(); $this->mWaitForPos = false; - $this->mLaggedSlaveMode = false; $this->mErrorConnection = false; $this->mAllowLagged = false; + if ( isset( $params['readOnlyReason'] ) && is_string( $params['readOnlyReason'] ) ) { + $this->readOnlyReason = $params['readOnlyReason']; + } + if ( isset( $params['loadMonitor'] ) ) { $this->mLoadMonitorClass = $params['loadMonitor']; } else { @@ -152,10 +162,10 @@ class LoadBalancer { /** * @param array $loads * @param bool|string $wiki Wiki to get non-lagged for - * @param float $maxLag Restrict the maximum allowed lag to this many seconds + * @param int $maxLag Restrict the maximum allowed lag to this many seconds * @return bool|int|string */ - private function getRandomNonLagged( array $loads, $wiki = false, $maxLag = INF ) { + private function getRandomNonLagged( array $loads, $wiki = false, $maxLag = self::MAX_LAG ) { $lags = $this->getLagTimes( $wiki ); # Unset excessively lagged servers @@ -327,7 +337,7 @@ class LoadBalancer { $this->mReadIndex = $i; # Record if the generic reader index is in "lagged slave" mode if ( $laggedSlaveMode ) { - $this->mLaggedSlaveMode = true; + $this->laggedSlaveMode = true; } } $serverName = $this->getServerName( $i ); @@ -350,7 +360,7 @@ class LoadBalancer { if ( $i > 0 ) { if ( !$this->doWait( $i ) ) { $this->mServers[$i]['slave pos'] = $this->getAnyOpenConnection( $i )->getSlavePos(); - $this->mLaggedSlaveMode = true; + $this->laggedSlaveMode = true; } } } @@ -546,6 +556,11 @@ class LoadBalancer { $trxProf->recordConnection( $host, $dbname, $masterOnly ); } + if ( $masterOnly ) { + # Make master-requested DB handles inherit any read-only mode setting + $conn->setLBInfo( 'readOnlyReason', $this->getReadOnlyReason( $wiki ) ); + } + return $conn; } @@ -1131,13 +1146,56 @@ class LoadBalancer { } /** + * @note This method will trigger a DB connection if not yet done + * + * @param string|bool $wiki Wiki ID, or false for the current wiki * @return bool Whether the generic connection for reads is highly "lagged" */ - public function getLaggedSlaveMode() { - # Get a generic reader connection - $this->getConnection( DB_SLAVE ); + public function getLaggedSlaveMode( $wiki = false ) { + // No-op if there is only one DB (also avoids recursion) + if ( !$this->laggedSlaveMode && $this->getServerCount() > 1 ) { + try { + // See if laggedSlaveMode gets set + $this->getConnection( DB_SLAVE, false, $wiki ); + } catch ( DBConnectionError $e ) { + // Avoid expensive re-connect attempts and failures + $this->slavesDownMode = true; + $this->laggedSlaveMode = true; + } + } + + return $this->laggedSlaveMode; + } + + /** + * @note This method will never cause a new DB connection + * @return bool Whether any generic connection used for reads was highly "lagged" + * @since 1.27 + */ + public function laggedSlaveUsed() { + return $this->laggedSlaveMode; + } + + /** + * @note This method may trigger a DB connection if not yet done + * @param string|bool $wiki Wiki ID, or false for the current wiki + * @return string|bool Reason the master is read-only or false if it is not + * @since 1.27 + */ + public function getReadOnlyReason( $wiki = false ) { + if ( $this->readOnlyReason !== false ) { + return $this->readOnlyReason; + } elseif ( $this->getLaggedSlaveMode( $wiki ) ) { + if ( $this->slavesDownMode ) { + return 'The database has been automatically locked ' . + 'until the slave database servers become available'; + } else { + return 'The database has been automatically locked ' . + 'while the slave database servers catch up to the master.'; + } + } - return $this->mLaggedSlaveMode; + return false; } /** @@ -1221,12 +1279,14 @@ class LoadBalancer { } /** - * Get lag time for each server + * Get an estimate of replication lag (in seconds) for each server * * Results are cached for a short time in memcached/process cache * + * Values may be "false" if replication is too broken to estimate + * * @param string|bool $wiki - * @return int[] Map of (server index => seconds) + * @return int[] Map of (server index => float|int|bool) */ public function getLagTimes( $wiki = false ) { if ( $this->getServerCount() <= 1 ) {