Skip to content
This repository was archived by the owner on Feb 28, 2024. It is now read-only.

Commit bb0ebaf

Browse files
Matthieu`mmontes11
authored andcommitted
Handle wsrep_gtid_mode enabled
- Changed the parsing logic of the seqno field in the Galera state file - Added a struct to serialize/deserialize the GTID tied to the sequence number - Updated the unit tests
1 parent 508bf40 commit bb0ebaf

File tree

8 files changed

+241
-51
lines changed

8 files changed

+241
-51
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/go-chi/chi/v5 v5.0.8
77
github.com/go-chi/httprate v0.7.4
88
github.com/go-logr/logr v1.2.4
9+
github.com/google/go-cmp v0.6.0
910
github.com/google/uuid v1.3.0
1011
go.uber.org/zap v1.25.0
1112
k8s.io/api v0.28.1
@@ -25,7 +26,6 @@ require (
2526
github.com/gogo/protobuf v1.3.2 // indirect
2627
github.com/golang/protobuf v1.5.3 // indirect
2728
github.com/google/gnostic-models v0.6.8 // indirect
28-
github.com/google/go-cmp v0.5.9 // indirect
2929
github.com/google/gofuzz v1.2.0 // indirect
3030
github.com/imdario/mergo v0.3.12 // indirect
3131
github.com/josharian/intern v1.0.0 // indirect

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
3333
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
3434
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
3535
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
36-
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
37-
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
36+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
37+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
3838
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
3939
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
4040
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=

pkg/galera/galera.go

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ type GaleraState struct {
3636
Version string `json:"version"`
3737
UUID string `json:"uuid"`
3838
Seqno int `json:"seqno"`
39+
GTID *GTID `json:"gtid"`
3940
SafeToBootstrap bool `json:"safeToBootstrap"`
4041
}
4142

@@ -60,25 +61,27 @@ func (g *GaleraState) Compare(other GaleraRecoverer) int {
6061
return 0
6162
}
6263

63-
func (g *GaleraState) Marshal() ([]byte, error) {
64+
func (g *GaleraState) MarshalText() ([]byte, error) {
6465
if _, err := guuid.Parse(g.UUID); err != nil {
6566
return nil, fmt.Errorf("invalid uuid: %v", err)
6667
}
6768
type tplOpts struct {
6869
Version string
6970
UUID string
7071
Seqno int
72+
GTID *GTID
7173
SafeToBootstrap int
7274
}
7375
tpl := createTpl("grastate.dat", `version: {{ .Version }}
7476
uuid: {{ .UUID }}
75-
seqno: {{ .Seqno }}
77+
seqno: {{ .Seqno }}{{ if .GTID }},{{ .GTID }}{{ end }}
7678
safe_to_bootstrap: {{ .SafeToBootstrap }}`)
7779
buf := new(bytes.Buffer)
7880
err := tpl.Execute(buf, tplOpts{
7981
Version: g.Version,
8082
UUID: g.UUID,
8183
Seqno: g.Seqno,
84+
GTID: g.GTID,
8285
SafeToBootstrap: func() int {
8386
if g.SafeToBootstrap {
8487
return 1
@@ -92,14 +95,17 @@ safe_to_bootstrap: {{ .SafeToBootstrap }}`)
9295
return buf.Bytes(), nil
9396
}
9497

95-
func (g *GaleraState) Unmarshal(text []byte) error {
98+
func (g *GaleraState) UnmarshalText(text []byte) error {
9699
fileScanner := bufio.NewScanner(bytes.NewReader(text))
97100
fileScanner.Split(bufio.ScanLines)
98101

99-
var version *string
100-
var uuid *string
101-
var seqno *int
102-
var safeToBootstrap *bool
102+
var (
103+
version *string
104+
uuid *string
105+
seqno *int
106+
gtid *GTID
107+
safeToBootstrap *bool
108+
)
103109

104110
for fileScanner.Scan() {
105111
parts := strings.Split(fileScanner.Text(), ":")
@@ -118,7 +124,18 @@ func (g *GaleraState) Unmarshal(text []byte) error {
118124
}
119125
uuid = &value
120126
case "seqno":
121-
i, err := strconv.Atoi(value)
127+
// When the `wsrep_gtid_mode` is set to `ON`, the `seqno` is
128+
// actually a string of the form `seqno,gtid`.
129+
seqnoStr, gtidStr, found := strings.Cut(value, ",")
130+
if found {
131+
gtid = &GTID{}
132+
err := gtid.UnmarshalText([]byte(gtidStr))
133+
if err != nil {
134+
return fmt.Errorf("error parsing gtid: %v", err)
135+
}
136+
137+
}
138+
i, err := strconv.Atoi(seqnoStr)
122139
if err != nil {
123140
return fmt.Errorf("error parsing seqno: %v", err)
124141
}
@@ -141,6 +158,10 @@ func (g *GaleraState) Unmarshal(text []byte) error {
141158
g.Version = *version
142159
g.UUID = *uuid
143160
g.Seqno = *seqno
161+
// Only set the GTID if it was found in the file.
162+
if gtid != nil {
163+
g.GTID = gtid
164+
}
144165
g.SafeToBootstrap = *safeToBootstrap
145166
return nil
146167
}

pkg/galera/galera_test.go

Lines changed: 68 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,26 @@ seqno: -1
6565
safe_to_bootstrap: 0`,
6666
wantErr: false,
6767
},
68+
{
69+
name: "wsrep_gtid_mode enabled",
70+
galeraState: &GaleraState{
71+
Version: "2.1",
72+
UUID: "05f061bd-02a3-11ee-857c-aa370ff6666b",
73+
Seqno: 1,
74+
GTID: &GTID{DomainID: 0, ServerID: 1, SequenceNumber: 2},
75+
SafeToBootstrap: true,
76+
},
77+
want: `version: 2.1
78+
uuid: 05f061bd-02a3-11ee-857c-aa370ff6666b
79+
seqno: 1,0-1-2
80+
safe_to_bootstrap: 1`,
81+
wantErr: false,
82+
},
6883
}
6984

7085
for _, tt := range tests {
7186
t.Run(tt.name, func(t *testing.T) {
72-
bytes, err := tt.galeraState.Marshal()
87+
bytes, err := tt.galeraState.MarshalText()
7388
if tt.wantErr && err == nil {
7489
t.Fatal("error expected, got nil")
7590
}
@@ -93,17 +108,17 @@ func TestGaleraStateUnmarshal(t *testing.T) {
93108
{
94109
name: "empty",
95110
bytes: []byte(`
96-
`),
111+
`),
97112
want: GaleraState{},
98113
wantErr: true,
99114
},
100115
{
101116
name: "comment",
102117
bytes: []byte(`# GALERA saved state
103-
version: 2.1
104-
uuid: 05f061bd-02a3-11ee-857c-aa370ff6666b
105-
seqno: 1
106-
safe_to_bootstrap: 1`),
118+
version: 2.1
119+
uuid: 05f061bd-02a3-11ee-857c-aa370ff6666b
120+
seqno: 1
121+
safe_to_bootstrap: 1`),
107122
want: GaleraState{
108123
Version: "2.1",
109124
UUID: "05f061bd-02a3-11ee-857c-aa370ff6666b",
@@ -115,10 +130,10 @@ safe_to_bootstrap: 1`),
115130
{
116131
name: "indentation",
117132
bytes: []byte(`# GALERA saved state
118-
version: 2.1
119-
uuid: 05f061bd-02a3-11ee-857c-aa370ff6666b
120-
seqno: 1
121-
safe_to_bootstrap: 1`),
133+
version: 2.1
134+
uuid: 05f061bd-02a3-11ee-857c-aa370ff6666b
135+
seqno: 1
136+
safe_to_bootstrap: 1`),
122137
want: GaleraState{
123138
Version: "2.1",
124139
UUID: "05f061bd-02a3-11ee-857c-aa370ff6666b",
@@ -130,39 +145,39 @@ safe_to_bootstrap: 1`),
130145
{
131146
name: "invalid uuid",
132147
bytes: []byte(`# GALERA saved state
133-
version: 2.1
134-
uuid: foo
135-
seqno: -1
136-
safe_to_bootstrap: 1`),
148+
version: 2.1
149+
uuid: foo
150+
seqno: -1
151+
safe_to_bootstrap: 1`),
137152
want: GaleraState{},
138153
wantErr: true,
139154
},
140155
{
141156
name: "invalid seqno",
142157
bytes: []byte(`# GALERA saved state
143-
version: 2.1
144-
uuid: 05f061bd-02a3-11ee-857c-aa370ff6666b
145-
seqno: foo
146-
safe_to_bootstrap: 1`),
158+
version: 2.1
159+
uuid: 05f061bd-02a3-11ee-857c-aa370ff6666b
160+
seqno: foo
161+
safe_to_bootstrap: 1`),
147162
want: GaleraState{},
148163
wantErr: true,
149164
},
150165
{
151166
name: "invalid safe_to_bootstrap",
152167
bytes: []byte(`# GALERA saved state
153-
version: 2.1
154-
uuid: 05f061bd-02a3-11ee-857c-aa370ff6666b
155-
seqno: 1
156-
safe_to_bootstrap: true`),
168+
version: 2.1
169+
uuid: 05f061bd-02a3-11ee-857c-aa370ff6666b
170+
seqno: 1
171+
safe_to_bootstrap: true`),
157172
want: GaleraState{},
158173
wantErr: true,
159174
},
160175
{
161176
name: "safe_to_bootstrap true",
162177
bytes: []byte(`version: 2.1
163-
uuid: 05f061bd-02a3-11ee-857c-aa370ff6666b
164-
seqno: 1
165-
safe_to_bootstrap: 1`),
178+
uuid: 05f061bd-02a3-11ee-857c-aa370ff6666b
179+
seqno: 1
180+
safe_to_bootstrap: 1`),
166181
want: GaleraState{
167182
Version: "2.1",
168183
UUID: "05f061bd-02a3-11ee-857c-aa370ff6666b",
@@ -174,9 +189,9 @@ safe_to_bootstrap: 1`),
174189
{
175190
name: "safe_to_bootstrap false",
176191
bytes: []byte(`version: 2.1
177-
uuid: 05f061bd-02a3-11ee-857c-aa370ff6666b
178-
seqno: 1
179-
safe_to_bootstrap: 0`),
192+
uuid: 05f061bd-02a3-11ee-857c-aa370ff6666b
193+
seqno: 1
194+
safe_to_bootstrap: 0`),
180195
want: GaleraState{
181196
Version: "2.1",
182197
UUID: "05f061bd-02a3-11ee-857c-aa370ff6666b",
@@ -188,9 +203,9 @@ safe_to_bootstrap: 0`),
188203
{
189204
name: "negative seqno",
190205
bytes: []byte(`version: 2.1
191-
uuid: 05f061bd-02a3-11ee-857c-aa370ff6666b
192-
seqno: -1
193-
safe_to_bootstrap: 0`),
206+
uuid: 05f061bd-02a3-11ee-857c-aa370ff6666b
207+
seqno: -1
208+
safe_to_bootstrap: 0`),
194209
want: GaleraState{
195210
Version: "2.1",
196211
UUID: "05f061bd-02a3-11ee-857c-aa370ff6666b",
@@ -202,25 +217,41 @@ safe_to_bootstrap: 0`),
202217
{
203218
name: "missing safe_to_bootstrap",
204219
bytes: []byte(`version: 2.1
205-
uuid: 05f061bd-02a3-11ee-857c-aa370ff6666b
206-
safe_to_bootstrap: 0`),
220+
uuid: 05f061bd-02a3-11ee-857c-aa370ff6666b
221+
safe_to_bootstrap: 0`),
207222
want: GaleraState{},
208223
wantErr: true,
209224
},
210225
{
211226
name: "missing seqno",
212227
bytes: []byte(`version: 2.1
213-
uuid: 05f061bd-02a3-11ee-857c-aa370ff6666b
214-
safe_to_bootstrap: 0`),
228+
uuid: 05f061bd-02a3-11ee-857c-aa370ff6666b
229+
safe_to_bootstrap: 0`),
215230
want: GaleraState{},
216231
wantErr: true,
217232
},
233+
{
234+
// See the following threadh: https://mariadb-operator.slack.com/archives/C056RAECH0W/p1699350363009529
235+
name: "wsrep_gtid_mode enabled",
236+
bytes: []byte(`# GALERA saved state
237+
version: 2.1
238+
uuid: 05f061bd-02a3-11ee-857c-aa370ff6666b
239+
seqno: 1,0-1-2
240+
safe_to_bootstrap: 1`),
241+
want: GaleraState{
242+
Version: "2.1",
243+
UUID: "05f061bd-02a3-11ee-857c-aa370ff6666b",
244+
Seqno: 1,
245+
GTID: &GTID{DomainID: 0, ServerID: 1, SequenceNumber: 2},
246+
SafeToBootstrap: true,
247+
},
248+
},
218249
}
219250

220251
for _, tt := range tests {
221252
t.Run(tt.name, func(t *testing.T) {
222253
var galeraState GaleraState
223-
err := galeraState.Unmarshal(tt.bytes)
254+
err := galeraState.UnmarshalText(tt.bytes)
224255
if tt.wantErr && err == nil {
225256
t.Fatal("error expected, got nil")
226257
}
@@ -412,7 +443,7 @@ Warning: Memory not freed: 280
412443
2023-06-04 8:24:18 0 [Note] Plugin 'FEEDBACK' is disabled.
413444
2023-06-04 8:24:18 0 [Note] Server socket created on IP: '0.0.0.0'.
414445
2023-06-04 8:24:18 0 [Note] WSREP: Recovered position: 08dd3b99-ac6b-46f8-84bd-8cb8f9f949b0:3
415-
Warning: Memory not freed: 280
446+
Warning: Memory not freed: 280
416447
`),
417448
want: Bootstrap{
418449
UUID: "08dd3b99-ac6b-46f8-84bd-8cb8f9f949b0",

pkg/galera/gtid.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package galera
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
"strings"
7+
)
8+
9+
// GTID represents a MariaDB global transaction identifier.
10+
// See: https://mariadb.com/kb/en/gtid/
11+
type GTID struct {
12+
DomainID uint32 `json:"domainId"`
13+
ServerID uint32 `json:"serverId"`
14+
SequenceNumber uint64 `json:"sequenceNumber"`
15+
}
16+
17+
func (gtid *GTID) String() string {
18+
return fmt.Sprintf("%d-%d-%d", gtid.DomainID, gtid.ServerID, gtid.SequenceNumber)
19+
}
20+
21+
func (gtid *GTID) MarshalText() ([]byte, error) {
22+
return []byte(gtid.String()), nil
23+
}
24+
25+
func (gtid *GTID) UnmarshalText(text []byte) error {
26+
parts := strings.Split(string(text), "-")
27+
if len(parts) != 3 {
28+
return fmt.Errorf("invalid gtid: %s", text)
29+
}
30+
domainID, err := strconv.ParseUint(parts[0], 10, 32)
31+
if err != nil {
32+
return fmt.Errorf("invalid domain id: %v", err)
33+
}
34+
serverID, err := strconv.ParseUint(parts[1], 10, 32)
35+
if err != nil {
36+
return fmt.Errorf("invalid server id: %v", err)
37+
}
38+
seqno, err := strconv.ParseUint(parts[2], 10, 64)
39+
if err != nil {
40+
return fmt.Errorf("invalid seqno: %v", err)
41+
}
42+
gtid.DomainID = uint32(domainID)
43+
gtid.ServerID = uint32(serverID)
44+
gtid.SequenceNumber = seqno
45+
return nil
46+
}

0 commit comments

Comments
 (0)