From: Aaron Schulz Date: Tue, 8 Mar 2016 20:36:03 +0000 (-0800) Subject: Support custom query in pt-heartbeat lag detection X-Git-Tag: 1.31.0-rc.0~7433^2 X-Git-Url: https://git.cyclocoop.org/%7B%24www_url%7Dadmin/compta/comptes/ajouter.php?a=commitdiff_plain;h=6e38db159cc7b07bbd36c454e3cd06ff0fabc9cf;p=lhc%2Fweb%2Fwiklou.git Support custom query in pt-heartbeat lag detection Bug: T111266 Change-Id: Ib0f9ca8072d9becebacfa8c9afe9d61567f73912 --- diff --git a/includes/db/DatabaseMysqlBase.php b/includes/db/DatabaseMysqlBase.php index 1e2720504c..13be911686 100644 --- a/includes/db/DatabaseMysqlBase.php +++ b/includes/db/DatabaseMysqlBase.php @@ -34,6 +34,8 @@ abstract class DatabaseMysqlBase extends Database { protected $lastKnownSlavePos; /** @var string Method to detect slave lag */ protected $lagDetectionMethod; + /** @var array Method to detect slave lag */ + protected $lagDetectionOptions = []; /** @var string|null */ private $serverVersion = null; @@ -44,6 +46,10 @@ abstract class DatabaseMysqlBase extends Database { * pt-heartbeat assumes the table is at heartbeat.heartbeat * and uses UTC timestamps in the heartbeat.ts column. * (https://www.percona.com/doc/percona-toolkit/2.2/pt-heartbeat.html) + * - lagDetectionOptions : if using pt-heartbeat, this can be set to an array map to change + * the default behavior. Normally, the heartbeat row with the server + * ID of this server's master will be used. Set the "conds" field to + * override the query conditions, e.g. ['shard' => 's1']. * @param array $params */ function __construct( array $params ) { @@ -52,6 +58,9 @@ abstract class DatabaseMysqlBase extends Database { $this->lagDetectionMethod = isset( $params['lagDetectionMethod'] ) ? $params['lagDetectionMethod'] : 'Seconds_Behind_Master'; + $this->lagDetectionOptions = isset( $params['lagDetectionOptions'] ) + ? $params['lagDetectionOptions'] + : []; } /** @@ -652,19 +661,30 @@ abstract class DatabaseMysqlBase extends Database { * @return bool|float */ protected function getLagFromPtHeartbeat() { - $masterInfo = $this->getMasterServerInfo(); - if ( !$masterInfo ) { - wfLogDBError( - "Unable to query master of {db_server} for server ID", - $this->getLogContext( [ - 'method' => __METHOD__ - ] ) - ); + $options = $this->lagDetectionOptions; + + if ( isset( $options['conds'] ) ) { + // Best method for multi-DC setups: use logical channel names + $data = $this->getHeartbeatData( $options['conds'] ); + } else { + // Standard method: use master server ID (works with stock pt-heartbeat) + $masterInfo = $this->getMasterServerInfo(); + if ( !$masterInfo ) { + wfLogDBError( + "Unable to query master of {db_server} for server ID", + $this->getLogContext( [ + 'method' => __METHOD__ + ] ) + ); + + return false; // could not get master server ID + } - return false; // could not get master server ID + $conds = [ 'server_id' => intval( $masterInfo['serverId'] ) ]; + $data = $this->getHeartbeatData( $conds ); } - list( $time, $nowUnix ) = $this->getHeartbeatData( $masterInfo['serverId'] ); + list( $time, $nowUnix ) = $data; if ( $time !== null ) { // @time is in ISO format like "2015-09-25T16:48:10.000510" $dateTime = new DateTime( $time, new DateTimeZone( 'UTC' ) ); @@ -722,17 +742,17 @@ abstract class DatabaseMysqlBase extends Database { } /** - * @param string $masterId Server ID - * @return array (heartbeat `ts` column value or null, UNIX timestamp) + * @param array $conds WHERE clause conditions to find a row + * @return array (heartbeat `ts` column value or null, UNIX timestamp) for the newest beat * @see https://www.percona.com/doc/percona-toolkit/2.1/pt-heartbeat.html */ - protected function getHeartbeatData( $masterId ) { - // Get the status row for this master; use the oldest for sanity in case the master - // has entries listed under different server IDs (which should really not happen). - // Note: this would use "MAX(TIMESTAMPDIFF(MICROSECOND,ts,UTC_TIMESTAMP(6)))" but the + protected function getHeartbeatData( array $conds ) { + $whereSQL = $this->makeList( $conds, LIST_AND ); + // Use ORDER BY for channel based queries since that field might not be UNIQUE. + // Note: this would use "TIMESTAMPDIFF(MICROSECOND,ts,UTC_TIMESTAMP(6))" but the // percision field is not supported in MySQL <= 5.5. $res = $this->query( - "SELECT ts FROM heartbeat.heartbeat WHERE server_id=" . intval( $masterId ) + "SELECT ts FROM heartbeat.heartbeat WHERE $whereSQL ORDER BY ts DESC LIMIT 1" ); $row = $res ? $res->fetchObject() : false; diff --git a/tests/phpunit/includes/db/DatabaseMysqlBaseTest.php b/tests/phpunit/includes/db/DatabaseMysqlBaseTest.php index 168b2c6837..bb747c7c95 100644 --- a/tests/phpunit/includes/db/DatabaseMysqlBaseTest.php +++ b/tests/phpunit/includes/db/DatabaseMysqlBaseTest.php @@ -339,7 +339,7 @@ class DatabaseMysqlBaseTest extends MediaWikiTestCase { $db->expects( $this->any() ) ->method( 'getHeartbeatData' ) - ->with( 172 ) + ->with( [ 'server_id' => 172 ] ) ->will( $this->returnValue( [ $ptTimeISO, $now ] ) ); $db->setLBInfo( 'clusterMasterHost', 'db1052' );