github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/binlog/event/generator.go (about)

     1  // Copyright 2019 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package event
    15  
    16  import (
    17  	"fmt"
    18  	"time"
    19  
    20  	"github.com/coreos/go-semver/semver"
    21  	gmysql "github.com/go-mysql-org/go-mysql/mysql"
    22  	"github.com/go-mysql-org/go-mysql/replication"
    23  	"github.com/pingcap/tiflow/dm/pkg/gtid"
    24  	"github.com/pingcap/tiflow/dm/pkg/terror"
    25  )
    26  
    27  // Generator represents a binlog events generator.
    28  type Generator struct {
    29  	Flavor        string
    30  	ServerID      uint32
    31  	LatestPos     uint32
    32  	LatestGTID    gmysql.GTIDSet
    33  	ExecutedGTIDs gmysql.GTIDSet
    34  	LatestXID     uint64
    35  
    36  	GenGTID       bool
    37  	AnonymousGTID bool
    38  }
    39  
    40  // NewGenerator creates a new instance of Generator.
    41  func NewGenerator(flavor string, serverID uint32, latestPos uint32, latestGTID gmysql.GTIDSet, previousGTIDs gmysql.GTIDSet, latestXID uint64) (*Generator, error) {
    42  	return newGenerator(flavor, "5.7.0", serverID, latestPos, latestGTID, previousGTIDs, latestXID, true)
    43  }
    44  
    45  func NewGeneratorV2(flavor, version, latestGTIDStr string, enableGTID bool) (*Generator, error) {
    46  	latestGTID, _ := gtid.ParserGTID(flavor, latestGTIDStr)
    47  	previousGTIDSet, _ := gtid.ParserGTID(flavor, latestGTIDStr)
    48  	return newGenerator(flavor, version, 1, 0, latestGTID, previousGTIDSet, 0, enableGTID)
    49  }
    50  
    51  func newGenerator(flavor, version string, serverID uint32, latestPos uint32, latestGTID gmysql.GTIDSet, previousGTIDs gmysql.GTIDSet, latestXID uint64, genGTID bool) (*Generator, error) {
    52  	singleGTID, err := verifySingleGTID(flavor, latestGTID)
    53  	if err != nil {
    54  		return nil, terror.Annotate(err, "verify single latest GTID in set")
    55  	}
    56  	var anonymousGTID bool
    57  	switch flavor {
    58  	case gmysql.MySQLFlavor:
    59  		uuidSet := singleGTID.(*gmysql.UUIDSet)
    60  		prevGSet, ok := previousGTIDs.(*gmysql.MysqlGTIDSet)
    61  		if !ok || prevGSet == nil {
    62  			return nil, terror.ErrBinlogGTIDMySQLNotValid.Generate(previousGTIDs)
    63  		}
    64  		// latestGTID should be one of the latest previousGTIDs
    65  		prevGTID, ok := prevGSet.Sets[uuidSet.SID.String()]
    66  		if !ok || prevGTID.Intervals.Len() != 1 || prevGTID.Intervals[0].Stop != uuidSet.Intervals[0].Stop {
    67  			return nil, terror.ErrBinlogLatestGTIDNotInPrev.Generate(latestGTID, previousGTIDs)
    68  		}
    69  
    70  		ver, err := semver.NewVersion(version)
    71  		if err != nil {
    72  			return nil, err
    73  		}
    74  		if ver.Compare(*semver.New("5.7.0")) >= 0 && !genGTID {
    75  			// 5.7+ add anonymous GTID when GTID is disabled
    76  			genGTID = true
    77  			anonymousGTID = true
    78  		}
    79  	case gmysql.MariaDBFlavor:
    80  		mariaGTID := singleGTID.(*gmysql.MariadbGTID)
    81  		if mariaGTID.ServerID != serverID {
    82  			return nil, terror.ErrBinlogMariaDBServerIDMismatch.Generate(mariaGTID.ServerID, serverID)
    83  		}
    84  		// latestGTID should be one of previousGTIDs
    85  		prevGSet, ok := previousGTIDs.(*gmysql.MariadbGTIDSet)
    86  		if !ok || prevGSet == nil {
    87  			return nil, terror.ErrBinlogGTIDMariaDBNotValid.Generate(previousGTIDs)
    88  		}
    89  		set, ok := prevGSet.Sets[mariaGTID.DomainID]
    90  		if !ok {
    91  			return nil, terror.ErrBinlogLatestGTIDNotInPrev.Generate(latestGTID, previousGTIDs)
    92  		}
    93  		prevGTID, ok := set[mariaGTID.ServerID]
    94  		if !ok || prevGTID.ServerID != mariaGTID.ServerID || prevGTID.SequenceNumber != mariaGTID.SequenceNumber {
    95  			return nil, terror.ErrBinlogLatestGTIDNotInPrev.Generate(latestGTID, previousGTIDs)
    96  		}
    97  		// MariaDB 10.0.2+ always contains GTID
    98  		genGTID = true
    99  	default:
   100  		return nil, terror.ErrBinlogFlavorNotSupport.Generate(flavor)
   101  	}
   102  
   103  	return &Generator{
   104  		Flavor:        flavor,
   105  		ServerID:      serverID,
   106  		LatestPos:     latestPos,
   107  		LatestGTID:    latestGTID,
   108  		ExecutedGTIDs: previousGTIDs.Clone(),
   109  		LatestXID:     latestXID,
   110  		GenGTID:       genGTID,
   111  		AnonymousGTID: anonymousGTID,
   112  	}, nil
   113  }
   114  
   115  // GenFileHeader generates a binlog file header, including to PreviousGTIDsEvent/MariadbGTIDListEvent.
   116  // for MySQL:
   117  //  1. BinLogFileHeader, [ fe `bin` ]
   118  //  2. FormatDescriptionEvent
   119  //  3. PreviousGTIDsEvent
   120  //
   121  // for MariaDB:
   122  //  1. BinLogFileHeader, [ fe `bin` ]
   123  //  2. FormatDescriptionEvent
   124  //  3. MariadbGTIDListEvent
   125  func (g *Generator) GenFileHeader(ts int64) ([]*replication.BinlogEvent, []byte, error) {
   126  	events, data, err := GenCommonFileHeader(g.Flavor, g.ServerID, g.ExecutedGTIDs, g.GenGTID, ts)
   127  	if err != nil {
   128  		return nil, nil, err
   129  	}
   130  	g.LatestPos = uint32(len(data)) // if generate a binlog file header then reset latest pos
   131  	return events, data, nil
   132  }
   133  
   134  // GenCreateDatabaseEvents generates binlog events for `CREATE DATABASE`.
   135  // events: [GTIDEvent, QueryEvent]
   136  func (g *Generator) GenCreateDatabaseEvents(schema string) ([]*replication.BinlogEvent, []byte, error) {
   137  	query := fmt.Sprintf("CREATE DATABASE `%s`", schema)
   138  	result, err := GenDDLEvents(g.Flavor, g.ServerID, g.LatestPos, g.LatestGTID, schema, query, g.GenGTID, g.AnonymousGTID, 0)
   139  	if err != nil {
   140  		return nil, nil, err
   141  	}
   142  	g.updateLatestPosGTID(result.LatestPos, result.LatestGTID)
   143  	return result.Events, result.Data, nil
   144  }
   145  
   146  // GenDropDatabaseEvents generates binlog events for `DROP DATABASE`.
   147  // events: [GTIDEvent, QueryEvent]
   148  func (g *Generator) GenDropDatabaseEvents(schema string) ([]*replication.BinlogEvent, []byte, error) {
   149  	query := fmt.Sprintf("DROP DATABASE `%s`", schema)
   150  	result, err := GenDDLEvents(g.Flavor, g.ServerID, g.LatestPos, g.LatestGTID, schema, query, g.GenGTID, g.AnonymousGTID, 0)
   151  	if err != nil {
   152  		return nil, nil, err
   153  	}
   154  	g.updateLatestPosGTID(result.LatestPos, result.LatestGTID)
   155  	return result.Events, result.Data, nil
   156  }
   157  
   158  // GenCreateTableEvents generates binlog events for `CREATE TABLE`.
   159  // events: [GTIDEvent, QueryEvent]
   160  func (g *Generator) GenCreateTableEvents(schema string, query string) ([]*replication.BinlogEvent, []byte, error) {
   161  	result, err := GenDDLEvents(g.Flavor, g.ServerID, g.LatestPos, g.LatestGTID, schema, query, g.GenGTID, g.AnonymousGTID, 0)
   162  	if err != nil {
   163  		return nil, nil, err
   164  	}
   165  	g.updateLatestPosGTID(result.LatestPos, result.LatestGTID)
   166  	return result.Events, result.Data, nil
   167  }
   168  
   169  // GenDropTableEvents generates binlog events for `DROP TABLE`.
   170  // events: [GTIDEvent, QueryEvent]
   171  func (g *Generator) GenDropTableEvents(schema string, table string) ([]*replication.BinlogEvent, []byte, error) {
   172  	query := fmt.Sprintf("DROP TABLE `%s`.`%s`", schema, table)
   173  	result, err := GenDDLEvents(g.Flavor, g.ServerID, g.LatestPos, g.LatestGTID, schema, query, g.GenGTID, g.AnonymousGTID, 0)
   174  	if err != nil {
   175  		return nil, nil, err
   176  	}
   177  	g.updateLatestPosGTID(result.LatestPos, result.LatestGTID)
   178  	return result.Events, result.Data, nil
   179  }
   180  
   181  // GenDDLEvents generates binlog events for DDL statements.
   182  // events: [GTIDEvent, QueryEvent]
   183  func (g *Generator) GenDDLEvents(schema string, query string, ts int64) ([]*replication.BinlogEvent, []byte, error) {
   184  	result, err := GenDDLEvents(g.Flavor, g.ServerID, g.LatestPos, g.LatestGTID, schema, query, g.GenGTID, g.AnonymousGTID, ts)
   185  	if err != nil {
   186  		return nil, nil, err
   187  	}
   188  	g.updateLatestPosGTID(result.LatestPos, result.LatestGTID)
   189  	return result.Events, result.Data, nil
   190  }
   191  
   192  // GenDMLEvents generates binlog events for `INSERT`/`UPDATE`/`DELETE`.
   193  // events: [GTIDEvent, QueryEvent, TableMapEvent, RowsEvent, ..., XIDEvent]
   194  // NOTE: multi <TableMapEvent, RowsEvent> pairs can be in events.
   195  func (g *Generator) GenDMLEvents(eventType replication.EventType, dmlData []*DMLData, ts int64) ([]*replication.BinlogEvent, []byte, error) {
   196  	result, err := GenDMLEvents(g.Flavor, g.ServerID, g.LatestPos, g.LatestGTID, eventType, g.LatestXID+1, dmlData, g.GenGTID, g.AnonymousGTID, ts)
   197  	if err != nil {
   198  		return nil, nil, err
   199  	}
   200  	g.updateLatestPosGTID(result.LatestPos, result.LatestGTID)
   201  	g.LatestXID++ // increase XID
   202  	return result.Events, result.Data, nil
   203  }
   204  
   205  func (g *Generator) Rotate(nextName string, ts int64) (*replication.BinlogEvent, []byte, error) {
   206  	if ts == 0 {
   207  		ts = time.Now().Unix()
   208  	}
   209  	header := &replication.EventHeader{
   210  		Timestamp: uint32(ts),
   211  		ServerID:  11,
   212  		Flags:     0x01,
   213  	}
   214  	ev, err := GenRotateEvent(header, g.LatestPos, []byte(nextName), 4)
   215  	if err != nil {
   216  		return nil, nil, err
   217  	}
   218  	g.updateLatestPosGTID(4, nil)
   219  	return ev, ev.RawData, nil
   220  }
   221  
   222  func (g *Generator) updateLatestPosGTID(latestPos uint32, latestGTID gmysql.GTIDSet) {
   223  	g.LatestPos = latestPos
   224  	if latestGTID != nil {
   225  		g.LatestGTID = latestGTID
   226  		_ = g.ExecutedGTIDs.Update(latestGTID.String())
   227  	}
   228  }