jenkins-bot has submitted this change and it was merged.
Change subject: Fixes to masterPosWait() for master switchovers
......................................................................
Fixes to masterPosWait() for master switchovers
* Clean up return value types and docs.
* Handle master switch-over better w.r.t the job queue due
to binlog name changes (the host portion). Previously the method
would fail and trigger read-only mode when waiting on former
master positions. Assume the the switch-over was done properly
and thus return immediately.
Bug: T126436
Change-Id: Ib8c05a5c72d03a5c98e41b23c5653fc194b6d130
---
M includes/db/DatabaseMysqlBase.php
M includes/db/DatabaseUtility.php
M includes/db/IDatabase.php
M tests/phpunit/includes/db/DatabaseMysqlBaseTest.php
4 files changed, 116 insertions(+), 33 deletions(-)
Approvals:
Gilles: Looks good to me, approved
jenkins-bot: Verified
diff --git a/includes/db/DatabaseMysqlBase.php
b/includes/db/DatabaseMysqlBase.php
index 7058061..1e27205 100644
--- a/includes/db/DatabaseMysqlBase.php
+++ b/includes/db/DatabaseMysqlBase.php
@@ -757,19 +757,13 @@
return $approxLag;
}
- /**
- * Wait for the slave to catch up to a given master position.
- * @todo Return values for this and base class are rubbish
- *
- * @param DBMasterPos|MySQLMasterPos $pos
- * @param int $timeout The maximum number of seconds to wait for
synchronisation
- * @return int Zero if the slave was past that position already,
- * greater than zero if we waited for some period of time, less than
- * zero if we timed out.
- */
function masterPosWait( DBMasterPos $pos, $timeout ) {
+ if ( !( $pos instanceof MySQLMasterPos ) ) {
+ throw new InvalidArgumentException( "Position not an
instance of MySQLMasterPos" );
+ }
+
if ( $this->lastKnownSlavePos &&
$this->lastKnownSlavePos->hasReached( $pos ) ) {
- return '0'; //
http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html
+ return 0;
}
# Commit any open transactions
@@ -778,18 +772,28 @@
# Call doQuery() directly, to avoid opening a transaction if
DBO_TRX is set
$encFile = $this->addQuotes( $pos->file );
$encPos = intval( $pos->pos );
- $sql = "SELECT MASTER_POS_WAIT($encFile, $encPos, $timeout)";
- $res = $this->doQuery( $sql );
+ $res = $this->doQuery( "SELECT MASTER_POS_WAIT($encFile,
$encPos, $timeout)" );
- $status = false;
- if ( $res ) {
- $row = $this->fetchRow( $res );
- if ( $row ) {
- $status = $row[0]; // can be NULL, -1, or 0+
per the MySQL manual
- if ( ctype_digit( $status ) ) { // success
- $this->lastKnownSlavePos = $pos;
- }
+ $row = $res ? $this->fetchRow( $res ) : false;
+ if ( !$row ) {
+ throw new DBExpectedError( $this, "Failed to query
MASTER_POS_WAIT()" );
+ }
+
+ // Result can be NULL (error), -1 (timeout), or 0+ per the
MySQL manual
+ $status = ( $row[0] !== null ) ? intval( $row[0] ) : null;
+ if ( $status === null ) {
+ // T126436: jobs programmed to wait on master positions
might be referencing binlogs
+ // with an old master hostname. Such calls make
MASTER_POS_WAIT() return null. Try
+ // to detect this and treat the slave as having reached
the position; a proper master
+ // switchover already requires that the new master be
caught up before the switch.
+ $slavePos = $this->getSlavePos();
+ if ( $slavePos && !$slavePos->channelsMatch( $pos ) ) {
+ $this->lastKnownSlavePos = $slavePos;
+ $status = 0;
}
+ } elseif ( $status >= 0 ) {
+ // Remember that this position was reached to save
queries next time
+ $this->lastKnownSlavePos = $pos;
}
return $status;
@@ -1446,12 +1450,35 @@
return ( $thisPos && $thatPos && $thisPos >= $thatPos );
}
+ function channelsMatch( DBMasterPos $pos ) {
+ if ( !( $pos instanceof self ) ) {
+ throw new InvalidArgumentException( "Position not an
instance of " . __CLASS__ );
+ }
+
+ $thisBinlog = $this->getBinlogName();
+ $thatBinlog = $pos->getBinlogName();
+
+ return ( $thisBinlog !== false && $thisBinlog === $thatBinlog );
+ }
+
function __toString() {
// e.g db1034-bin.000976/843431247
return "{$this->file}/{$this->pos}";
}
/**
+ * @return string|bool
+ */
+ protected function getBinlogName() {
+ $m = [];
+ if ( preg_match( '!^(.+)\.(\d+)/(\d+)$!', (string)$this, $m ) )
{
+ return $m[1];
+ }
+
+ return false;
+ }
+
+ /**
* @return array|bool (int, int)
*/
protected function getCoordinates() {
diff --git a/includes/db/DatabaseUtility.php b/includes/db/DatabaseUtility.php
index 6fa8bf0..b6c37ee 100644
--- a/includes/db/DatabaseUtility.php
+++ b/includes/db/DatabaseUtility.php
@@ -333,6 +333,13 @@
public function hasReached( DBMasterPos $pos );
/**
+ * @param DBMasterPos $pos
+ * @return bool Whether this position appears to be for the same
channel as another
+ * @since 1.27
+ */
+ public function channelsMatch( DBMasterPos $pos );
+
+ /**
* @return string
* @since 1.27
*/
diff --git a/includes/db/IDatabase.php b/includes/db/IDatabase.php
index e1d1173..225122d 100644
--- a/includes/db/IDatabase.php
+++ b/includes/db/IDatabase.php
@@ -1183,14 +1183,13 @@
public function wasReadOnlyError();
/**
- * Wait for the slave to catch up to a given master position.
+ * Wait for the slave to catch up to a given master position
*
* @param DBMasterPos $pos
- * @param int $timeout The maximum number of seconds to wait for
- * synchronisation
- * @return int Zero if the slave was past that position already,
+ * @param int $timeout The maximum number of seconds to wait for
synchronisation
+ * @return int|null Zero if the slave was past that position already,
* greater than zero if we waited for some period of time, less than
- * zero if we timed out.
+ * zero if it timed out, and null on error
*/
public function masterPosWait( DBMasterPos $pos, $timeout );
diff --git a/tests/phpunit/includes/db/DatabaseMysqlBaseTest.php
b/tests/phpunit/includes/db/DatabaseMysqlBaseTest.php
index 8e8002c..168b2c6 100644
--- a/tests/phpunit/includes/db/DatabaseMysqlBaseTest.php
+++ b/tests/phpunit/includes/db/DatabaseMysqlBaseTest.php
@@ -247,14 +247,64 @@
];
}
- function testMasterPos() {
- $pos1 = new MySQLMasterPos( 'db1034-bin.000976', '843431247' );
- $pos2 = new MySQLMasterPos( 'db1034-bin.000976', '843431248' );
+ /**
+ * @dataProvider provideComparePositions
+ */
+ function testHasReached( MySQLMasterPos $lowerPos, MySQLMasterPos
$higherPos ) {
+ $this->assertTrue( $higherPos->hasReached( $lowerPos ) );
+ $this->assertTrue( $higherPos->hasReached( $higherPos ) );
+ $this->assertTrue( $lowerPos->hasReached( $lowerPos ) );
+ $this->assertFalse( $lowerPos->hasReached( $higherPos ) );
+ }
- $this->assertTrue( $pos1->hasReached( $pos1 ) );
- $this->assertTrue( $pos2->hasReached( $pos2 ) );
- $this->assertTrue( $pos2->hasReached( $pos1 ) );
- $this->assertFalse( $pos1->hasReached( $pos2 ) );
+ function provideComparePositions() {
+ return [
+ [
+ new MySQLMasterPos( 'db1034-bin.000976',
'843431247' ),
+ new MySQLMasterPos( 'db1034-bin.000976',
'843431248' )
+ ],
+ [
+ new MySQLMasterPos( 'db1034-bin.000976', '999'
),
+ new MySQLMasterPos( 'db1034-bin.000976', '1000'
)
+ ],
+ [
+ new MySQLMasterPos( 'db1034-bin.000976', '999'
),
+ new MySQLMasterPos( 'db1035-bin.000976', '1000'
)
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideChannelPositions
+ */
+ function testChannelsMatch( MySQLMasterPos $pos1, MySQLMasterPos $pos2,
$matches ) {
+ $this->assertEquals( $matches, $pos1->channelsMatch( $pos2 ) );
+ $this->assertEquals( $matches, $pos2->channelsMatch( $pos1 ) );
+ }
+
+ function provideChannelPositions() {
+ return [
+ [
+ new MySQLMasterPos( 'db1034-bin.000876', '44' ),
+ new MySQLMasterPos( 'db1034-bin.000976', '74' ),
+ true
+ ],
+ [
+ new MySQLMasterPos( 'db1052-bin.000976', '999'
),
+ new MySQLMasterPos( 'db1052-bin.000976', '1000'
),
+ true
+ ],
+ [
+ new MySQLMasterPos( 'db1066-bin.000976', '9999'
),
+ new MySQLMasterPos( 'db1035-bin.000976',
'10000' ),
+ false
+ ],
+ [
+ new MySQLMasterPos( 'db1066-bin.000976', '9999'
),
+ new MySQLMasterPos( 'trump2016.000976', '10000'
),
+ false
+ ],
+ ];
}
/**
--
To view, visit https://gerrit.wikimedia.org/r/271427
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Ib8c05a5c72d03a5c98e41b23c5653fc194b6d130
Gerrit-PatchSet: 5
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Aaron Schulz <[email protected]>
Gerrit-Reviewer: Aaron Schulz <[email protected]>
Gerrit-Reviewer: Gilles <[email protected]>
Gerrit-Reviewer: Jcrespo <[email protected]>
Gerrit-Reviewer: Krinkle <[email protected]>
Gerrit-Reviewer: Ori.livneh <[email protected]>
Gerrit-Reviewer: Parent5446 <[email protected]>
Gerrit-Reviewer: Tim Starling <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits