From da4a378e74f2e3636b4f1132e4732ec3c2637eb9 Mon Sep 17 00:00:00 2001 From: Aaron Schulz Date: Mon, 9 Nov 2015 12:44:06 -0800 Subject: [PATCH] Improve LoadBalancer::doWait() performance via APC * This avoids bothering with MASTER_POS_WAIT() if an equal/higher position was already successfully waited on by another process. * Add DBMasterPos toString() and hasReached() methods, which MySQLMasterPos already implemented and used. * Moved more wfDebug() statements to the 'replication' log. Change-Id: I423b5fe2da8d97889a6d204a635e351342de7649 --- includes/db/ChronologyProtector.php | 2 +- includes/db/DatabaseMysqlBase.php | 26 +++++++++------- includes/db/DatabaseUtility.php | 14 +++++++++ includes/db/loadbalancer/LoadBalancer.php | 31 ++++++++++++++----- .../includes/db/DatabaseMysqlBaseTest.php | 9 ++++++ 5 files changed, 62 insertions(+), 20 deletions(-) diff --git a/includes/db/ChronologyProtector.php b/includes/db/ChronologyProtector.php index 0c7b612eba..6840d17c6d 100644 --- a/includes/db/ChronologyProtector.php +++ b/includes/db/ChronologyProtector.php @@ -60,7 +60,7 @@ class ChronologyProtector { if ( !empty( $this->startupPositions[$masterName] ) ) { $info = $lb->parentInfo(); $pos = $this->startupPositions[$masterName]; - wfDebug( __METHOD__ . ": LB " . $info['id'] . " waiting for master pos $pos\n" ); + wfDebug( __METHOD__ . ": LB '" . $info['id'] . "' waiting for master pos $pos\n" ); $lb->waitFor( $pos ); } } diff --git a/includes/db/DatabaseMysqlBase.php b/includes/db/DatabaseMysqlBase.php index 907cdbf248..4f93419107 100644 --- a/includes/db/DatabaseMysqlBase.php +++ b/includes/db/DatabaseMysqlBase.php @@ -1354,6 +1354,21 @@ class MySQLMasterPos implements DBMasterPos { $this->asOfTime = microtime( true ); } + function asOfTime() { + return $this->asOfTime; + } + + function hasReached( DBMasterPos $pos ) { + if ( !( $pos instanceof self ) ) { + throw new InvalidArgumentException( "Position not an instance of " . __CLASS__ ); + } + + $thisPos = $this->getCoordinates(); + $thatPos = $pos->getCoordinates(); + + return ( $thisPos && $thatPos && $thisPos >= $thatPos ); + } + function __toString() { // e.g db1034-bin.000976/843431247 return "{$this->file}/{$this->pos}"; @@ -1370,15 +1385,4 @@ class MySQLMasterPos implements DBMasterPos { return false; } - - function hasReached( MySQLMasterPos $pos ) { - $thisPos = $this->getCoordinates(); - $thatPos = $pos->getCoordinates(); - - return ( $thisPos && $thatPos && $thisPos >= $thatPos ); - } - - function asOfTime() { - return $this->asOfTime; - } } diff --git a/includes/db/DatabaseUtility.php b/includes/db/DatabaseUtility.php index 969ed5e0f2..dea7d94997 100644 --- a/includes/db/DatabaseUtility.php +++ b/includes/db/DatabaseUtility.php @@ -321,6 +321,20 @@ class LikeMatch { interface DBMasterPos { /** * @return float UNIX timestamp + * @since 1.25 */ public function asOfTime(); + + /** + * @param DBMasterPos $pos + * @return bool Whether this position is at or higher than $pos + * @since 1.27 + */ + public function hasReached( DBMasterPos $pos ); + + /** + * @return string + * @since 1.27 + */ + public function __toString(); } diff --git a/includes/db/loadbalancer/LoadBalancer.php b/includes/db/loadbalancer/LoadBalancer.php index a5a5c37d2b..65ee9c716a 100644 --- a/includes/db/loadbalancer/LoadBalancer.php +++ b/includes/db/loadbalancer/LoadBalancer.php @@ -47,6 +47,8 @@ class LoadBalancer { private $mLoadMonitorClass; /** @var LoadMonitor */ private $mLoadMonitor; + /** @var BagOStuff */ + private $srvCache; /** @var bool|DatabaseBase Database connection that caused a problem */ private $mErrorConnection; @@ -123,6 +125,8 @@ class LoadBalancer { } } } + + $this->srvCache = ObjectCache::getLocalServerInstance(); } /** @@ -445,17 +449,27 @@ class LoadBalancer { protected function doWait( $index, $open = false, $timeout = null ) { $close = false; // close the connection afterwards - # Find a connection to wait on, creating one if needed and allowed + // Check if we already know that the DB has reached this point + $server = $this->getServerName( $index ); + $key = $this->srvCache->makeGlobalKey( __CLASS__, 'last-known-pos', $server ); + /** @var DBMasterPos $knownReachedPos */ + $knownReachedPos = $this->srvCache->get( $key ); + if ( $knownReachedPos && $knownReachedPos->hasReached( $this->mWaitForPos ) ) { + wfDebugLog( 'replication', __METHOD__ . ": Slave $server known to be caught up.\n" ); + return true; + } + + // Find a connection to wait on, creating one if needed and allowed $conn = $this->getAnyOpenConnection( $index ); if ( !$conn ) { if ( !$open ) { - wfDebug( __METHOD__ . ": no connection open\n" ); + wfDebugLog( 'replication', __METHOD__ . ": no connection open for $server\n" ); return false; } else { $conn = $this->openConnection( $index, '' ); if ( !$conn ) { - wfDebug( __METHOD__ . ": failed to open connection\n" ); + wfDebugLog( 'replication', __METHOD__ . ": failed to open connection to $server\n" ); return false; } @@ -465,20 +479,21 @@ class LoadBalancer { } } - wfDebug( __METHOD__ . ": Waiting for slave #$index to catch up...\n" ); + wfDebugLog( 'replication', __METHOD__ . ": Waiting for slave $server to catch up...\n" ); $timeout = $timeout ?: $this->mWaitTimeout; $result = $conn->masterPosWait( $this->mWaitForPos, $timeout ); if ( $result == -1 || is_null( $result ) ) { - # Timed out waiting for slave, use master instead - $server = $server = $this->getServerName( $index ); + // Timed out waiting for slave, use master instead $msg = __METHOD__ . ": Timed out waiting on $server pos {$this->mWaitForPos}"; - wfDebug( "$msg\n" ); + wfDebugLog( 'replication', "$msg\n" ); wfDebugLog( 'DBPerformance', "$msg:\n" . wfBacktrace( true ) ); $ok = false; } else { - wfDebug( __METHOD__ . ": Done\n" ); + wfDebugLog( 'replication', __METHOD__ . ": Done\n" ); $ok = true; + // Remember that the DB reached this point + $this->srvCache->set( $key, $this->mWaitForPos, BagOStuff::TTL_DAY ); } if ( $close ) { diff --git a/tests/phpunit/includes/db/DatabaseMysqlBaseTest.php b/tests/phpunit/includes/db/DatabaseMysqlBaseTest.php index 8c09471285..31e4f5b826 100644 --- a/tests/phpunit/includes/db/DatabaseMysqlBaseTest.php +++ b/tests/phpunit/includes/db/DatabaseMysqlBaseTest.php @@ -247,4 +247,13 @@ class DatabaseMysqlBaseTest extends MediaWikiTestCase { ); } + function testMasterPos() { + $pos1 = new MySQLMasterPos( 'db1034-bin.000976', '843431247' ); + $pos2 = new MySQLMasterPos( 'db1034-bin.000976', '843431248' ); + + $this->assertTrue( $pos1->hasReached( $pos1 ) ); + $this->assertTrue( $pos2->hasReached( $pos2 ) ); + $this->assertTrue( $pos2->hasReached( $pos1 ) ); + $this->assertFalse( $pos1->hasReached( $pos2 ) ); + } } -- 2.20.1