зеркало из https://github.com/github/vitess-gh.git
Implement GTID and GTIDSet for MySQL 5.6.
This commit is contained in:
Родитель
57290ce3d4
Коммит
1cd37f7ca0
|
@ -0,0 +1,85 @@
|
|||
// Copyright 2015, Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const mysql56FlavorID = "MySQL56"
|
||||
|
||||
// SID is the 16-byte unique ID of a MySQL 5.6 server.
|
||||
type SID [16]byte
|
||||
|
||||
// String prints an SID in the form used by MySQL 5.6.
|
||||
func (sid SID) String() string {
|
||||
dst := []byte("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
|
||||
hex.Encode(dst, sid[:4])
|
||||
hex.Encode(dst[9:], sid[4:6])
|
||||
hex.Encode(dst[14:], sid[6:8])
|
||||
hex.Encode(dst[19:], sid[8:10])
|
||||
hex.Encode(dst[24:], sid[10:16])
|
||||
return string(dst)
|
||||
}
|
||||
|
||||
// ParseSID parses an SID in the form used by MySQL 5.6.
|
||||
func ParseSID(s string) (sid SID, err error) {
|
||||
if len(s) != 36 || s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
|
||||
return sid, fmt.Errorf("invalid MySQL 5.6 SID %q", s)
|
||||
}
|
||||
|
||||
// Drop the dashes so we can just check the error of Decode once.
|
||||
b := make([]byte, 0, 32)
|
||||
b = append(b, s[:8]...)
|
||||
b = append(b, s[9:13]...)
|
||||
b = append(b, s[14:18]...)
|
||||
b = append(b, s[19:23]...)
|
||||
b = append(b, s[24:]...)
|
||||
|
||||
if _, err := hex.Decode(sid[:], b); err != nil {
|
||||
return sid, fmt.Errorf("invalid MySQL 5.6 SID %q: %v", s, err)
|
||||
}
|
||||
return sid, nil
|
||||
}
|
||||
|
||||
// Mysql56GTID implements GTID
|
||||
type Mysql56GTID struct {
|
||||
// Server is the SID of the server that originally committed the transaction.
|
||||
Server SID
|
||||
// Sequence is the sequence number of the transaction within a given Server's
|
||||
// scope.
|
||||
Sequence int64
|
||||
}
|
||||
|
||||
// String implements GTID.String().
|
||||
func (gtid Mysql56GTID) String() string {
|
||||
return fmt.Sprintf("%s:%d", gtid.Server, gtid.Sequence)
|
||||
}
|
||||
|
||||
// Flavor implements GTID.Flavor().
|
||||
func (gtid Mysql56GTID) Flavor() string {
|
||||
return mysql56FlavorID
|
||||
}
|
||||
|
||||
// SequenceDomain implements GTID.SequenceDomain().
|
||||
func (gtid Mysql56GTID) SequenceDomain() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SourceServer implements GTID.SourceServer().
|
||||
func (gtid Mysql56GTID) SourceServer() interface{} {
|
||||
return gtid.Server
|
||||
}
|
||||
|
||||
// SequenceNumber implements GTID.SequenceNumber().
|
||||
func (gtid Mysql56GTID) SequenceNumber() interface{} {
|
||||
return gtid.Sequence
|
||||
}
|
||||
|
||||
// GTIDSet implements GTID.GTIDSet().
|
||||
func (gtid Mysql56GTID) GTIDSet() GTIDSet {
|
||||
return Mysql56GTIDSet{}.AddGTID(gtid)
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
// Copyright 2015, Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type interval struct {
|
||||
start, end int64
|
||||
}
|
||||
|
||||
func (iv interval) contains(other interval) bool {
|
||||
return iv.start <= other.start && other.end <= iv.end
|
||||
}
|
||||
|
||||
// Mysql56GTIDSet implements GTIDSet for MySQL 5.6.
|
||||
type Mysql56GTIDSet map[SID][]interval
|
||||
|
||||
// SIDs returns a sorted list of SIDs in the set.
|
||||
func (set Mysql56GTIDSet) SIDs() []SID {
|
||||
sids := make([]SID, 0, len(set))
|
||||
for sid := range set {
|
||||
sids = append(sids, sid)
|
||||
}
|
||||
sort.Sort(sidList(sids))
|
||||
return sids
|
||||
}
|
||||
|
||||
// Len implements sort.Interface.
|
||||
type sidList []SID
|
||||
|
||||
func (s sidList) Len() int { return len(s) }
|
||||
|
||||
// Less implements sort.Interface.
|
||||
func (s sidList) Less(i, j int) bool { return bytes.Compare(s[i][:], s[j][:]) < 0 }
|
||||
|
||||
// Swap implements sort.Interface.
|
||||
func (s sidList) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
// String implements GTIDSet.
|
||||
func (set Mysql56GTIDSet) String() string {
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
for i, sid := range set.SIDs() {
|
||||
if i != 0 {
|
||||
buf.WriteByte(',')
|
||||
}
|
||||
buf.WriteString(sid.String())
|
||||
|
||||
for _, interval := range set[sid] {
|
||||
buf.WriteByte(':')
|
||||
buf.WriteString(strconv.FormatInt(interval.start, 10))
|
||||
|
||||
if interval.end != interval.start {
|
||||
buf.WriteByte('-')
|
||||
buf.WriteString(strconv.FormatInt(interval.end, 10))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Flavor implements GTIDSet.
|
||||
func (Mysql56GTIDSet) Flavor() string { return mysql56FlavorID }
|
||||
|
||||
// ContainsGTID implements GTIDSet.
|
||||
func (set Mysql56GTIDSet) ContainsGTID(gtid GTID) bool {
|
||||
gtid56, ok := gtid.(Mysql56GTID)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, iv := range set[gtid56.Server] {
|
||||
if iv.start > gtid56.Sequence {
|
||||
// We assume intervals are sorted, so we can skip the rest.
|
||||
return false
|
||||
}
|
||||
if gtid56.Sequence <= iv.end {
|
||||
// Now we know that: start <= Sequence <= end.
|
||||
return true
|
||||
}
|
||||
}
|
||||
// Server wasn't in the set, or no interval contained gtid.
|
||||
return false
|
||||
}
|
||||
|
||||
// Contains implements GTIDSet.
|
||||
func (set Mysql56GTIDSet) Contains(other GTIDSet) bool {
|
||||
other56, ok := other.(Mysql56GTIDSet)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check each SID in the other set.
|
||||
for sid, otherIntervals := range other56 {
|
||||
i := 0
|
||||
intervals := set[sid]
|
||||
count := len(intervals)
|
||||
|
||||
// Check each interval for this SID in the other set.
|
||||
for _, iv := range otherIntervals {
|
||||
// Check that interval against each of our intervals.
|
||||
// Intervals are monotonically increasing,
|
||||
// so we don't need to reset the index each time.
|
||||
for {
|
||||
if i >= count {
|
||||
// We ran out of intervals to check against.
|
||||
return false
|
||||
}
|
||||
if intervals[i].contains(iv) {
|
||||
// Yes it's covered. Go on to the next one.
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No uncovered intervals were found.
|
||||
return true
|
||||
}
|
||||
|
||||
// Equal implements GTIDSet.
|
||||
func (set Mysql56GTIDSet) Equal(other GTIDSet) bool {
|
||||
other56, ok := other.(Mysql56GTIDSet)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check for same number of SIDs.
|
||||
if len(set) != len(other56) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Compare each SID.
|
||||
for sid, intervals := range set {
|
||||
otherIntervals := other56[sid]
|
||||
|
||||
// Check for same number of intervals.
|
||||
if len(intervals) != len(otherIntervals) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Compare each interval.
|
||||
// Since intervals are sorted, they have to be in the same order.
|
||||
for i, iv := range intervals {
|
||||
if iv != otherIntervals[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No discrepancies were found.
|
||||
return true
|
||||
}
|
||||
|
||||
// AddGTID implements GTIDSet.
|
||||
func (set Mysql56GTIDSet) AddGTID(gtid GTID) GTIDSet {
|
||||
gtid56, ok := gtid.(Mysql56GTID)
|
||||
if !ok {
|
||||
return set
|
||||
}
|
||||
|
||||
// Make a copy and add the new GTID in the proper place.
|
||||
// This function is not supposed to modify the original set.
|
||||
newSet := make(Mysql56GTIDSet)
|
||||
|
||||
added := false
|
||||
|
||||
for sid, intervals := range set {
|
||||
newIntervals := make([]interval, 0, len(intervals))
|
||||
|
||||
if sid == gtid56.Server {
|
||||
// Look for the right place to add this GTID.
|
||||
for _, iv := range intervals {
|
||||
if !added {
|
||||
switch {
|
||||
case gtid56.Sequence == iv.start-1:
|
||||
// Expand the interval at the beginning.
|
||||
iv.start = gtid56.Sequence
|
||||
added = true
|
||||
case gtid56.Sequence == iv.end+1:
|
||||
// Expand the interval at the end.
|
||||
iv.end = gtid56.Sequence
|
||||
added = true
|
||||
case gtid56.Sequence < iv.start-1:
|
||||
// The next interval is beyond the new GTID, but it can't
|
||||
// be expanded, so we have to insert a new interval.
|
||||
newIntervals = append(newIntervals, interval{start: gtid56.Sequence, end: gtid56.Sequence})
|
||||
added = true
|
||||
}
|
||||
}
|
||||
// Check if this interval can be merged with the previous one.
|
||||
count := len(newIntervals)
|
||||
if count != 0 && iv.start == newIntervals[count-1].end+1 {
|
||||
// Merge instead of appending.
|
||||
newIntervals[count-1].end = iv.end
|
||||
} else {
|
||||
// Can't be merged.
|
||||
newIntervals = append(newIntervals, iv)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Just copy everything.
|
||||
newIntervals = append(newIntervals, intervals...)
|
||||
}
|
||||
|
||||
newSet[sid] = newIntervals
|
||||
}
|
||||
|
||||
if !added {
|
||||
// There wasn't any place to insert the new GTID, so just append it
|
||||
// as a new interval.
|
||||
newSet[gtid56.Server] = append(newSet[gtid56.Server], interval{start: gtid56.Sequence, end: gtid56.Sequence})
|
||||
}
|
||||
|
||||
return newSet
|
||||
}
|
||||
|
||||
// SIDBlock returns the binary encoding of a MySQL 5.6 GTID set as expected
|
||||
// by internal commands that refer to an "SID block".
|
||||
//
|
||||
// e.g. https://dev.mysql.com/doc/internals/en/com-binlog-dump-gtid.html
|
||||
func (set Mysql56GTIDSet) SIDBlock() []byte {
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
// Number of SIDs.
|
||||
binary.Write(buf, binary.LittleEndian, uint64(len(set)))
|
||||
|
||||
for _, sid := range set.SIDs() {
|
||||
buf.Write(sid[:])
|
||||
|
||||
// Number of intervals.
|
||||
intervals := set[sid]
|
||||
binary.Write(buf, binary.LittleEndian, uint64(len(intervals)))
|
||||
|
||||
for _, iv := range intervals {
|
||||
binary.Write(buf, binary.LittleEndian, iv.start)
|
||||
binary.Write(buf, binary.LittleEndian, iv.end)
|
||||
}
|
||||
}
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
|
@ -0,0 +1,321 @@
|
|||
// Copyright 2015, Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMysql56GTIDSetString(t *testing.T) {
|
||||
sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
sid2 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 255}
|
||||
|
||||
table := map[string]Mysql56GTIDSet{
|
||||
// Simple case
|
||||
"00010203-0405-0607-0809-0a0b0c0d0e0f:1-5": Mysql56GTIDSet{
|
||||
sid1: []interval{{1, 5}},
|
||||
},
|
||||
// Interval with same start and end
|
||||
"00010203-0405-0607-0809-0a0b0c0d0e0f:12": Mysql56GTIDSet{
|
||||
sid1: []interval{{12, 12}},
|
||||
},
|
||||
// Multiple intervals
|
||||
"00010203-0405-0607-0809-0a0b0c0d0e0f:1-5:10-20": Mysql56GTIDSet{
|
||||
sid1: []interval{{1, 5}, {10, 20}},
|
||||
},
|
||||
// Multiple SIDs
|
||||
"00010203-0405-0607-0809-0a0b0c0d0e0f:1-5:10-20,00010203-0405-0607-0809-0a0b0c0d0eff:1-5:50": Mysql56GTIDSet{
|
||||
sid1: []interval{{1, 5}, {10, 20}},
|
||||
sid2: []interval{{1, 5}, {50, 50}},
|
||||
},
|
||||
}
|
||||
|
||||
for want, input := range table {
|
||||
got := strings.ToLower(input.String())
|
||||
if got != want {
|
||||
t.Errorf("%#v.String() = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56GTIDSetFlavor(t *testing.T) {
|
||||
input := Mysql56GTIDSet{}
|
||||
if got, want := input.Flavor(), "MySQL56"; got != want {
|
||||
t.Errorf("%#v.Flavor() = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56GTIDSetContainsGTID(t *testing.T) {
|
||||
sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
sid2 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16}
|
||||
sid3 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17}
|
||||
|
||||
set := Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}},
|
||||
sid2: []interval{{1, 5}, {50, 50}},
|
||||
}
|
||||
|
||||
table := map[GTID]bool{
|
||||
fakeGTID{}: false,
|
||||
|
||||
Mysql56GTID{sid1, 1}: false,
|
||||
Mysql56GTID{sid1, 19}: false,
|
||||
Mysql56GTID{sid1, 20}: true,
|
||||
Mysql56GTID{sid1, 23}: true,
|
||||
Mysql56GTID{sid1, 30}: true,
|
||||
Mysql56GTID{sid1, 31}: false,
|
||||
|
||||
Mysql56GTID{sid2, 1}: true,
|
||||
Mysql56GTID{sid2, 10}: false,
|
||||
Mysql56GTID{sid2, 50}: true,
|
||||
Mysql56GTID{sid2, 51}: false,
|
||||
|
||||
Mysql56GTID{sid3, 1}: false,
|
||||
}
|
||||
|
||||
for input, want := range table {
|
||||
if got := set.ContainsGTID(input); got != want {
|
||||
t.Errorf("ContainsGTID(%#v) = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56GTIDSetContains(t *testing.T) {
|
||||
sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
sid2 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16}
|
||||
sid3 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17}
|
||||
|
||||
// The set to test against.
|
||||
set := Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}},
|
||||
sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
}
|
||||
|
||||
// Test cases that should return Contains() = true.
|
||||
contained := []Mysql56GTIDSet{
|
||||
// The set should contain itself.
|
||||
set,
|
||||
// Every set contains the empty set.
|
||||
Mysql56GTIDSet{},
|
||||
|
||||
// Simple case
|
||||
Mysql56GTIDSet{sid1: []interval{{25, 30}}},
|
||||
// Multiple intervals
|
||||
Mysql56GTIDSet{sid2: []interval{{1, 2}, {4, 5}, {60, 70}}},
|
||||
// Multiple SIDs
|
||||
Mysql56GTIDSet{
|
||||
sid1: []interval{{25, 30}, {35, 37}},
|
||||
sid2: []interval{{1, 5}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, other := range contained {
|
||||
if !set.Contains(other) {
|
||||
t.Errorf("Contains(%#v) = false, want true", other)
|
||||
}
|
||||
}
|
||||
|
||||
// Test cases that should return Contains() = false.
|
||||
notContained := []GTIDSet{
|
||||
// Wrong flavor is not contained.
|
||||
fakeGTID{},
|
||||
|
||||
// Simple cases
|
||||
Mysql56GTIDSet{sid1: []interval{{1, 5}}},
|
||||
Mysql56GTIDSet{sid1: []interval{{10, 19}}},
|
||||
// Overlapping intervals
|
||||
Mysql56GTIDSet{sid1: []interval{{10, 20}}},
|
||||
Mysql56GTIDSet{sid1: []interval{{10, 25}}},
|
||||
Mysql56GTIDSet{sid1: []interval{{25, 31}}},
|
||||
Mysql56GTIDSet{sid1: []interval{{30, 31}}},
|
||||
// Multiple intervals
|
||||
Mysql56GTIDSet{sid1: []interval{{20, 30}, {34, 34}}},
|
||||
// Multiple SIDs
|
||||
Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {36, 36}},
|
||||
sid2: []interval{{3, 5}, {55, 60}},
|
||||
},
|
||||
// SID is missing entirely
|
||||
Mysql56GTIDSet{sid3: []interval{{1, 5}}},
|
||||
}
|
||||
|
||||
for _, other := range notContained {
|
||||
if set.Contains(other) {
|
||||
t.Errorf("Contains(%#v) = true, want false", other)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56GTIDSetEqual(t *testing.T) {
|
||||
sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
sid2 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16}
|
||||
sid3 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17}
|
||||
|
||||
// The set to test against.
|
||||
set := Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}},
|
||||
sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
}
|
||||
|
||||
// Test cases that should return Equal() = true.
|
||||
equal := []Mysql56GTIDSet{
|
||||
// Same underlying map instance
|
||||
set,
|
||||
// Different instance, same data
|
||||
Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}},
|
||||
sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, other := range equal {
|
||||
if !set.Equal(other) {
|
||||
t.Errorf("Equal(%#v) = false, want true", other)
|
||||
}
|
||||
}
|
||||
|
||||
// Test cases that should return Equal() = false.
|
||||
notEqual := []GTIDSet{
|
||||
// Wrong flavor is not equal.
|
||||
fakeGTID{},
|
||||
// Empty set
|
||||
Mysql56GTIDSet{},
|
||||
// Interval changed
|
||||
Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 31}, {35, 40}},
|
||||
sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
},
|
||||
// Interval added
|
||||
Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {32, 33}, {35, 40}},
|
||||
sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
},
|
||||
// Interval removed
|
||||
Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}},
|
||||
sid2: []interval{{1, 5}, {60, 70}},
|
||||
},
|
||||
// Different SID, same intervals
|
||||
Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}},
|
||||
sid3: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
},
|
||||
// SID added
|
||||
Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}},
|
||||
sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
sid3: []interval{{1, 5}},
|
||||
},
|
||||
// SID removed
|
||||
Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, other := range notEqual {
|
||||
if set.Equal(other) {
|
||||
t.Errorf("Equal(%#v) = true, want false", other)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56GTIDSetAddGTID(t *testing.T) {
|
||||
sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
sid2 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16}
|
||||
sid3 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17}
|
||||
|
||||
// The set to test against.
|
||||
set := Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}, {42, 45}},
|
||||
sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
}
|
||||
|
||||
table := map[GTID]Mysql56GTIDSet{
|
||||
// Adding wrong flavor is a no-op.
|
||||
fakeGTID{}: set,
|
||||
|
||||
// New interval beginning
|
||||
Mysql56GTID{Server: sid1, Sequence: 1}: Mysql56GTIDSet{
|
||||
sid1: []interval{{1, 1}, {20, 30}, {35, 40}, {42, 45}},
|
||||
sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
},
|
||||
// New interval middle
|
||||
Mysql56GTID{Server: sid1, Sequence: 32}: Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {32, 32}, {35, 40}, {42, 45}},
|
||||
sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
},
|
||||
// New interval end
|
||||
Mysql56GTID{Server: sid1, Sequence: 50}: Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}, {42, 45}, {50, 50}},
|
||||
sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
},
|
||||
// Extend interval start
|
||||
Mysql56GTID{Server: sid2, Sequence: 49}: Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}, {42, 45}},
|
||||
sid2: []interval{{1, 5}, {49, 50}, {60, 70}},
|
||||
},
|
||||
// Extend interval end
|
||||
Mysql56GTID{Server: sid2, Sequence: 51}: Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}, {42, 45}},
|
||||
sid2: []interval{{1, 5}, {50, 51}, {60, 70}},
|
||||
},
|
||||
// Merge intervals
|
||||
Mysql56GTID{Server: sid1, Sequence: 41}: Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 45}},
|
||||
sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
},
|
||||
// Different SID
|
||||
Mysql56GTID{Server: sid3, Sequence: 1}: Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}, {42, 45}},
|
||||
sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
sid3: []interval{{1, 1}},
|
||||
},
|
||||
}
|
||||
|
||||
for input, want := range table {
|
||||
if got := set.AddGTID(input); !got.Equal(want) {
|
||||
t.Errorf("AddGTID(%#v) = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56GTIDSetSIDBlock(t *testing.T) {
|
||||
sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
sid2 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16}
|
||||
|
||||
input := Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}},
|
||||
sid2: []interval{{1, 5}},
|
||||
}
|
||||
want := []byte{
|
||||
// n_sids
|
||||
2, 0, 0, 0, 0, 0, 0, 0,
|
||||
// sid1
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
// sid1: n_intervals
|
||||
2, 0, 0, 0, 0, 0, 0, 0,
|
||||
// sid1: interval 1 start
|
||||
20, 0, 0, 0, 0, 0, 0, 0,
|
||||
// sid1: interval 1 end
|
||||
30, 0, 0, 0, 0, 0, 0, 0,
|
||||
// sid1: interval 2 start
|
||||
35, 0, 0, 0, 0, 0, 0, 0,
|
||||
// sid1: interval 2 end
|
||||
40, 0, 0, 0, 0, 0, 0, 0,
|
||||
// sid2
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16,
|
||||
// sid2: n_intervals
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
// sid2: interval 1 start
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
// sid2: interval 1 end
|
||||
5, 0, 0, 0, 0, 0, 0, 0,
|
||||
}
|
||||
if got := input.SIDBlock(); !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%#v.SIDBlock() = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
// Copyright 2015, Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSIDString(t *testing.T) {
|
||||
input := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
want := "00010203-0405-0607-0809-0a0b0c0d0e0f"
|
||||
|
||||
if got := strings.ToLower(input.String()); got != want {
|
||||
t.Errorf("%#v.String() = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseSID(t *testing.T) {
|
||||
input := "00010203-0405-0607-0809-0A0B0C0D0E0F"
|
||||
want := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
|
||||
got, err := ParseSID(input)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if got != want {
|
||||
t.Errorf("ParseSID(%#v) = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseSIDInvalid(t *testing.T) {
|
||||
table := []string{
|
||||
"123",
|
||||
"x",
|
||||
"00010203-0405-0607-0809-0A0B0C0D0E0x",
|
||||
"00010203-0405-0607-080900A0B0C0D0E0F",
|
||||
}
|
||||
|
||||
for _, input := range table {
|
||||
_, err := ParseSID(input)
|
||||
if err == nil {
|
||||
t.Errorf("ParseSID(%#v): expected error, got none", input)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56GTIDString(t *testing.T) {
|
||||
input := Mysql56GTID{
|
||||
Server: SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||
Sequence: 12345,
|
||||
}
|
||||
want := "00010203-0405-0607-0809-0a0b0c0d0e0f:12345"
|
||||
if got := strings.ToLower(input.String()); got != want {
|
||||
t.Errorf("%#v.String() = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56GTIDFlavor(t *testing.T) {
|
||||
input := Mysql56GTID{}
|
||||
if got, want := input.Flavor(), "MySQL56"; got != want {
|
||||
t.Errorf("%#v.Flavor() = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56SequenceDomain(t *testing.T) {
|
||||
input := Mysql56GTID{}
|
||||
if got, want := input.SequenceDomain(), interface{}(nil); got != want {
|
||||
t.Errorf("%#v.SequenceDomain() = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56SourceServer(t *testing.T) {
|
||||
input := Mysql56GTID{
|
||||
Server: SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||
}
|
||||
want := interface{}(SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15})
|
||||
if got := input.SourceServer(); got != want {
|
||||
t.Errorf("%#v.SourceServer() = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56SequenceNumber(t *testing.T) {
|
||||
input := Mysql56GTID{
|
||||
Server: SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||
Sequence: 5432,
|
||||
}
|
||||
want := interface{}(int64(5432))
|
||||
if got := input.SequenceNumber(); got != want {
|
||||
t.Errorf("%#v.SequenceNumber() = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56GTIDGTIDSet(t *testing.T) {
|
||||
sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
input := Mysql56GTID{Server: sid1, Sequence: 5432}
|
||||
want := Mysql56GTIDSet{sid1: []interval{{5432, 5432}}}
|
||||
if got := input.GTIDSet(); !got.Equal(want) {
|
||||
t.Errorf("%#v.GTIDSet() = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче