github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/relay/file.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 relay
    15  
    16  import (
    17  	"os"
    18  	"path/filepath"
    19  
    20  	"github.com/pingcap/tiflow/dm/pkg/binlog"
    21  	"github.com/pingcap/tiflow/dm/pkg/log"
    22  	"github.com/pingcap/tiflow/dm/pkg/terror"
    23  	"github.com/pingcap/tiflow/dm/pkg/utils"
    24  	"go.uber.org/zap"
    25  )
    26  
    27  // FileCmp is a compare condition used when collecting binlog files.
    28  type FileCmp uint8
    29  
    30  // FileCmpLess represents a < FileCmp condition, others are similar.
    31  const (
    32  	FileCmpLess FileCmp = iota + 1
    33  	FileCmpLessEqual
    34  	FileCmpEqual
    35  	FileCmpBiggerEqual
    36  	FileCmpBigger
    37  )
    38  
    39  // EventNotifier notifies whether there is new binlog event written to the file.
    40  type EventNotifier interface {
    41  	// Notified returns a channel used to check whether there is new binlog event written to the file
    42  	Notified() chan interface{}
    43  }
    44  
    45  // CollectAllBinlogFiles collects all valid binlog files in dir, and returns filenames in binlog ascending order.
    46  func CollectAllBinlogFiles(dir string) ([]string, error) {
    47  	if dir == "" {
    48  		return nil, terror.ErrEmptyRelayDir.Generate()
    49  	}
    50  	return binlog.ReadSortedBinlogFromDir(dir)
    51  }
    52  
    53  // CollectBinlogFilesCmp collects valid binlog files with a compare condition.
    54  func CollectBinlogFilesCmp(dir, baseFile string, cmp FileCmp) ([]string, error) {
    55  	if dir == "" {
    56  		return nil, terror.ErrEmptyRelayDir.Generate()
    57  	}
    58  
    59  	if bp := filepath.Join(dir, baseFile); !utils.IsFileExists(bp) {
    60  		return nil, terror.ErrBaseFileNotFound.Generate(baseFile, dir)
    61  	}
    62  
    63  	bf, err := utils.ParseFilename(baseFile)
    64  	if err != nil {
    65  		return nil, terror.Annotatef(err, "filename %s", baseFile)
    66  	}
    67  
    68  	allFiles, err := CollectAllBinlogFiles(dir)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	results := make([]string, 0, len(allFiles))
    74  	for _, f := range allFiles {
    75  		// we have parse f in `CollectAllBinlogFiles`, may be we can refine this
    76  		parsed, err := utils.ParseFilename(f)
    77  		if err != nil || parsed.BaseName != bf.BaseName {
    78  			log.L().Warn("collecting binlog file, ignore invalid file", zap.String("file", f), log.ShortError(err))
    79  			continue
    80  		}
    81  		switch cmp {
    82  		case FileCmpBigger:
    83  			if !parsed.GreaterThan(bf) {
    84  				log.L().Debug("ignore older or equal binlog file", zap.String("file", f), zap.String("directory", dir))
    85  				continue
    86  			}
    87  		case FileCmpBiggerEqual:
    88  			if !parsed.GreaterThanOrEqualTo(bf) {
    89  				log.L().Debug("ignore older binlog file", zap.String("file", f), zap.String("directory", dir))
    90  				continue
    91  			}
    92  		case FileCmpLess:
    93  			if !parsed.LessThan(bf) {
    94  				log.L().Debug("ignore newer or equal binlog file", zap.String("file", f), zap.String("directory", dir))
    95  				continue
    96  			}
    97  		default:
    98  			return nil, terror.ErrBinFileCmpCondNotSupport.Generate(cmp)
    99  		}
   100  
   101  		results = append(results, f)
   102  	}
   103  
   104  	return results, nil
   105  }
   106  
   107  // getFirstBinlogName gets the first binlog file in relay sub directory.
   108  func getFirstBinlogName(baseDir, uuid string) (string, error) {
   109  	subDir := filepath.Join(baseDir, uuid)
   110  	files, err := binlog.ReadSortedBinlogFromDir(subDir)
   111  	if err != nil {
   112  		return "", terror.Annotatef(err, "get binlog file for dir %s", subDir)
   113  	}
   114  
   115  	if len(files) == 0 {
   116  		return "", terror.ErrBinlogFilesNotFound.Generate(subDir)
   117  	}
   118  	return files[0], nil
   119  }
   120  
   121  // fileSizeUpdated checks whether the file's size has updated
   122  // return
   123  //
   124  //	 0: not updated
   125  //	 1: update to larger
   126  //	-1: update to smaller, only happens in special case, for example we change
   127  //	    relay.meta manually and start task before relay log catches up.
   128  func fileSizeUpdated(path string, latestSize int64) (int, error) {
   129  	fi, err := os.Stat(path)
   130  	if err != nil {
   131  		return 0, terror.ErrGetRelayLogStat.Delegate(err, path)
   132  	}
   133  	curSize := fi.Size()
   134  	switch {
   135  	case curSize == latestSize:
   136  		return 0, nil
   137  	case curSize > latestSize:
   138  		log.L().Debug("size of relay log file has been changed", zap.String("file", path),
   139  			zap.Int64("old size", latestSize), zap.Int64("size", curSize))
   140  		return 1, nil
   141  	default:
   142  		log.L().Error("size of relay log file has been changed", zap.String("file", path),
   143  			zap.Int64("old size", latestSize), zap.Int64("size", curSize))
   144  		return -1, nil
   145  	}
   146  }