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  }