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  }