namespace Wikimedia\Rdbms;
use InvalidArgumentException;
+use UnexpectedValueException;
/**
* DBMasterPos class for MySQL/MariaDB
* @param float $asOfTime UNIX timestamp
*/
public function __construct( $position, $asOfTime ) {
+ $this->init( $position, $asOfTime );
+ }
+
+ /**
+ * @param string $position
+ * @param float $asOfTime
+ */
+ protected function init( $position, $asOfTime ) {
$m = [];
if ( preg_match( '!^(.+)\.(\d+)/(\d+)$!', $position, $m ) ) {
$this->binlog = $m[1]; // ideally something like host name
$this->pos = [ (int)$m[2], (int)$m[3] ];
} else {
- $this->gtids = array_map( 'trim', explode( ',', $position ) );
+ $gtids = array_filter( array_map( 'trim', explode( ',', $position ) ) );
+ foreach ( $gtids as $gtid ) {
+ if ( !self::parseGTID( $gtid ) ) {
+ throw new InvalidArgumentException( "Invalid GTID '$gtid'." );
+ }
+ $this->gtids[] = $gtid;
+ }
if ( !$this->gtids ) {
- throw new InvalidArgumentException( "GTID set should not be empty." );
+ throw new InvalidArgumentException( "Got empty GTID set." );
}
}
return $this->gtids ? null : "{$this->binlog}.{$this->pos[0]}";
}
+ /**
+ * @return string[]
+ */
+ public function getGTIDs() {
+ return $this->gtids;
+ }
+
/**
* @return string GTID set or <binlog file>/<position> (e.g db1034-bin.000976/843431247)
*/
}
/**
- * @note: this returns false for multi-source replication GTID sets
+ * @param MySQLMasterPos $pos
+ * @param MySQLMasterPos $refPos
+ * @return string[] List of GTIDs from $pos that have domains in $refPos
+ */
+ public static function getCommonDomainGTIDs( MySQLMasterPos $pos, MySQLMasterPos $refPos ) {
+ $gtidsCommon = [];
+
+ $relevantDomains = $refPos->getGtidCoordinates(); // (domain => unused)
+ foreach ( $pos->gtids as $gtid ) {
+ list( $domain ) = self::parseGTID( $gtid );
+ if ( isset( $relevantDomains[$domain] ) ) {
+ $gtidsCommon[] = $gtid;
+ }
+ }
+
+ return $gtidsCommon;
+ }
+
+ /**
* @see https://mariadb.com/kb/en/mariadb/gtid
* @see https://dev.mysql.com/doc/refman/5.6/en/replication-gtids-concepts.html
* @return array Map of (domain => integer position); possibly empty
protected function getGtidCoordinates() {
$gtidInfos = [];
foreach ( $this->gtids as $gtid ) {
- $m = [];
- // MariaDB style: <domain>-<server id>-<sequence number>
- if ( preg_match( '!^(\d+)-\d+-(\d+)$!', $gtid, $m ) ) {
- $gtidInfos[(int)$m[1]] = (int)$m[2];
- // MySQL style: <UUID domain>:<sequence number>
- } elseif ( preg_match( '!^(\w{8}-\w{4}-\w{4}-\w{4}-\w{12}):(\d+)$!', $gtid, $m ) ) {
- $gtidInfos[$m[1]] = (int)$m[2];
- } else {
- $gtidInfos = [];
- break; // unrecognized GTID
- }
-
+ list( $domain, $pos ) = self::parseGTID( $gtid );
+ $gtidInfos[$domain] = $pos;
}
return $gtidInfos;
}
+ /**
+ * @param string $gtid
+ * @return array|null [domain, integer position] or null
+ */
+ protected static function parseGTID( $gtid ) {
+ $m = [];
+ if ( preg_match( '!^(\d+)-\d+-(\d+)$!', $gtid, $m ) ) {
+ // MariaDB style: <domain>-<server id>-<sequence number>
+ return [ (int)$m[1], (int)$m[2] ];
+ } elseif ( preg_match( '!^(\w{8}-\w{4}-\w{4}-\w{4}-\w{12}):(\d+)$!', $gtid, $m ) ) {
+ // MySQL style: <UUID domain>:<sequence number>
+ return [ $m[1], (int)$m[2] ];
+ }
+
+ return null;
+ }
+
/**
* @see https://dev.mysql.com/doc/refman/5.7/en/show-master-status.html
* @see https://dev.mysql.com/doc/refman/5.7/en/show-slave-status.html
? [ 'binlog' => $this->binlog, 'pos' => $this->pos ]
: false;
}
+
+ public function serialize() {
+ return serialize( [ 'position' => $this->__toString(), 'asOfTime' => $this->asOfTime ] );
+ }
+
+ public function unserialize( $serialized ) {
+ $data = unserialize( $serialized );
+ if ( !is_array( $data ) ) {
+ throw new UnexpectedValueException( __METHOD__ . ": cannot unserialize position" );
+ }
+
+ $this->init( $data['position'], $data['asOfTime'] );
+ }
}