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 }