From d8824e1b8d042ae423cbe07747ecc6e66f7212df Mon Sep 17 00:00:00 2001 From: Nikolai Antonov Date: Thu, 30 Dec 2021 16:15:13 +0300 Subject: [PATCH 1/2] Add MysqlGTIDSet.Add() and Minus() methods, so it will help users to estimate GTID sets that are stored in binlog. --- mysql/mysql_gtid.go | 82 +++++++++++++++++++++++++++++++++++++++++++++ mysql/mysql_test.go | 55 ++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) diff --git a/mysql/mysql_gtid.go b/mysql/mysql_gtid.go index fbd5b7b82..c1745672c 100644 --- a/mysql/mysql_gtid.go +++ b/mysql/mysql_gtid.go @@ -227,6 +227,58 @@ func (s *UUIDSet) AddInterval(in IntervalSlice) { s.Intervals = s.Intervals.Normalize() } +func (s *UUIDSet) MinusInterval(in IntervalSlice) { + var n IntervalSlice + in = in.Normalize() + + i, j := 0, 0 + var minuend Interval + var subtrahend Interval + for j < len(in) && i < len(s.Intervals) { + if minuend.Stop != s.Intervals[i].Stop { // `i` changed? + minuend = s.Intervals[i] + } + subtrahend = in[j] + + if minuend.Stop <= subtrahend.Start { + // no overlapping + n = append(n, minuend) + i++ + } else if minuend.Start >= subtrahend.Stop { + // no overlapping + j++ + } else { + if minuend.Start < subtrahend.Start && minuend.Stop < subtrahend.Stop { + n = append(n, Interval{minuend.Start, subtrahend.Start}) + i++ + } else if minuend.Start > subtrahend.Start && minuend.Stop > subtrahend.Stop { + minuend = Interval{subtrahend.Stop, minuend.Stop} + j++ + } else if minuend.Start >= subtrahend.Start && minuend.Stop <= subtrahend.Stop { + // minuend is completely removed + i++ + } else { + n = append(n, Interval{minuend.Start, subtrahend.Start}) + minuend = Interval{subtrahend.Stop, minuend.Stop} + j++ + } + } + } + + lastSub := in[len(in)-1] + for ; i < len(s.Intervals); i++ { + minuend = s.Intervals[i] + if minuend.Start < lastSub.Stop { + n = append(n, Interval{lastSub.Stop, minuend.Stop}) + } else { + n = append(n, s.Intervals[i]) + } + } + + s.Intervals = n + s.Intervals = s.Intervals.Normalize() +} + func (s *UUIDSet) String() string { return hack.String(s.Bytes()) } @@ -304,6 +356,8 @@ type MysqlGTIDSet struct { Sets map[string]*UUIDSet } +var _ GTIDSet = &MysqlGTIDSet{} + func ParseMysqlGTIDSet(str string) (GTIDSet, error) { s := new(MysqlGTIDSet) s.Sets = make(map[string]*UUIDSet) @@ -362,6 +416,20 @@ func (s *MysqlGTIDSet) AddSet(set *UUIDSet) { } } +func (s *MysqlGTIDSet) MinusSet(set *UUIDSet) { + if set == nil { + return + } + sid := set.SID.String() + uuidSet, ok := s.Sets[sid] + if ok { + uuidSet.MinusInterval(set.Intervals) + if uuidSet.Intervals == nil { + delete(s.Sets, sid) + } + } +} + func (s *MysqlGTIDSet) Update(GTIDStr string) error { gtidSet, err := ParseMysqlGTIDSet(GTIDStr) if err != nil { @@ -373,6 +441,20 @@ func (s *MysqlGTIDSet) Update(GTIDStr string) error { return nil } +func (s *MysqlGTIDSet) Add(addend MysqlGTIDSet) error { + for _, uuidSet := range addend.Sets { + s.AddSet(uuidSet) + } + return nil +} + +func (s *MysqlGTIDSet) Minus(subtrahend MysqlGTIDSet) error { + for _, uuidSet := range subtrahend.Sets { + s.MinusSet(uuidSet) + } + return nil +} + func (s *MysqlGTIDSet) Contain(o GTIDSet) bool { sub, ok := o.(*MysqlGTIDSet) if !ok { diff --git a/mysql/mysql_test.go b/mysql/mysql_test.go index d3264239b..8cc267ebe 100644 --- a/mysql/mysql_test.go +++ b/mysql/mysql_test.go @@ -1,6 +1,7 @@ package mysql import ( + "fmt" "strings" "testing" @@ -142,6 +143,53 @@ func (t *mysqlTestSuite) TestMysqlGTIDContain(c *check.C) { c.Assert(g1.Contain(g2), check.Equals, false) } +func (t *mysqlTestSuite) TestMysqlGTIDAdd(c *check.C) { + testCases := []struct { + left, right, expected string + }{ + // simple cases works: + {"3E11FA47-71CA-11E1-9E33-C80AA9429562:23", "3E11FA47-71CA-11E1-9E33-C80AA9429562:28-57", "3E11FA47-71CA-11E1-9E33-C80AA9429562:23:28-57"}, + // summ is associative operation + {"3E11FA47-71CA-11E1-9E33-C80AA9429562:23", "3E11FA47-71CA-11E1-9E33-C80AA9429562:28-57", "3E11FA47-71CA-11E1-9E33-C80AA9429562:23:28-57"}, + } + + for _, tc := range testCases { + m1 := t.mysqlGTIDfromString(c, tc.left) + m2 := t.mysqlGTIDfromString(c, tc.right) + m1.Add(m2) + c.Assert( + fmt.Sprintf("%s + %s = %s", tc.left, tc.right, strings.ToUpper(m1.String())), + check.Equals, + fmt.Sprintf("%s + %s = %s", tc.left, tc.right, tc.expected)) + } +} + +func (t *mysqlTestSuite) TestMysqlGTIDMinus(c *check.C) { + testCases := []struct { + left, right, expected string + }{ + // Minuses that doesn't affect original value: + {"3E11FA47-71CA-11E1-9E33-C80AA9429562:23", "3E11FA47-71CA-11E1-9E33-C80AA9429562:28-57", "3E11FA47-71CA-11E1-9E33-C80AA9429562:23"}, + {"3E11FA47-71CA-11E1-9E33-C80AA9429562:28-57", "3E11FA47-71CA-11E1-9E33-C80AA9429562:23", "3E11FA47-71CA-11E1-9E33-C80AA9429562:28-57"}, + {"3E11FA47-71CA-11E1-9E33-C80AA9429562:23", "3E11FA47-71CA-11E1-9E33-C80AA9429562:1-22:24-57", "3E11FA47-71CA-11E1-9E33-C80AA9429562:23"}, + {"3E11FA47-71CA-11E1-9E33-C80AA9429562:23", "ABCDEF12-1234-5678-9012-345678901234:1-1000", "3E11FA47-71CA-11E1-9E33-C80AA9429562:23"}, + // Minuses that change original value: + {"3E11FA47-71CA-11E1-9E33-C80AA9429562:20-57:60-90", "3E11FA47-71CA-11E1-9E33-C80AA9429562:23", "3E11FA47-71CA-11E1-9E33-C80AA9429562:20-22:24-57:60-90"}, + {"3E11FA47-71CA-11E1-9E33-C80AA9429562:20-57:60-90", "3E11FA47-71CA-11E1-9E33-C80AA9429562:22-70", "3E11FA47-71CA-11E1-9E33-C80AA9429562:20-21:71-90"}, + {"3E11FA47-71CA-11E1-9E33-C80AA9429562:28-57", "3E11FA47-71CA-11E1-9E33-C80AA9429562:28-57", ""}, + } + + for _, tc := range testCases { + m1 := t.mysqlGTIDfromString(c, tc.left) + m2 := t.mysqlGTIDfromString(c, tc.right) + m1.Minus(m2) + c.Assert( + fmt.Sprintf("%s - %s = %s", tc.left, tc.right, strings.ToUpper(m1.String())), + check.Equals, + fmt.Sprintf("%s - %s = %s", tc.left, tc.right, tc.expected)) + } +} + func (t *mysqlTestSuite) TestMysqlParseBinaryInt8(c *check.C) { i8 := ParseBinaryInt8([]byte{128}) c.Assert(i8, check.Equals, int8(-128)) @@ -229,3 +277,10 @@ func (t *mysqlTestSuite) TestMysqlEmptyDecode(c *check.C) { c.Assert(isNull, check.IsTrue) c.Assert(n, check.Equals, 0) } + +func (t *mysqlTestSuite) mysqlGTIDfromString(c *check.C, gtidStr string) MysqlGTIDSet { + gtid, err := ParseMysqlGTIDSet(gtidStr) + c.Assert(err, check.IsNil) + + return *gtid.(*MysqlGTIDSet) +} From 24e41d5e097483ea1e305038560150bb77fdf890 Mon Sep 17 00:00:00 2001 From: Nikolai Antonov Date: Tue, 11 Jan 2022 13:25:38 +0300 Subject: [PATCH 2/2] Code review fixes: clean code and better tests --- mysql/mysql_gtid.go | 3 +-- mysql/mysql_test.go | 10 +++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/mysql/mysql_gtid.go b/mysql/mysql_gtid.go index c1745672c..2a10ae257 100644 --- a/mysql/mysql_gtid.go +++ b/mysql/mysql_gtid.go @@ -275,8 +275,7 @@ func (s *UUIDSet) MinusInterval(in IntervalSlice) { } } - s.Intervals = n - s.Intervals = s.Intervals.Normalize() + s.Intervals = n.Normalize() } func (s *UUIDSet) String() string { diff --git a/mysql/mysql_test.go b/mysql/mysql_test.go index 8cc267ebe..62b24f758 100644 --- a/mysql/mysql_test.go +++ b/mysql/mysql_test.go @@ -150,13 +150,16 @@ func (t *mysqlTestSuite) TestMysqlGTIDAdd(c *check.C) { // simple cases works: {"3E11FA47-71CA-11E1-9E33-C80AA9429562:23", "3E11FA47-71CA-11E1-9E33-C80AA9429562:28-57", "3E11FA47-71CA-11E1-9E33-C80AA9429562:23:28-57"}, // summ is associative operation - {"3E11FA47-71CA-11E1-9E33-C80AA9429562:23", "3E11FA47-71CA-11E1-9E33-C80AA9429562:28-57", "3E11FA47-71CA-11E1-9E33-C80AA9429562:23:28-57"}, + {"3E11FA47-71CA-11E1-9E33-C80AA9429562:28-57", "3E11FA47-71CA-11E1-9E33-C80AA9429562:23", "3E11FA47-71CA-11E1-9E33-C80AA9429562:23:28-57"}, + // merge intervals: + {"3E11FA47-71CA-11E1-9E33-C80AA9429562:23-27", "3E11FA47-71CA-11E1-9E33-C80AA9429562:28-57", "3E11FA47-71CA-11E1-9E33-C80AA9429562:23-57"}, } for _, tc := range testCases { m1 := t.mysqlGTIDfromString(c, tc.left) m2 := t.mysqlGTIDfromString(c, tc.right) - m1.Add(m2) + err := m1.Add(m2) + c.Assert(err, check.IsNil) c.Assert( fmt.Sprintf("%s + %s = %s", tc.left, tc.right, strings.ToUpper(m1.String())), check.Equals, @@ -182,7 +185,8 @@ func (t *mysqlTestSuite) TestMysqlGTIDMinus(c *check.C) { for _, tc := range testCases { m1 := t.mysqlGTIDfromString(c, tc.left) m2 := t.mysqlGTIDfromString(c, tc.right) - m1.Minus(m2) + err := m1.Minus(m2) + c.Assert(err, check.IsNil) c.Assert( fmt.Sprintf("%s - %s = %s", tc.left, tc.right, strings.ToUpper(m1.String())), check.Equals,