diff --git a/mysql/position.go b/mysql/position.go index bee5485d5..c592d6363 100644 --- a/mysql/position.go +++ b/mysql/position.go @@ -2,6 +2,8 @@ package mysql import ( "fmt" + "strconv" + "strings" ) // For binlog filename + position based replication @@ -12,22 +14,67 @@ type Position struct { func (p Position) Compare(o Position) int { // First compare binlog name - if p.Name > o.Name { + nameCmp := CompareBinlogFileName(p.Name, o.Name) + if nameCmp != 0 { + return nameCmp + } + // Same binlog file, compare position + if p.Pos > o.Pos { return 1 - } else if p.Name < o.Name { + } else if p.Pos < o.Pos { return -1 } else { - // Same binlog file, compare position - if p.Pos > o.Pos { - return 1 - } else if p.Pos < o.Pos { - return -1 - } else { - return 0 - } + return 0 } } func (p Position) String() string { return fmt.Sprintf("(%s, %d)", p.Name, p.Pos) } + +func CompareBinlogFileName(a, b string) int { + // sometimes it's convenient to construct a `Position` literal with no `Name` + if a == "" && b == "" { + return 0 + } else if a == "" { + return -1 + } else if b == "" { + return 1 + } + + splitBinlogName := func(n string) (string, int) { + // mysqld appends a numeric extension to the binary log base name to generate binary log file names + // ... + // If you supply an extension in the log name (for example, --log-bin=base_name.extension), + // the extension is silently removed and ignored. + // ref: https://dev.mysql.com/doc/refman/8.0/en/binary-log.html + i := strings.LastIndexByte(n, '.') + if i == -1 { + // try keeping backward compatibility + return n, 0 + } + + seq, err := strconv.Atoi(n[i+1:]) + if err != nil { + panic(fmt.Sprintf("binlog file %s doesn't contain numeric extension", err)) + } + return n[:i], seq + } + + aBase, aSeq := splitBinlogName(a) + bBase, bSeq := splitBinlogName(b) + + if aBase > bBase { + return 1 + } else if aBase < bBase { + return -1 + } + + if aSeq > bSeq { + return 1 + } else if aSeq < bSeq { + return -1 + } else { + return 0 + } +} diff --git a/mysql/position_test.go b/mysql/position_test.go new file mode 100644 index 000000000..aecf5d63b --- /dev/null +++ b/mysql/position_test.go @@ -0,0 +1,51 @@ +package mysql + +import ( + "github.com/pingcap/check" +) + +type positionCompareSuite struct { +} + +var _ = check.Suite(&positionCompareSuite{}) + +func (t *positionCompareSuite) TestPosCompare(c *check.C) { + ascendingPositions := []Position{ + { + "", + 4, + }, + { + "", + 100, + }, + { + "mysql-bin.000001", + 4, + }, + { + "mysql-bin.000001", + 100, + }, + { + "mysql-bin.000002", + 4, + }, + { + "mysql-bin.999999", + 4, + }, + { + "mysql-bin.1000000", + 4, + }, + } + + for i := 1; i < len(ascendingPositions); i++ { + c.Assert(ascendingPositions[i-1].Compare(ascendingPositions[i]), check.Equals, -1) + } + + for _, p := range ascendingPositions { + c.Assert(p.Compare(p), check.Equals, 0) + } +}