vitess.io/vitess@v0.16.2/go/vt/vtorc/inst/binlog.go (about) 1 /* 2 Copyright 2015 Shlomi Noach, courtesy Booking.com 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package inst 18 19 import ( 20 "errors" 21 "fmt" 22 "regexp" 23 "strconv" 24 "strings" 25 ) 26 27 var detachPattern *regexp.Regexp 28 29 func init() { 30 detachPattern, _ = regexp.Compile(`//([^/:]+):([\d]+)`) // e.g. `//binlog.01234:567890` 31 } 32 33 type BinlogType int 34 35 const ( 36 BinaryLog BinlogType = iota 37 RelayLog 38 ) 39 40 // BinlogCoordinates described binary log coordinates in the form of log file & log position. 41 type BinlogCoordinates struct { 42 LogFile string 43 LogPos int64 44 Type BinlogType 45 } 46 47 // ParseInstanceKey will parse an InstanceKey from a string representation such as 127.0.0.1:3306 48 func ParseBinlogCoordinates(logFileLogPos string) (*BinlogCoordinates, error) { 49 tokens := strings.SplitN(logFileLogPos, ":", 2) 50 if len(tokens) != 2 { 51 return nil, fmt.Errorf("ParseBinlogCoordinates: Cannot parse BinlogCoordinates from %s. Expected format is file:pos", logFileLogPos) 52 } 53 54 logPos, err := strconv.ParseInt(tokens[1], 10, 0) 55 if err != nil { 56 return nil, fmt.Errorf("ParseBinlogCoordinates: invalid pos: %s", tokens[1]) 57 } 58 return &BinlogCoordinates{LogFile: tokens[0], LogPos: logPos}, nil 59 } 60 61 // DisplayString returns a user-friendly string representation of these coordinates 62 func (binlogCoordinates *BinlogCoordinates) DisplayString() string { 63 return fmt.Sprintf("%s:%d", binlogCoordinates.LogFile, binlogCoordinates.LogPos) 64 } 65 66 // String returns a user-friendly string representation of these coordinates 67 func (binlogCoordinates BinlogCoordinates) String() string { 68 return binlogCoordinates.DisplayString() 69 } 70 71 // Equals tests equality of this corrdinate and another one. 72 func (binlogCoordinates *BinlogCoordinates) Equals(other *BinlogCoordinates) bool { 73 if other == nil { 74 return false 75 } 76 return binlogCoordinates.LogFile == other.LogFile && binlogCoordinates.LogPos == other.LogPos && binlogCoordinates.Type == other.Type 77 } 78 79 // IsEmpty returns true if the log file is empty, unnamed 80 func (binlogCoordinates *BinlogCoordinates) IsEmpty() bool { 81 return binlogCoordinates.LogFile == "" 82 } 83 84 // SmallerThan returns true if this coordinate is strictly smaller than the other. 85 func (binlogCoordinates *BinlogCoordinates) SmallerThan(other *BinlogCoordinates) bool { 86 if binlogCoordinates.LogFile < other.LogFile { 87 return true 88 } 89 if binlogCoordinates.LogFile == other.LogFile && binlogCoordinates.LogPos < other.LogPos { 90 return true 91 } 92 return false 93 } 94 95 // SmallerThanOrEquals returns true if this coordinate is the same or equal to the other one. 96 // We do NOT compare the type so we can not use this.Equals() 97 func (binlogCoordinates *BinlogCoordinates) SmallerThanOrEquals(other *BinlogCoordinates) bool { 98 if binlogCoordinates.SmallerThan(other) { 99 return true 100 } 101 return binlogCoordinates.LogFile == other.LogFile && binlogCoordinates.LogPos == other.LogPos // No Type comparison 102 } 103 104 // FileSmallerThan returns true if this coordinate's file is strictly smaller than the other's. 105 func (binlogCoordinates *BinlogCoordinates) FileSmallerThan(other *BinlogCoordinates) bool { 106 return binlogCoordinates.LogFile < other.LogFile 107 } 108 109 // FileNumberDistance returns the numeric distance between this corrdinate's file number and the other's. 110 // Effectively it means "how many roatets/FLUSHes would make these coordinates's file reach the other's" 111 func (binlogCoordinates *BinlogCoordinates) FileNumberDistance(other *BinlogCoordinates) int { 112 thisNumber, _ := binlogCoordinates.FileNumber() 113 otherNumber, _ := other.FileNumber() 114 return otherNumber - thisNumber 115 } 116 117 // FileNumber returns the numeric value of the file, and the length in characters representing the number in the filename. 118 // Example: FileNumber() of mysqld.log.000789 is (789, 6) 119 func (binlogCoordinates *BinlogCoordinates) FileNumber() (int, int) { 120 tokens := strings.Split(binlogCoordinates.LogFile, ".") 121 numPart := tokens[len(tokens)-1] 122 numLen := len(numPart) 123 fileNum, err := strconv.Atoi(numPart) 124 if err != nil { 125 return 0, 0 126 } 127 return fileNum, numLen 128 } 129 130 // PreviousFileCoordinatesBy guesses the filename of the previous binlog/relaylog, by given offset (number of files back) 131 func (binlogCoordinates *BinlogCoordinates) PreviousFileCoordinatesBy(offset int) (BinlogCoordinates, error) { 132 result := BinlogCoordinates{LogPos: 0, Type: binlogCoordinates.Type} 133 134 fileNum, numLen := binlogCoordinates.FileNumber() 135 if fileNum == 0 { 136 return result, errors.New("Log file number is zero, cannot detect previous file") 137 } 138 newNumStr := fmt.Sprintf("%d", (fileNum - offset)) 139 newNumStr = strings.Repeat("0", numLen-len(newNumStr)) + newNumStr 140 141 tokens := strings.Split(binlogCoordinates.LogFile, ".") 142 tokens[len(tokens)-1] = newNumStr 143 result.LogFile = strings.Join(tokens, ".") 144 return result, nil 145 } 146 147 // PreviousFileCoordinates guesses the filename of the previous binlog/relaylog 148 func (binlogCoordinates *BinlogCoordinates) PreviousFileCoordinates() (BinlogCoordinates, error) { 149 return binlogCoordinates.PreviousFileCoordinatesBy(1) 150 } 151 152 // PreviousFileCoordinates guesses the filename of the previous binlog/relaylog 153 func (binlogCoordinates *BinlogCoordinates) NextFileCoordinates() (BinlogCoordinates, error) { 154 result := BinlogCoordinates{LogPos: 0, Type: binlogCoordinates.Type} 155 156 fileNum, numLen := binlogCoordinates.FileNumber() 157 newNumStr := fmt.Sprintf("%d", (fileNum + 1)) 158 newNumStr = strings.Repeat("0", numLen-len(newNumStr)) + newNumStr 159 160 tokens := strings.Split(binlogCoordinates.LogFile, ".") 161 tokens[len(tokens)-1] = newNumStr 162 result.LogFile = strings.Join(tokens, ".") 163 return result, nil 164 } 165 166 // Detach returns a detahced form of coordinates 167 func (binlogCoordinates *BinlogCoordinates) Detach() (detachedCoordinates BinlogCoordinates) { 168 detachedCoordinates = BinlogCoordinates{LogFile: fmt.Sprintf("//%s:%d", binlogCoordinates.LogFile, binlogCoordinates.LogPos), LogPos: binlogCoordinates.LogPos} 169 return detachedCoordinates 170 } 171 172 // FileSmallerThan returns true if this coordinate's file is strictly smaller than the other's. 173 func (binlogCoordinates *BinlogCoordinates) ExtractDetachedCoordinates() (isDetached bool, detachedCoordinates BinlogCoordinates) { 174 detachedCoordinatesSubmatch := detachPattern.FindStringSubmatch(binlogCoordinates.LogFile) 175 if len(detachedCoordinatesSubmatch) == 0 { 176 return false, *binlogCoordinates 177 } 178 detachedCoordinates.LogFile = detachedCoordinatesSubmatch[1] 179 detachedCoordinates.LogPos, _ = strconv.ParseInt(detachedCoordinatesSubmatch[2], 10, 0) 180 return true, detachedCoordinates 181 }