github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/utils/filename.go (about) 1 // Copyright 2022 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 "fmt" 18 "strconv" 19 "strings" 20 21 "github.com/pingcap/tiflow/dm/pkg/log" 22 "github.com/pingcap/tiflow/dm/pkg/terror" 23 "go.uber.org/zap" 24 ) 25 26 const ( 27 // the binlog file name format is `base + '.' + seq`. 28 binlogFilenameSep = "." 29 // PosRelaySubDirSuffixSeparator is used to differ binlog position from multiple 30 // (switched) masters, we added a suffix which comes from relay log subdirectory 31 // into binlogPos.Name. And we also need support position with RelaySubDirSuffix 32 // should always > position without RelaySubDirSuffix, so we can continue from 33 // latter to former automatically. convertedPos.BinlogName = 34 // originalPos.BinlogBaseName + PosRelaySubDirSuffixSeparator + RelaySubDirSuffix + binlogFilenameSep + originalPos.BinlogSeq 35 // eg. mysql-bin.000003 under folder c6ae5afe-c7a3-11e8-a19d-0242ac130006.000002 => mysql-bin|000002.000003 36 // when new relay log subdirectory is created, RelaySubDirSuffix should increase. 37 PosRelaySubDirSuffixSeparator = "|" 38 ) 39 40 // Filename represents a binlog filename. 41 type Filename struct { 42 BaseName string 43 Seq string 44 SeqInt64 int64 45 } 46 47 // LessThan checks whether this filename < other filename. 48 func (f Filename) LessThan(other Filename) bool { 49 return f.BaseName == other.BaseName && f.SeqInt64 < other.SeqInt64 50 } 51 52 // GreaterThanOrEqualTo checks whether this filename >= other filename. 53 func (f Filename) GreaterThanOrEqualTo(other Filename) bool { 54 return f.BaseName == other.BaseName && f.SeqInt64 >= other.SeqInt64 55 } 56 57 // GreaterThan checks whether this filename > other filename. 58 func (f Filename) GreaterThan(other Filename) bool { 59 return f.BaseName == other.BaseName && f.SeqInt64 > other.SeqInt64 60 } 61 62 // ParseFilename parses a string representation binlog filename into a `Filename`. 63 func ParseFilename(filename string) (Filename, error) { 64 var fn Filename 65 parts := strings.Split(filename, binlogFilenameSep) 66 if len(parts) != 2 { 67 return fn, terror.Annotatef(terror.ErrBinlogInvalidFilename.Generate(), "filename %s", filename) 68 } 69 70 var ( 71 seqInt64 int64 72 err error 73 ) 74 if seqInt64, err = strconv.ParseInt(parts[1], 10, 64); err != nil || seqInt64 <= 0 { 75 return fn, terror.Annotatef(terror.ErrBinlogInvalidFilename.Generate(), "filename %s", filename) 76 } 77 fn.BaseName = parts[0] 78 fn.Seq = parts[1] 79 fn.SeqInt64 = seqInt64 80 return fn, nil 81 } 82 83 // VerifyFilename verifies whether is a valid MySQL/MariaDB binlog filename. 84 // valid format is `base + '.' + seq`. 85 func VerifyFilename(filename string) bool { 86 if _, err := ParseFilename(filename); err != nil { 87 return false 88 } 89 return true 90 } 91 92 // GetFilenameIndex returns a int64 index value (seq number) of the filename. 93 func GetFilenameIndex(filename string) (int64, error) { 94 fn, err := ParseFilename(filename) 95 if err != nil { 96 return 0, err 97 } 98 return fn.SeqInt64, nil 99 } 100 101 // ConstructFilename constructs a binlog filename from the basename and seq. 102 func ConstructFilename(baseName, seq string) string { 103 return fmt.Sprintf("%s%s%s", baseName, binlogFilenameSep, seq) 104 } 105 106 // ConstructFilenameWithUUIDSuffix constructs a binlog filename with UUID suffix. 107 func ConstructFilenameWithUUIDSuffix(originalName Filename, uuidSuffix string) string { 108 return fmt.Sprintf("%s%s%s%s%s", originalName.BaseName, PosRelaySubDirSuffixSeparator, uuidSuffix, binlogFilenameSep, originalName.Seq) 109 } 110 111 // SplitFilenameWithUUIDSuffix analyzes a binlog filename with UUID suffix. 112 func SplitFilenameWithUUIDSuffix(filename string) (baseName, uuidSuffix, seq string, err error) { 113 items1 := strings.Split(filename, PosRelaySubDirSuffixSeparator) 114 if len(items1) != 2 { 115 return "", "", "", terror.ErrBinlogInvalidFilenameWithUUIDSuffix.Generate(filename) 116 } 117 118 baseName = items1[0] 119 items2 := strings.Split(items1[1], binlogFilenameSep) 120 121 if len(items2) != 2 { 122 return "", "", "", terror.ErrBinlogInvalidFilenameWithUUIDSuffix.Generate(filename) 123 } 124 uuidSuffix = items2[0] 125 seq = items2[1] 126 return baseName, uuidSuffix, seq, nil 127 } 128 129 // ExtractRealName removes relay log uuid suffix if it exists and returns real binlog name. 130 func ExtractRealName(name string) string { 131 if !strings.Contains(name, PosRelaySubDirSuffixSeparator) { 132 return name 133 } 134 baseName, _, seq, err := SplitFilenameWithUUIDSuffix(name) 135 if err != nil { 136 log.L().Error("failed to split binlog name with uuid suffix", zap.String("name", name), zap.Error(err)) 137 // nolint:nilerr 138 return name 139 } 140 return ConstructFilename(baseName, seq) 141 }