Skip to content

Commit 181f94a

Browse files
author
Venkatesh Prasad
authored
Merge pull request #39 from venkatesh-prasad-v/DISTMYSQL-412
DISTMYSQL 412: Orchestrator should handle tagged GTIDs
2 parents 819ba20 + 52e45cf commit 181f94a

File tree

8 files changed

+577
-34
lines changed

8 files changed

+577
-34
lines changed

go/inst/oracle_gtid_set_entry.go

Lines changed: 203 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -24,51 +24,228 @@ import (
2424
)
2525

2626
var (
27+
// Regex Pattern to match a Singe Value Interval e.g. 1 in uuid:1
2728
singleValueInterval = regexp.MustCompile("^([0-9]+)$")
28-
multiValueInterval = regexp.MustCompile("^([0-9]+)[-]([0-9]+)$")
29+
30+
// Regex Pattern to match a Multi Value Interval e.g. 1-5 in uuid:1-5
31+
multiValueInterval = regexp.MustCompile("^([0-9]+)[-]([0-9]+)$")
32+
33+
// Regex Pattern to match GTID tags
34+
// Tag must start with a letter e.g. tag1 in uuid:tag1:1-5
35+
tagRegex = regexp.MustCompile("^[a-z_][a-z0-9_]{0,31}$")
2936
)
3037

38+
// The below struct represents a tagged interval in GTID set.
39+
// It is private and should not be exported outside this package.
40+
type tagInterval struct {
41+
Tag string // tag name
42+
Interval []string // intervals
43+
}
44+
3145
// OracleGtidSetEntry represents an entry in a set of GTID ranges,
32-
// for example, the entry: "316d193c-70e5-11e5-adb2-ecf4bb2262ff:1-8935:8984-6124596" (may include gaps)
46+
// for example,
47+
//
48+
// Valid Formats:
49+
// - "316d193c-70e5-11e5-adb2-ecf4bb2262ff:1-8935:8984-6124596" (may include gaps)
50+
// - "321f5c0d-70e5-11e5-adb2-ecf4bb2262ff:1-56457" (no gaps)
51+
// - "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-10539:tag1:1-2474" (tagged intervals)
52+
// - "230ea8ea-81e3-11e4-972a-e25ec4bd140a:1-5139:5780-6317:tag1:1-2474:3201-4157" (tagged intervals and may have gaps)
3353
type OracleGtidSetEntry struct {
34-
UUID string
35-
Ranges string
54+
UUID string
55+
DefaultIv string // default (untagged) interval
56+
TaggedIv []tagInterval // tagged intervals
3657
}
3758

38-
// NewOracleGtidSetEntry parses a single entry text
39-
func NewOracleGtidSetEntry(gtidRangeString string) (*OracleGtidSetEntry, error) {
40-
gtidRangeString = strings.TrimSpace(gtidRangeString)
41-
tokens := strings.SplitN(gtidRangeString, ":", 2)
42-
if len(tokens) != 2 {
59+
func ParseOracleGtidSetEntry(gtidRangeString string) (*OracleGtidSetEntry, error) {
60+
61+
// Split the string into two parts: UUID part and the non-UUID part
62+
gtid_str := strings.SplitN(gtidRangeString, ":", 2)
63+
64+
// Sanity check
65+
if len(gtid_str) != 2 {
4366
return nil, fmt.Errorf("Cannot parse OracleGtidSetEntry from %s", gtidRangeString)
4467
}
45-
if tokens[0] == "" {
46-
return nil, fmt.Errorf("Unexpected UUID: %s", tokens[0])
68+
69+
if gtid_str[0] == "" {
70+
return nil, fmt.Errorf("Unexpected UUID: %s", gtid_str[0])
71+
}
72+
73+
if gtid_str[1] == "" {
74+
return nil, fmt.Errorf("Unexpected GTID range: %s", gtid_str[1])
75+
}
76+
77+
// UUID is the first part
78+
uuid := gtid_str[0]
79+
80+
// Split the non-UUID parts into multiple blocks
81+
s := strings.SplitN(gtid_str[1], ":", -1)
82+
83+
var default_iv string // Default interval
84+
var tag_ivs []tagInterval // Full tagged interval
85+
var tip *tagInterval = nil // Current tag interval
86+
87+
// Tagged intervals always follow untagged ones
88+
// so once tip != nil it will never be nil again
89+
for i := range s {
90+
// If it is a GTID tag
91+
if tagRegex.MatchString(s[i]) {
92+
if tip != nil && (tip.Tag != "") && (len(tip.Interval) == 0) {
93+
// If the tag is already set and we got another tag
94+
return nil, fmt.Errorf("Invalid format: Found a tag without any intervals")
95+
} else if tip != nil && (tip.Tag == "") && (len(tip.Interval) != 0) {
96+
// Should never happen - just in case
97+
return nil, fmt.Errorf("Invalid format")
98+
} else {
99+
// Now process the new tag
100+
ti := tagInterval{
101+
Tag: s[i],
102+
}
103+
// Append the new tag to tag_ivs
104+
tag_ivs = append(tag_ivs, ti)
105+
tip = &tag_ivs[len(tag_ivs)-1]
106+
}
107+
} else {
108+
// If it is a GTID interval
109+
if singleValueInterval.MatchString(s[i]) || multiValueInterval.MatchString(s[i]) {
110+
// If it is an empty tag, add it to default interval
111+
if tip == nil {
112+
default_iv += ":" + s[i]
113+
} else {
114+
// If tag is already set, add it to the tag interval
115+
tip.Interval = append(tip.Interval, s[i])
116+
}
117+
} else {
118+
// Regex failed, invalid format
119+
return nil, fmt.Errorf("Invalid format")
120+
}
121+
}
47122
}
48-
if tokens[1] == "" {
49-
return nil, fmt.Errorf("Unexpected GTID range: %s", tokens[1])
123+
// If the interval of the last tag is empty, then it is an invalid format
124+
// eg: "UUID:1-5139::tag1:"
125+
if tip != nil && (tip.Tag != "") && (len(tip.Interval) == 0) {
126+
return nil, fmt.Errorf("Invalid format: Found a tag without any intervals")
127+
}
128+
// Don't append ':' for the first interval in the default set
129+
default_iv, _ = strings.CutPrefix(default_iv, ":")
130+
entry := OracleGtidSetEntry{UUID: uuid, DefaultIv: default_iv, TaggedIv: tag_ivs}
131+
return &entry, nil
132+
}
133+
134+
// NewOracleGtidSetEntry parses a single entry text
135+
func NewOracleGtidSetEntry(gtidRangeString string) (*OracleGtidSetEntry, error) {
136+
gtidRangeString = strings.TrimSpace(gtidRangeString)
137+
138+
gtidRange, error := ParseOracleGtidSetEntry(gtidRangeString)
139+
if gtidRange == nil {
140+
return nil, error
50141
}
51-
gtidRange := &OracleGtidSetEntry{UUID: tokens[0], Ranges: tokens[1]}
142+
52143
return gtidRange, nil
53144
}
54145

55-
// String returns a user-friendly string representation of this entry
146+
// String() returns a user-friendly string representation of this entry
56147
func (this *OracleGtidSetEntry) String() string {
57-
return fmt.Sprintf("%s:%s", this.UUID, this.Ranges)
148+
149+
var res string
150+
151+
// UUID is always added in the beginning of the Gtid_set
152+
res += this.UUID
153+
154+
// Default ranges are always added immediately after the UUID
155+
if len(this.DefaultIv) != 0 {
156+
res += ":" + this.DefaultIv
157+
}
158+
159+
// Tagged ranges are added in the end
160+
for _, v := range this.TaggedIv {
161+
res += ":" + v.Tag
162+
if len(v.Interval) != 0 {
163+
res += ":" + strings.Join(v.Interval, ":")
164+
}
165+
}
166+
return res
58167
}
59168

60-
// String returns a user-friendly string representation of this entry
169+
/*
170+
Explode() returns a list of individual gtids that are represented by this entry.
171+
172+
Example:
173+
Explode of the GTID set "48ebed33-0d12-11ef-a3ec-ac198e4551c8:1-3:7:tag1:1-2:10-12:tag2:74-75:78:81"
174+
shall return the following
175+
176+
48ebed33-0d12-11ef-a3ec-ac198e4551c8:1
177+
48ebed33-0d12-11ef-a3ec-ac198e4551c8:2
178+
48ebed33-0d12-11ef-a3ec-ac198e4551c8:3
179+
48ebed33-0d12-11ef-a3ec-ac198e4551c8:7
180+
48ebed33-0d12-11ef-a3ec-ac198e4551c8:tag1:1
181+
48ebed33-0d12-11ef-a3ec-ac198e4551c8:tag1:2
182+
48ebed33-0d12-11ef-a3ec-ac198e4551c8:tag1:10
183+
48ebed33-0d12-11ef-a3ec-ac198e4551c8:tag1:11
184+
48ebed33-0d12-11ef-a3ec-ac198e4551c8:tag1:12
185+
48ebed33-0d12-11ef-a3ec-ac198e4551c8:tag2:74
186+
48ebed33-0d12-11ef-a3ec-ac198e4551c8:tag2:75
187+
48ebed33-0d12-11ef-a3ec-ac198e4551c8:tag2:78
188+
48ebed33-0d12-11ef-a3ec-ac198e4551c8:tag2:81
189+
*/
61190
func (this *OracleGtidSetEntry) Explode() (result [](*OracleGtidSetEntry)) {
62-
intervals := strings.Split(this.Ranges, ":")
63-
for _, interval := range intervals {
64-
if submatch := multiValueInterval.FindStringSubmatch(interval); submatch != nil {
65-
intervalStart, _ := strconv.Atoi(submatch[1])
66-
intervalEnd, _ := strconv.Atoi(submatch[2])
67-
for i := intervalStart; i <= intervalEnd; i++ {
68-
result = append(result, &OracleGtidSetEntry{UUID: this.UUID, Ranges: fmt.Sprintf("%d", i)})
191+
192+
// Appends the default interval to the result
193+
var AppendDefaultInterval = func(this *OracleGtidSetEntry) {
194+
intervals := strings.Split(this.DefaultIv, ":")
195+
for _, interval := range intervals {
196+
// Multi-value interval
197+
if submatch := multiValueInterval.FindStringSubmatch(interval); submatch != nil {
198+
intervalStart, _ := strconv.Atoi(submatch[1])
199+
intervalEnd, _ := strconv.Atoi(submatch[2])
200+
for i := intervalStart; i <= intervalEnd; i++ {
201+
result = append(result, &OracleGtidSetEntry{UUID: this.UUID, DefaultIv: fmt.Sprintf("%d", i)})
202+
}
203+
} else if submatch := singleValueInterval.FindStringSubmatch(interval); submatch != nil {
204+
// Single-value interval
205+
result = append(result, &OracleGtidSetEntry{UUID: this.UUID, DefaultIv: interval})
69206
}
70-
} else if submatch := singleValueInterval.FindStringSubmatch(interval); submatch != nil {
71-
result = append(result, &OracleGtidSetEntry{UUID: this.UUID, Ranges: interval})
207+
}
208+
}
209+
210+
// Appends tagged intervals to the result
211+
var AppendTaggedInterval = func(tag string, interval string) {
212+
intervals := strings.Split(interval, ":")
213+
for _, interval := range intervals {
214+
// Multi-value interval
215+
if submatch := multiValueInterval.FindStringSubmatch(interval); submatch != nil {
216+
intervalStart, _ := strconv.Atoi(submatch[1])
217+
intervalEnd, _ := strconv.Atoi(submatch[2])
218+
for i := intervalStart; i <= intervalEnd; i++ {
219+
220+
ti := tagInterval{
221+
Tag: tag,
222+
Interval: []string{fmt.Sprintf("%d", i)}}
223+
taggedIv := []tagInterval{ti}
224+
225+
entry := OracleGtidSetEntry{UUID: this.UUID, TaggedIv: taggedIv}
226+
result = append(result, &entry)
227+
}
228+
} else if submatch := singleValueInterval.FindStringSubmatch(interval); submatch != nil {
229+
230+
// Single-value interval
231+
ti := tagInterval{
232+
Tag: tag,
233+
Interval: []string{interval}}
234+
taggedIv := []tagInterval{ti}
235+
236+
entry := OracleGtidSetEntry{UUID: this.UUID, TaggedIv: taggedIv}
237+
result = append(result, &entry)
238+
}
239+
}
240+
}
241+
242+
// Process default interval first
243+
AppendDefaultInterval(this)
244+
245+
// Process tagged intervals next
246+
for _, v := range this.TaggedIv {
247+
for _, iv := range v.Interval {
248+
AppendTaggedInterval(v.Tag, iv)
72249
}
73250
}
74251
return result

0 commit comments

Comments
 (0)