rdbms: make DatabaseMysql::masterPosWait() handle inactive GTIDs
[lhc/web/wiklou.git] / includes / libs / rdbms / database / position / MySQLMasterPos.php
index 62809cd..2ee9068 100644 (file)
@@ -32,9 +32,15 @@ class MySQLMasterPos implements DBMasterPos {
                        $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 ( !$this->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." );
                        }
                }
 
@@ -109,6 +115,13 @@ class MySQLMasterPos implements DBMasterPos {
                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)
         */
@@ -119,7 +132,25 @@ class MySQLMasterPos implements DBMasterPos {
        }
 
        /**
-        * @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
@@ -127,23 +158,30 @@ class MySQLMasterPos implements DBMasterPos {
        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