github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/utils/relay.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 utils 15 16 import ( 17 "bufio" 18 "encoding/binary" 19 "fmt" 20 "io" 21 "os" 22 "strconv" 23 "strings" 24 25 "github.com/go-mysql-org/go-mysql/replication" 26 "github.com/pingcap/tiflow/dm/pkg/terror" 27 ) 28 29 // not support to config yet. 30 var ( 31 UUIDIndexFilename = "server-uuid.index" 32 MetaFilename = "relay.meta" 33 uuidIndexSeparator = "." 34 ) 35 36 // ParseUUIDIndex parses UUIDIndexFilename, return a list of relay log subdirectory names and error. 37 func ParseUUIDIndex(indexPath string) ([]string, error) { 38 fd, err := os.Open(indexPath) 39 if os.IsNotExist(err) { 40 return nil, nil 41 } else if err != nil { 42 return nil, terror.ErrRelayParseUUIDIndex.Delegate(err) 43 } 44 defer fd.Close() 45 46 uuids := make([]string, 0, 5) 47 br := bufio.NewReader(fd) 48 for { 49 line, err := br.ReadString('\n') 50 if err == io.EOF { 51 if len(line) == 0 { 52 break 53 } 54 } else if err != nil { 55 return nil, terror.ErrRelayParseUUIDIndex.Delegate(err) 56 } 57 58 line = strings.TrimSpace(line) 59 if len(line) == 0 { 60 continue 61 } 62 63 uuids = append(uuids, line) 64 } 65 66 return uuids, nil 67 } 68 69 // AddSuffixForUUID adds a suffix for UUID, returns the name for relay log subdirectory. 70 func AddSuffixForUUID(uuid string, id int) string { 71 return fmt.Sprintf("%s%s%06d", uuid, uuidIndexSeparator, id) // eg. 53ea0ed1-9bf8-11e6-8bea-64006a897c73.000001 72 } 73 74 // SuffixIntToStr convert int-represented suffix to string-represented. 75 // TODO: assign RelaySubDirSuffix a type and implement Stringer. 76 func SuffixIntToStr(id int) string { 77 return fmt.Sprintf("%06d", id) 78 } 79 80 // ParseRelaySubDir parses relay log subdirectory name to (server UUID, RelaySubDirSuffix) pair. 81 func ParseRelaySubDir(uuid string) (string, int, error) { 82 parts := strings.Split(uuid, uuidIndexSeparator) 83 if len(parts) != 2 || len(parts[1]) != 6 { 84 return "", 0, terror.ErrRelayParseUUIDSuffix.Generate(uuid) 85 } 86 ID, err := strconv.Atoi(parts[1]) 87 if err != nil { 88 return "", 0, terror.ErrRelayParseUUIDSuffix.Generate(uuid) 89 } 90 return parts[0], ID, nil 91 } 92 93 // GetUUIDBySuffix gets relay log subdirectory name by matching suffix. 94 func GetUUIDBySuffix(uuids []string, suffix string) string { 95 suffix2 := fmt.Sprintf("%s%s", uuidIndexSeparator, suffix) 96 for _, uuid := range uuids { 97 if strings.HasSuffix(uuid, suffix2) { 98 return uuid 99 } 100 } 101 return "" 102 } 103 104 // GenFakeRotateEvent generates a fake ROTATE_EVENT without checksum 105 // ref: https://github.com/mysql/mysql-server/blob/4f1d7cf5fcb11a3f84cff27e37100d7295e7d5ca/sql/rpl_binlog_sender.cc#L855 106 func GenFakeRotateEvent(nextLogName string, logPos uint64, serverID uint32) (*replication.BinlogEvent, error) { 107 headerLen := replication.EventHeaderSize 108 bodyLen := 8 + len(nextLogName) 109 eventSize := headerLen + bodyLen 110 111 rawData := make([]byte, eventSize) 112 113 // header 114 binary.LittleEndian.PutUint32(rawData, 0) // timestamp 115 rawData[4] = byte(replication.ROTATE_EVENT) // event type 116 binary.LittleEndian.PutUint32(rawData[4+1:], serverID) // server ID 117 binary.LittleEndian.PutUint32(rawData[4+1+4:], uint32(eventSize)) // event size 118 binary.LittleEndian.PutUint32(rawData[4+1+4+4:], 0) // log pos, always 0 119 binary.LittleEndian.PutUint16(rawData[4+1+4+4+4:], 0x20) // flags, LOG_EVENT_ARTIFICIAL_F 120 121 // body 122 binary.LittleEndian.PutUint64(rawData[headerLen:], logPos) 123 copy(rawData[headerLen+8:], nextLogName) 124 125 // decode header 126 h := &replication.EventHeader{} 127 err := h.Decode(rawData) 128 if err != nil { 129 return nil, terror.ErrRelayGenFakeRotateEvent.Delegate(err) 130 } 131 132 // decode body 133 e := &replication.RotateEvent{} 134 err = e.Decode(rawData[headerLen:]) 135 if err != nil { 136 return nil, terror.ErrRelayGenFakeRotateEvent.Delegate(err) 137 } 138 139 return &replication.BinlogEvent{RawData: rawData, Header: h, Event: e}, nil 140 }