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  }