From 6ade2968aedc9d961753961361b62e46ab806ecd Mon Sep 17 00:00:00 2001 From: Aaron Schulz Date: Tue, 9 Dec 2014 23:56:46 -0800 Subject: [PATCH] Avoid blocking on ChronologyProtecter check in LoadBalancer::getConnection() Change-Id: Iccf324d87d117972cc6321b2abf1ff101d98fa65 --- includes/db/DatabaseMysqlBase.php | 10 ++++++++-- includes/db/DatabaseUtility.php | 4 ++++ includes/db/LoadBalancer.php | 26 ++++++++++++++++++++++---- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/includes/db/DatabaseMysqlBase.php b/includes/db/DatabaseMysqlBase.php index 7dfae6302b..430b20c46b 100644 --- a/includes/db/DatabaseMysqlBase.php +++ b/includes/db/DatabaseMysqlBase.php @@ -1271,13 +1271,15 @@ class MySQLField implements Field { class MySQLMasterPos implements DBMasterPos { /** @var string */ public $file; - - /** @var int Timestamp */ + /** @var int Position */ public $pos; + /** @var float UNIX timestamp */ + public $asOfTime = 0.0; function __construct( $file, $pos ) { $this->file = $file; $this->pos = $pos; + $this->asOfTime = microtime( true ); } function __toString() { @@ -1303,4 +1305,8 @@ class MySQLMasterPos implements DBMasterPos { return ( $thisPos && $thatPos && $thisPos >= $thatPos ); } + + function asOfTime() { + return $this->asOfTime; + } } diff --git a/includes/db/DatabaseUtility.php b/includes/db/DatabaseUtility.php index c1e80d3331..4e5ed08cf8 100644 --- a/includes/db/DatabaseUtility.php +++ b/includes/db/DatabaseUtility.php @@ -339,4 +339,8 @@ class LikeMatch { * The implementation details of this opaque type are up to the database subclass. */ interface DBMasterPos { + /** + * @return float UNIX timestamp + */ + public function asOfTime(); } diff --git a/includes/db/LoadBalancer.php b/includes/db/LoadBalancer.php index 07645bdd85..6930039ba0 100644 --- a/includes/db/LoadBalancer.php +++ b/includes/db/LoadBalancer.php @@ -151,17 +151,23 @@ 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 * @return bool|int|string */ - private function getRandomNonLagged( array $loads, $wiki = false ) { - # Unset excessively lagged servers + private function getRandomNonLagged( array $loads, $wiki = false, $maxLag = INF ) { $lags = $this->getLagTimes( $wiki ); + + # Unset excessively lagged servers foreach ( $lags as $i => $lag ) { if ( $i != 0 ) { + $maxServerLag = $maxLag; + if ( isset( $this->mServers[$i]['max lag'] ) ) { + $maxServerLag = min( $maxServerLag, $this->mServers[$i]['max lag'] ); + } if ( $lag === false ) { wfDebugLog( 'replication', "Server #$i is not replicating" ); unset( $loads[$i] ); - } elseif ( isset( $this->mServers[$i]['max lag'] ) && $lag > $this->mServers[$i]['max lag'] ) { + } elseif ( $lag > $maxServerLag ) { wfDebugLog( 'replication', "Server #$i is excessively lagged ($lag seconds)" ); unset( $loads[$i] ); } @@ -252,7 +258,19 @@ class LoadBalancer { if ( $wgReadOnly || $this->mAllowLagged || $laggedSlaveMode ) { $i = ArrayUtils::pickRandom( $currentLoads ); } else { - $i = $this->getRandomNonLagged( $currentLoads, $wiki ); + $i = false; + if ( $this->mWaitForPos && $this->mWaitForPos->asOfTime() ) { + # ChronologyProtecter causes mWaitForPos to be set via sessions. + # This triggers doWait() after connect, so it's especially good to + # avoid lagged servers so as to avoid just blocking in that method. + $ago = microtime( true ) - $this->mWaitForPos->asOfTime(); + # Aim for <= 1 second of waiting (being too picky can backfire) + $i = $this->getRandomNonLagged( $currentLoads, $wiki, $ago + 1 ); + } + if ( $i === false ) { + # Any server with less lag than it's 'max lag' param is preferable + $i = $this->getRandomNonLagged( $currentLoads, $wiki ); + } if ( $i === false && count( $currentLoads ) != 0 ) { # All slaves lagged. Switch to read-only mode wfDebugLog( 'replication', "All slaves lagged. Switch to read-only mode" ); -- 2.20.1