github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/debug-tools/binlog-event-blackhole/fetcher.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 main 15 16 import ( 17 "context" 18 "time" 19 20 "github.com/go-mysql-org/go-mysql/client" 21 "github.com/pingcap/errors" 22 "github.com/pingcap/tiflow/dm/pkg/log" 23 "go.uber.org/atomic" 24 "go.uber.org/zap" 25 ) 26 27 // registerSlave register a slave connection on the master. 28 func registerSlave(addr, username, password string, serverID uint32) (*client.Conn, error) { 29 conn, err := client.Connect(addr, username, password, "", func(c *client.Conn) { 30 }) 31 if err != nil { 32 return nil, errors.Annotate(err, "connect to the master") 33 } 34 35 // takes as an indication that the client is checksum-aware. 36 // ref https://dev.mysql.com/doc/refman/8.0/en/c-api-binary-log-functions.html. 37 _, err = conn.Execute(`SET @master_binlog_checksum='NONE'`) 38 if err != nil { 39 return nil, errors.Annotate(err, `SET @master_binlog_checksum='NONE'`) 40 } 41 42 conn.ResetSequence() 43 packet := registerSlaveCommand(username, password, serverID) 44 err = conn.WritePacket(packet) 45 if err != nil { 46 return nil, errors.Annotatef(err, "write COM_REGISTER_SLAVE packet %v", packet) 47 } 48 49 _, err = conn.ReadOKPacket() 50 if err != nil { 51 return nil, errors.Annotate(err, "read OK packet") 52 } 53 54 return conn, nil 55 } 56 57 // startSync starts to sync binlog event from <name, pos>. 58 func startSync(conn *client.Conn, serverID uint32, name string, pos uint32) error { 59 conn.ResetSequence() 60 packet := dumpCommand(serverID, name, pos) 61 err := conn.WritePacket(packet) 62 return errors.Annotatef(err, "write COM_BINLOG_DUMP %v", packet) 63 } 64 65 // closeConn closes the connection to the master server. 66 func closeConn(conn *client.Conn) error { 67 deadline := time.Now().Add(time.Millisecond) 68 err := conn.SetReadDeadline(deadline) 69 if err != nil { 70 return errors.Annotatef(err, "set connection read deadline to %v", deadline) 71 } 72 73 return errors.Trace(conn.Close()) 74 } 75 76 // readEventsWithGoMySQL reads binlog events from the master server with `go-mysql` pkg. 77 func readEventsWithGoMySQL(ctx context.Context, conn *client.Conn) (uint64, uint64, time.Duration, error) { 78 var ( 79 eventCount atomic.Uint64 80 byteCount atomic.Uint64 81 startTime = time.Now() 82 ) 83 for { 84 select { 85 case <-ctx.Done(): 86 return eventCount.Load(), byteCount.Load(), time.Since(startTime), nil 87 default: 88 } 89 90 data, err := conn.ReadPacket() 91 if err != nil { 92 return eventCount.Load(), byteCount.Load(), time.Since(startTime), errors.Annotate(err, "read event packet") 93 } 94 95 switch data[0] { 96 case 0x00: // OK_HEADER 97 eventCount.Add(1) // got one more event 98 byteCount.Add(4 + uint64(len(data))) // with 4 bytes packet header 99 continue 100 case 0xff: // ERR_HEADER 101 return eventCount.Load(), byteCount.Load(), time.Since(startTime), errors.New("read event fail with 0xFF header") 102 case 0xfe: // EOF_HEADER 103 // Refer http://dev.mysql.com/doc/internals/en/packet-EOF_Packet.html 104 log.L().Warn("receive EOF packet, retrying") 105 continue 106 default: 107 log.L().Warn("invalid stream header, retrying", zap.Uint8("header", data[0])) 108 continue 109 } 110 } 111 } 112 113 // readEventsWithoutGoMySQL reads binlog events from master server without `go-mysql` pkg. 114 func readEventsWithoutGoMySQL(ctx context.Context, conn *client.Conn) (uint64, uint64, time.Duration, error) { 115 var ( 116 eventCount atomic.Uint64 117 byteCount atomic.Uint64 118 startTime = time.Now() 119 ) 120 for { 121 select { 122 case <-ctx.Done(): 123 return eventCount.Load(), byteCount.Load(), time.Since(startTime), nil 124 default: 125 } 126 127 _, data, err := readPacket(conn) 128 if err != nil { 129 return eventCount.Load(), byteCount.Load(), time.Since(startTime), errors.Annotate(err, "read event packet") 130 } 131 132 switch data[0] { 133 case 0x00: // OK_HEADER 134 eventCount.Add(1) // got one more event 135 byteCount.Add(4 + uint64(len(data))) // with 4 bytes packet header 136 continue 137 case 0xff: // ERR_HEADER 138 return eventCount.Load(), byteCount.Load(), time.Since(startTime), errors.New("read event fail with 0xFF header") 139 case 0xfe: // EOF_HEADER 140 // Refer http://dev.mysql.com/doc/internals/en/packet-EOF_Packet.html 141 log.L().Warn("receive EOF packet, retrying") 142 continue 143 default: 144 log.L().Warn("invalid stream header, retrying", zap.Uint8("header", data[0])) 145 continue 146 } 147 } 148 } 149 150 // readDataOnly reads the binary data only and does not parse packet or binlog event. 151 func readDataOnly(ctx context.Context, conn *client.Conn) (uint64, time.Duration, error) { 152 var ( 153 buf = make([]byte, 10240) 154 byteCount atomic.Uint64 155 startTime = time.Now() 156 ) 157 for { 158 select { 159 case <-ctx.Done(): 160 return byteCount.Load(), time.Since(startTime), nil 161 default: 162 } 163 164 n, err := conn.Conn.Conn.Read(buf) 165 if err != nil { 166 return byteCount.Load(), time.Since(startTime), errors.Annotatef(err, "read binary data") 167 } 168 byteCount.Add(uint64(n)) 169 } 170 }