From 298e51a6804408c3572f1af1f11a779867084fec Mon Sep 17 00:00:00 2001 From: starainrt Date: Tue, 25 Apr 2023 18:50:31 +0800 Subject: [PATCH] init --- .idea/.gitignore | 8 + .idea/deployment.xml | 14 ++ .idea/gtid.iml | 9 + .idea/modules.xml | 8 + go.mod | 3 + gtid.go | 569 +++++++++++++++++++++++++++++++++++++++++++ gtid_test.go | 424 ++++++++++++++++++++++++++++++++ gtidtool.go | 97 ++++++++ struct.go | 33 +++ 9 files changed, 1165 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/deployment.xml create mode 100644 .idea/gtid.iml create mode 100644 .idea/modules.xml create mode 100644 go.mod create mode 100644 gtid.go create mode 100644 gtid_test.go create mode 100644 gtidtool.go create mode 100644 struct.go diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/deployment.xml b/.idea/deployment.xml new file mode 100644 index 0000000..01ce228 --- /dev/null +++ b/.idea/deployment.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/gtid.iml b/.idea/gtid.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/gtid.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..3ed7d08 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..30a2bcf --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module b612.me/mysql/gtid + +go 1.20 diff --git a/gtid.go b/gtid.go new file mode 100644 index 0000000..b7ef3a4 --- /dev/null +++ b/gtid.go @@ -0,0 +1,569 @@ +package gtid + +import ( + "errors" + "fmt" + "sort" + "strconv" + "strings" +) + +func Parse(gtidDesc string) (*Gtid, error) { + return parse(gtidDesc) +} +func parse(gtidDesc string) (*Gtid, error) { + var gtid Gtid + gtid.hashMap = make(map[string]int) + gtidDesc = strings.TrimSpace(gtidDesc) + if "" == gtidDesc { + return >id, nil + } + return >id, gtid.uniform(gtidDesc) +} + +func (g *Gtid) uniform(gtidDesc string) error { + hashMap := make(map[string][]string) + gtidlists := strings.Split(gtidDesc, ",") + uuidLists := make([]string, 0, len(gtidlists)) + for _, v := range gtidlists { + if v == "" { + continue + } + numbers := strings.Split(v, ":") + if len(numbers) < 2 { + return fmt.Errorf("invalid format:%v", v) + } + uuid, err := g.isValidUUID(numbers[0]) + if err != nil { + return err + } + numbers = numbers[1:] + existsNums, ok := hashMap[uuid] + if ok { + for _, v := range existsNums { + numbers = append(numbers, v) + } + hashMap[uuid] = numbers + continue + } + hashMap[uuid] = numbers + uuidLists = append(uuidLists, uuid) + } + sort.Strings(uuidLists) + g.uuidNumbers = make([]UUIDNumber, 0, len(uuidLists)) + for k, v := range uuidLists { + number, err := g.uniformNumber(hashMap[v]) + if err != nil { + return err + } + number, err = g.uniformRange(number) + if err != nil { + return err + } + g.mu.Lock() + g.hashMap[v] = k + g.mu.Unlock() + g.uuidNumbers = append(g.uuidNumbers, UUIDNumber{ + uuid: v, + intervals: number, + }) + } + return nil +} + +func (g *Gtid) reform() { + sort.Sort(SortUUID(g.uuidNumbers)) + g.hashMap = make(map[string]int, len(g.uuidNumbers)) + for k, v := range g.uuidNumbers { + g.uuidNumbers[k].intervals, _ = g.uniformRange(v.intervals) + g.hashMap[v.uuid] = k + } + +} + +func (g *Gtid) uniformRange(rg []uuidRange) ([]uuidRange, error) { + newRg := make([]uuidRange, 0, len(rg)) + sort.Sort(SortRange(rg)) + var last *uuidRange = nil + for _, v := range rg { + if last != nil && v.min <= last.max+1 { + if last.max <= v.max { + last.max = v.max + } + continue + } + newRg = append(newRg, v) + last = &newRg[len(newRg)-1] + } + return newRg, nil +} + +func subRange(origin, rg []uuidRange) []uuidRange { + newRg := make([]uuidRange, 0) + sort.Sort(SortRange(rg)) + sort.Sort(SortRange(origin)) + for i := 0; i < len(origin); i++ { + ori := origin[i] + res := make([]uuidRange, 0) + shouldAdd := true + for _, v := range rg { + if v.min <= ori.min && v.max >= ori.max { + shouldAdd = false + break + } + if v.max < ori.min { + continue + } + if v.min > ori.max { + break + } + + ur1 := uuidRange{ + min: ori.min, + max: v.min - 1, + } + ur2 := uuidRange{ + min: v.max + 1, + max: ori.max, + } + if ur1.max >= ur1.min { + res = append(res, ur1) + } + if ur2.max >= ur2.min { + res = append(res, ur2) + } + + } + if len(res) == 0 && shouldAdd { + res = append(res, ori) + } + newRg = append(newRg, res...) + } + return newRg +} + +func containRange(origin, rg []uuidRange) bool { + sort.Sort(SortRange(rg)) + sort.Sort(SortRange(origin)) + for i := 0; i < len(rg); i++ { + ft := rg[i] + found := false + for _, v := range origin { + if v.min <= ft.min && v.max >= ft.max { + found = true + } + } + if !found { + return false + } + } + return true +} + +func overlapRange(origin, rg []uuidRange) bool { + sort.Sort(SortRange(rg)) + sort.Sort(SortRange(origin)) + for i := 0; i < len(origin); i++ { + ori := origin[i] + for _, v := range rg { + if v.min <= ori.min && v.max >= ori.max { + return true + } + if v.max < ori.min { + continue + } + if v.min > ori.max { + break + } + + ur1 := uuidRange{ + min: ori.min, + max: v.min - 1, + } + ur2 := uuidRange{ + min: v.max + 1, + max: ori.max, + } + if ur1.max >= ur1.min { + return true + } + if ur2.max >= ur2.min { + return true + } + + } + } + return false +} + +func (g *Gtid) uniformNumber(num []string) ([]uuidRange, error) { + rg := make([]uuidRange, 0, len(num)) + for _, v := range num { + ret := uuidRange{} + if splitPos := strings.Index(v, "-"); -1 != splitPos { + firstPart := string(v[0:splitPos]) + if i64, err := strconv.ParseUint(firstPart, 10, 64); nil == err { + ret.min = i64 + } else { + return rg, fmt.Errorf("invalid number %v", firstPart) + } + secondPart := string(v[splitPos+1:]) + if i64, err := strconv.ParseUint(secondPart, 10, 64); nil == err { + ret.max = i64 + } else { + return rg, fmt.Errorf("invalid number %v", secondPart) + } + } else { + if i64, err := strconv.ParseUint(v, 10, 64); nil == err { + ret.min = i64 + ret.max = i64 + } else { + return rg, fmt.Errorf("invalid number %v", v) + } + } + rg = append(rg, ret) + } + return rg, nil +} + +func (g *Gtid) isValidUUID(uuid string) (string, error) { + uuid = strings.TrimSpace(strings.ToLower(strings.Replace(uuid, "-", "", -1))) + if 32 != len(uuid) { + return "", errors.New("invalid uuid" + uuid) + } + return uuid[0:8] + "-" + uuid[8:12] + "-" + uuid[12:16] + "-" + uuid[16:20] + "-" + uuid[20:], nil +} + +func (g *Gtid) String() (ret string) { + + if g.changed { + g.mu.Lock() + g.reform() + g.mu.Unlock() + g.changed = false + } + for _, uuidNumber := range g.uuidNumbers { + s := uuidNumber.uuid + for _, interval := range uuidNumber.intervals { + if interval.min == interval.max { + s = s + ":" + strconv.FormatUint(interval.min, 10) + } else { + s = s + ":" + strconv.FormatUint(interval.min, 10) + "-" + strconv.FormatUint(interval.max, 10) + } + } + if "" != ret { + ret = ret + "," + } + ret = ret + s + } + return ret +} + +func (g *Gtid) AddGtid(uuid string, number uint64) error { + if strings.TrimSpace(uuid) == "" { + return nil + } + uuid, err := g.isValidUUID(uuid) + if err != nil { + return err + } + g.mu.RLock() + id, ok := g.hashMap[uuid] + g.mu.RUnlock() + g.mu.Lock() + defer g.mu.Unlock() + if ok { + tmp := g.uuidNumbers[id].intervals + if len(tmp) > 0 && tmp[len(tmp)-1].max+1 == number { + g.uuidNumbers[id].intervals[len(tmp)-1].max = number + } else { + g.uuidNumbers[id].intervals = append(g.uuidNumbers[id].intervals, uuidRange{ + min: number, + max: number, + }) + } + } else { + g.uuidNumbers = append(g.uuidNumbers, UUIDNumber{ + uuid: uuid, + intervals: []uuidRange{uuidRange{ + min: number, + max: number, + }}, + }) + g.hashMap[uuid] = len(g.uuidNumbers) - 1 + } + if len(g.uuidNumbers[id].intervals) > 5000 { + g.reform() + } + g.changed = true + return nil +} +func (g *Gtid) Add(gtidDesc string) error { + return g.add(gtidDesc) +} +func (g *Gtid) add(gtidDesc string) error { + tmpSlice := make([]UUIDNumber, 0) + gtidlists := strings.Split(gtidDesc, ",") + for _, v := range gtidlists { + numbers := strings.Split(v, ":") + if len(numbers) < 2 { + return fmt.Errorf("invalid format:%v", v) + } + uuid, err := g.isValidUUID(numbers[0]) + if err != nil { + return err + } + num, err := g.uniformNumber(numbers[1:]) + if err != nil { + return err + } + tmpSlice = append(tmpSlice, UUIDNumber{ + uuid: uuid, + intervals: num, + }) + } + g.changed = true + g.mu.Lock() + defer g.mu.Unlock() + for _, v := range tmpSlice { + id, ok := g.hashMap[v.uuid] + if ok { + n := g.uuidNumbers[id].intervals + n = append(n, v.intervals...) + g.uuidNumbers[id].intervals = n + } else { + g.uuidNumbers = append(g.uuidNumbers, v) + g.hashMap[v.uuid] = len(g.uuidNumbers) - 1 + } + + if len(g.uuidNumbers[id].intervals) > 5000 { + g.reform() + } + } + return nil +} +func (g *Gtid) Sub(gtidDesc string) error { + return g.sub(gtidDesc) +} +func (g *Gtid) sub(gtidDesc string) error { + if strings.TrimSpace(gtidDesc) == "" { + return nil + } + tmpSlice := make([]UUIDNumber, 0) + gtidlists := strings.Split(gtidDesc, ",") + for _, v := range gtidlists { + numbers := strings.Split(v, ":") + if len(numbers) < 2 { + return fmt.Errorf("invalid format:%v", v) + } + uuid, err := g.isValidUUID(numbers[0]) + if err != nil { + return err + } + num, err := g.uniformNumber(numbers[1:]) + if err != nil { + return err + } + tmpSlice = append(tmpSlice, UUIDNumber{ + uuid: uuid, + intervals: num, + }) + } + g.changed = true + g.mu.Lock() + defer g.mu.Unlock() + for _, v := range tmpSlice { + id, ok := g.hashMap[v.uuid] + if ok { + n := subRange(g.uuidNumbers[id].intervals, v.intervals) + n, _ = g.uniformRange(n) + if len(n) == 0 || (len(n) == 1 && n[0].max == 0) { + delete(g.hashMap, v.uuid) + g.uuidNumbers = append(g.uuidNumbers[:id], g.uuidNumbers[id+1:]...) + for k, v := range g.uuidNumbers { + g.hashMap[v.uuid] = k + } + continue + } else { + g.uuidNumbers[id].intervals = n + } + } else { + continue + } + if len(g.uuidNumbers[id].intervals) > 5000 { + g.reform() + } + } + return nil +} + +func (g *Gtid) ContainGtid(uuid string, number uint64) (bool, error) { + if strings.TrimSpace(uuid) == "" { + return true, nil + } + uuid, err := g.isValidUUID(uuid) + if err != nil { + return false, err + } + g.mu.Lock() + defer g.mu.Unlock() + id, ok := g.hashMap[uuid] + if ok { + if g.changed { + g.reform() + } + for _, v := range g.uuidNumbers[id].intervals { + if v.min <= number && v.max >= number { + return true, nil + } + } + } + return false, nil +} + +func (g *Gtid) Contain(gtidDesc string) (bool, error) { + return g.contain(gtidDesc) +} + +func (g *Gtid) contain(gtidDesc string) (bool, error) { + if strings.TrimSpace(gtidDesc) == "" { + return true, nil + } + tmpSlice := make([]UUIDNumber, 0) + gtidlists := strings.Split(gtidDesc, ",") + for _, v := range gtidlists { + numbers := strings.Split(v, ":") + if len(numbers) < 2 { + return false, fmt.Errorf("invalid format:%v", v) + } + uuid, err := g.isValidUUID(numbers[0]) + if err != nil { + return false, err + } + num, err := g.uniformNumber(numbers[1:]) + if err != nil { + return false, err + } + tmpSlice = append(tmpSlice, UUIDNumber{ + uuid: uuid, + intervals: num, + }) + } + g.mu.Lock() + defer g.mu.Unlock() + for _, v := range tmpSlice { + id, ok := g.hashMap[v.uuid] + if ok { + if !containRange(g.uuidNumbers[id].intervals, v.intervals) { + return false, nil + } + } else { + return false, nil + } + } + return true, nil +} +func (g *Gtid) Overlap(gtidDesc string) (bool, error) { + return g.overlap(gtidDesc) +} +func (g *Gtid) overlap(gtidDesc string) (bool, error) { + if strings.TrimSpace(gtidDesc) == "" { + if g.String() == "" { + return true, nil + } + return false, nil + } + tmpSlice := make([]UUIDNumber, 0) + gtidlists := strings.Split(gtidDesc, ",") + for _, v := range gtidlists { + numbers := strings.Split(v, ":") + if len(numbers) < 2 { + return false, fmt.Errorf("invalid format:%v", v) + } + uuid, err := g.isValidUUID(numbers[0]) + if err != nil { + return false, err + } + num, err := g.uniformNumber(numbers[1:]) + if err != nil { + return false, err + } + tmpSlice = append(tmpSlice, UUIDNumber{ + uuid: uuid, + intervals: num, + }) + } + g.mu.Lock() + defer g.mu.Unlock() + for _, v := range tmpSlice { + id, ok := g.hashMap[v.uuid] + if ok { + if overlapRange(g.uuidNumbers[id].intervals, v.intervals) { + return true, nil + } + } else { + continue + } + } + return false, nil +} + +func (g *Gtid) Equal(gtidDesc string) (bool, error) { + if g.String() == "" && strings.TrimSpace(gtidDesc) == "" { + return true, nil + } + + equ, err := parse(gtidDesc) + if err != nil { + return false, err + } + return g.String() == equ.String(), nil +} + +func (g *Gtid) Clone() *Gtid { + g.mu.Lock() + defer g.mu.Unlock() + var newGtid = &Gtid{} + newGtid.uuidNumbers = make([]UUIDNumber, len(g.uuidNumbers)) + copy(newGtid.uuidNumbers, g.uuidNumbers) + newGtid.hashMap = make(map[string]int, len(g.hashMap)) + for k, v := range g.hashMap { + newGtid.hashMap[k] = v + } + if g.changed { + newGtid.reform() + } + return newGtid +} + +func (g *Gtid) EventCount() uint64 { + g.mu.Lock() + defer g.mu.Unlock() + if g.changed { + g.reform() + } + var ret uint64 = 0 + for _, uuidNumber := range g.uuidNumbers { + for _, interval := range uuidNumber.intervals { + ret += interval.max - interval.min + 1 + } + } + return ret +} + +func (g *Gtid) EventList() []string { + g.mu.Lock() + defer g.mu.Unlock() + if g.changed { + g.reform() + } + eventList := []string{} + for _, uuidNumber := range g.uuidNumbers { + for _, interval := range uuidNumber.intervals { + for i := interval.min; i <= interval.max; i++ { + eventList = append(eventList, fmt.Sprintf("%s:%d", uuidNumber.uuid, i)) + } + } + } + return eventList +} diff --git a/gtid_test.go b/gtid_test.go new file mode 100644 index 0000000..c51834e --- /dev/null +++ b/gtid_test.go @@ -0,0 +1,424 @@ +package gtid + +import ( + "fmt" + "strconv" + "testing" +) + +func TestGtidEqual(t *testing.T) { + equal, err := Equal( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-30", + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:11-12") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if equal { + t.Fatalf("unexpected error should not equal!") + } + equal, err = Equal( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:11-12", + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-30") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if equal { + t.Fatalf("unexpected error should not equal!") + } + equal, err = Equal( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-30", + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-30") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if !equal { + t.Fatalf("unexpected error should equal!") + } + + equal, err = Equal( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:11-12", + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:11-13") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if equal { + t.Fatalf("unexpected error should not equal!") + } + equal, err = Equal( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:11-13", + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:11-12") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if equal { + t.Fatalf("unexpected error should not equal!") + } + equal, err = Equal( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:11-12,ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-123", + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:11-12") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if equal { + t.Fatalf("unexpected error should not equal!") + } + equal, err = Equal( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:11-12", + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:11-12,ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-123", + ) + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if equal { + t.Fatalf("unexpected error should not equal!") + } + + equal, err = Equal( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:11-13", + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:11-12,ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-123", + ) + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if equal { + t.Fatalf("unexpected error should not equal!") + } + equal, err = Equal( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:11-12,ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-123", + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:11-13", + ) + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if equal { + t.Fatalf("unexpected error should not equal!") + } + equal, err = Equal( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:11-12,ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-123", + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:11-12,14b12195-39b4-11eb-b3a9-005056bf03d8:1-20", + ) + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if equal { + t.Fatalf("unexpected error should not equal!") + } + equal, err = Equal( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:11-12,14b12195-39b4-11eb-b3a9-005056bf03d8:1-20", + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:11-12,ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-123", + ) + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if equal { + t.Fatalf("unexpected error should not equal!") + } + equal, err = Equal( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:11-12,14b12195-39b4-11eb-b3a9-005056bf03d8:1-10,14b12195-39b4-11eb-b3a9-005056bf03d8:11-15,ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-123,14b12195-39b4-11eb-b3a9-005056bf03d8:16-20", + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:11-12,ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-123,14b12195-39b4-11eb-b3a9-005056bf03d8:1-20", + ) + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if !equal { + t.Fatalf("unexpected error should equal!") + } +} + +func TestGtidAdd(t *testing.T) { + gtid, err := Add( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:5-10,ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:7", + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-4,ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-5") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if gtid != "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-10,ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-5:7" { + t.Fatalf("wrong gtid %v", gtid) + } +} +func TestGtidAdd2(t *testing.T) { + gtid, err := Parse( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:5-10,ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:7") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + gtid.Add("ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-4,ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-5") + if gtid.String() != "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-10,ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-5:7" { + t.Fatalf("wrong gtid %v", gtid) + } +} +func TestGtid_AddGtid(t *testing.T) { + src := `14b12195-39b4-11eb-b3a9-005056bf03d8:1-16` + gtid, err := Parse(src) + if err != nil { + panic(err) + } + err = gtid.Add(`14b12195-39b4-11eb-b3a9-005056bf03d8:1-17`) + if err != nil { + panic(err) + } + gtid.Add(`14b12195-39b4-11eb-b3a9-005056bf03d8:19-20`) + fmt.Println(gtid.String()) + if gtid.String() != `14b12195-39b4-11eb-b3a9-005056bf03d8:1-17:19-20` { + t.FailNow() + } +} + +func TestGtid_SubGtid(t *testing.T) { + src := `14b12195-39b4-11eb-b3a9-005056bf03d8:1-16` + gtid, err := Parse(src) + if err != nil { + panic(err) + } + err = gtid.sub(`14b12195-39b4-11eb-b3a9-005056bf03d8:1-12`) + if err != nil { + panic(err) + } + //gtid.Add(`14b12195-39b4-11eb-b3a9-005056bf03d8:19-20`) + fmt.Println(gtid.String()) + if gtid.String() != `14b12195-39b4-11eb-b3a9-005056bf03d8:13-16` { + t.FailNow() + } +} + +func TestSub_1(t *testing.T) { + gtid, err := Sub( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-10:20-30", + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:11-12") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if gtid != "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-10:20-30" { + t.Fatalf("wrong gtid %v", gtid) + } +} + +func TestSub_2(t *testing.T) { + gtid, err := Sub( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-10:20-30", + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:15-40") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if gtid != "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-10" { + t.Fatalf("wrong gtid %v", gtid) + } +} + +func TestSub_3(t *testing.T) { + gtid, err := Sub( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-10:20-30", + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:15-25") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if gtid != "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-10:26-30" { + t.Fatalf("wrong gtid %v", gtid) + } +} + +func TestSub_4(t *testing.T) { + gtid, err := Sub( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-10:20-30", + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:15-30") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if gtid != "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-10" { + t.Fatalf("wrong gtid %v", gtid) + } +} + +func TestSub_5(t *testing.T) { + gtid, err := Sub( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-10:20-30", + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:2-29") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if gtid != "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1:30" { + t.Fatalf("wrong gtid %v", gtid) + } +} + +func TestSub_6(t *testing.T) { + gtid, err := Sub( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-10:20-30", + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-30") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if gtid != "" { + t.Fatalf("wrong gtid %v", gtid) + } +} + +func TestSub_7(t *testing.T) { + gtid, err := Sub( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-10:20-30", + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:5-6:25") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if gtid != "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-4:7-10:20-24:26-30" { + t.Fatalf("wrong gtid %v", gtid) + } +} + +func TestSub_8(t *testing.T) { + gtid, err := Sub( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:20-30, ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-7, ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-10", + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-9:25, ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:6") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if gtid != "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:10:20-24:26-30,ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-5:7" { + t.Fatalf("wrong gtid %v", gtid) + } +} + +func TestSub_empty(t *testing.T) { + gtid, err := Sub( + "", + "") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if gtid != "" { + t.Fatalf("wrong gtid %v", gtid) + } +} + +func Benchmark_SUB8(b *testing.B) { + for i := 0; i < b.N; i++ { + Sub( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:20-30, ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-7, ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-10", + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-9:25, ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:6") + } +} + +func Benchmark_SUB82(b *testing.B) { + p, _ := parse("ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:20-30, ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-7, ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-10") + for i := 0; i < b.N; i++ { + c := *p + c.Sub("ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-9:25, ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:6") + } +} + +func TestContain_pass(t *testing.T) { + contain, err := Contain( + "ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-7,ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-10", + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-4,ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-5") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if !contain { + t.Fatalf("contain should == true") + } +} + +func TestContain_not_pass(t *testing.T) { + contain, err := Contain( + "ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-7,ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:2-10", + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:1-4,ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-5") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if contain { + t.Fatalf("contain should == false") + } +} + +func TestContain_null(t *testing.T) { + contain, err := Contain( + "", + "") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if !contain { + t.Fatalf("contain should == true") + } +} + +func TestGtidOverlap_true(t *testing.T) { + ret, err := Overlap( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:10:20-24:26-30,ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-5:7", + "ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:4") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if !ret { + t.Fatalf("expect true, but got false") + } +} + +func TestGtidOverlap_false(t *testing.T) { + ret, err := Overlap( + "ca8035ea-c5d5-11e3-8ce9-e66ccf50db66:10:20-24:26-30,ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-5:7", + "ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:9") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if ret { + t.Fatalf("expect false, but got true") + } +} + +func TestGtidOverlap_false2(t *testing.T) { + ret, err := Overlap( + "ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-5:7", + "ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-12") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + if !ret { + t.Fatalf("expect true, but got false") + } +} + +func TestGtid(t *testing.T) { + ret, err := Parse("") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + for i := 0; i < 1000000; i++ { + err := ret.AddGtid("ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb", uint64(i+1)) + if err != nil { + panic(err) + } + } + fmt.Println(ret.String()) +} + +func TestGtid2(t *testing.T) { + ret, err := Parse("") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + str := ret.String() + for i := 0; i < 10000000; i++ { + gtid := "ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:" + strconv.Itoa(i+1) + str, err = Add(str, gtid) + if err != nil { + panic(err) + } + } + fmt.Println(str) +} + +func TestGtid_Contain(t *testing.T) { + ret, err := Parse("ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:1-99999,ff92c4da-c5d7-11e3-8cf7-5e10e6a05cf2:1-7867") + if nil != err { + t.Fatalf("unexpected error %v", err) + } + str := ret.String() + for i := 0; i < 10000000; i++ { + //gtid := "ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb:" + strconv.Itoa(i+1) + _, err = ret.ContainGtid("ff92c4da-c5d7-11e3-8cf7-5e10e6a05cfb", uint64(i+1)) //ret.Contain(gtid) + if err != nil { + panic(err) + } + } + fmt.Println(str == ret.String()) +} diff --git a/gtidtool.go b/gtidtool.go new file mode 100644 index 0000000..9793ce6 --- /dev/null +++ b/gtidtool.go @@ -0,0 +1,97 @@ +package gtid + +import ( + "strings" +) + +func Add(gtidDesc0, gtidDesc1 string) (string, error) { + gtid, err := Parse(gtidDesc0 + "," + gtidDesc1) + if nil != err { + return "", err + } + return gtid.String(), nil +} + +func Sub(gtidDesc0, gtidDesc1 string) (string, error) { + if strings.TrimSpace(gtidDesc0) == "" { + return "", nil + } + gtid, err := parse(gtidDesc0) + if err != nil { + return "", err + } + if strings.TrimSpace(gtidDesc1) == "" { + return gtid.String(), nil + } + err = gtid.sub(gtidDesc1) + if err != nil { + return "", err + } + return gtid.String(), nil +} + +func Contain(gtidDesc0, gtidDesc1 string) (bool, error) { + if strings.TrimSpace(gtidDesc0) == "" { + if strings.TrimSpace(gtidDesc1) == "" { + return true, nil + } + return false, nil + } + gtid, err := parse(gtidDesc0) + if err != nil { + return false, err + } + return gtid.Contain(gtidDesc1) +} + +func Overlap(gtidDesc0, gtidDesc1 string) (bool, error) { + if strings.TrimSpace(gtidDesc0) == "" { + if strings.TrimSpace(gtidDesc1) == "" { + return true, nil + } + return false, nil + } + gtid, err := parse(gtidDesc0) + if err != nil { + return false, err + } + return gtid.Overlap(gtidDesc1) +} + +func Equal(gtidDesc0, gtidDesc1 string) (bool, error) { + if strings.TrimSpace(gtidDesc0) == "" { + if strings.TrimSpace(gtidDesc1) == "" { + return true, nil + } + return false, nil + } + gtid, err := parse(gtidDesc0) + if err != nil { + return false, err + } + return gtid.Equal(gtidDesc1) +} + +func EventCount(gtidDesc string) (uint64, error) { + gtid, err := Parse(gtidDesc) + if err != nil { + return 0, err + } + return gtid.EventCount(), nil +} + +func EventList(gtidDesc string) ([]string, error) { + gtid, err := Parse(gtidDesc) + if err != nil { + return nil, err + } + return gtid.EventList(), nil +} + +func Uniform(gtidDesc string) (string, error) { + gtid, err := Parse(gtidDesc) + if err != nil { + return gtidDesc, err + } + return gtid.String(), nil +} diff --git a/struct.go b/struct.go new file mode 100644 index 0000000..3eb6f22 --- /dev/null +++ b/struct.go @@ -0,0 +1,33 @@ +package gtid + +import "sync" + +type Gtid struct { + uuidNumbers []UUIDNumber + hashMap map[string]int + changed bool + mu sync.RWMutex +} + +type UUIDNumber struct { + uuid string + intervals []uuidRange +} + +type uuidRange struct { + min uint64 + max uint64 +} + +type SortUUID []UUIDNumber + +func (s SortUUID) Len() int { return len(s) } +func (s SortUUID) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s SortUUID) Less(i, j int) bool { return s[i].uuid < s[j].uuid } + + +type SortRange []uuidRange + +func (s SortRange) Len() int { return len(s) } +func (s SortRange) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s SortRange) Less(i, j int) bool { return s[i].min < s[j].min }