From b2d723a8412cde93000409d2fee746be8c5e12fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Eeden?= Date: Thu, 21 Nov 2024 13:09:54 +0100 Subject: [PATCH 1/7] replication: Support GTID tag in PreviousGTIDsEvent Issue: ref #845 The `PreviousGTIDsEvent` / `PREVIOUS_GTIDS_LOG_EVENT` has changed to work with tagged GTIDs. First the `uuidCount` has changed, it encodes the GTID format. Here format 1 is tagged and format 0 is untagged. Then each entry may have a tag. If there is a tag then the uuid itself isn't printed but the tag is appended to the last entry. Examples: `896e7882-18fe-11ef-ab88-22222d34d411:1-3` regular format, compatible with both formats `896e7882-18fe-11ef-ab88-22222d34d411:1-4:aaaa:1` tagged format. Combination of - `896e7882-18fe-11ef-ab88-22222d34d411:1-4` - `896e7882-18fe-11ef-ab88-22222d34d411:aaaa:1` `896e7882-18fe-11ef-ab88-22222d34d411:1-4:aaaa:1:abc:1-3:bbbbb:1:bbbbbb:1:x:1,896e7882-18fe-11ef-ab88-22222d34d412:1-2` Combination of: ``` 896e7882-18fe-11ef-ab88-22222d34d411:1-4 :aaaa:1 :abc:1-3 :bbbbb:1 :bbbbbb:1 :x:1, 896e7882-18fe-11ef-ab88-22222d34d412:1-2 ``` Please also see: `mysqlbinlog --read-from-remote-server --hexdump $binlogfile` to see how MySQL encodes/decodes this. See also: - https://dev.mysql.com/doc/refman/8.4/en/replication-gtids-concepts.html --- replication/event.go | 57 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/replication/event.go b/replication/event.go index 6fb614f61..599a336b2 100644 --- a/replication/event.go +++ b/replication/event.go @@ -229,15 +229,59 @@ type PreviousGTIDsEvent struct { GTIDSets string } +type GtidFormat int + +const ( + GtidFormatClassic = iota + GtidFormatTagged +) + +func decodeSid(data []byte) (format GtidFormat, sidnr uint64) { + if data[7] == 1 { + format = GtidFormatTagged + } + + if format == GtidFormatTagged { + sid_mask := []byte{0, 255, 255, 255, 255, 255, 255, 0} + + // Apply the mask + for i, _ := range data[:8] { + data[i] &= sid_mask[i] + } + data = append(data, 0) + + // sidnr + sidnr = binary.LittleEndian.Uint64(data[1:]) + return + } + sidnr = binary.LittleEndian.Uint64(data) + return +} + func (e *PreviousGTIDsEvent) Decode(data []byte) error { pos := 0 - uuidCount := binary.LittleEndian.Uint16(data[pos : pos+8]) + + gtidinfo := make([]byte, 8) + copy(gtidinfo, data[:8]) + format, uuidCount := decodeSid(gtidinfo) pos += 8 previousGTIDSets := make([]string, uuidCount) - for i := range previousGTIDSets { + currentSetnr := 0 + for _ = range previousGTIDSets { uuid := e.decodeUuid(data[pos : pos+16]) pos += 16 + isTag := false + var tag string + if format == GtidFormatTagged { + tagLength := int(data[pos]) / 2 + pos += 1 + if tagLength > 0 { + isTag = true + tag = string(data[pos : pos+tagLength]) + pos += tagLength + } + } sliceCount := binary.LittleEndian.Uint16(data[pos : pos+8]) pos += 8 intervals := make([]string, sliceCount) @@ -254,9 +298,14 @@ func (e *PreviousGTIDsEvent) Decode(data []byte) error { } intervals[i] = interval } - previousGTIDSets[i] = fmt.Sprintf("%s:%s", uuid, strings.Join(intervals, ":")) + if isTag { + previousGTIDSets[currentSetnr-1] += fmt.Sprintf(":%s:%s", tag, strings.Join(intervals, ":")) + } else { + previousGTIDSets[currentSetnr] = fmt.Sprintf("%s:%s", uuid, strings.Join(intervals, ":")) + currentSetnr += 1 + } } - e.GTIDSets = strings.Join(previousGTIDSets, ",") + e.GTIDSets = strings.Join(previousGTIDSets[:currentSetnr], ",") return nil } From 03ef43c44b4a1a777dcc1e5b52fc617b2c686c5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Eeden?= Date: Thu, 21 Nov 2024 13:32:05 +0100 Subject: [PATCH 2/7] Add test --- replication/event_test.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/replication/event_test.go b/replication/event_test.go index 1333cd5ef..7dacb2539 100644 --- a/replication/event_test.go +++ b/replication/event_test.go @@ -140,3 +140,22 @@ func TestIntVarEvent(t *testing.T) { require.Equal(t, INSERT_ID, ev.Type) require.Equal(t, uint64(23), ev.Value) } + +func TestDecodeSid(t *testing.T) { + testcases := []struct { + input []byte + gtidFormat GtidFormat + uuidCount uint64 + }{ + {[]byte{1, 2, 0, 0, 0, 0, 0, 1}, GtidFormatTagged, 2}, + {[]byte{1, 1, 0, 0, 0, 0, 0, 1}, GtidFormatTagged, 1}, + {[]byte{1, 0, 0, 0, 0, 0, 0, 1}, GtidFormatTagged, 0}, + {[]byte{1, 0, 0, 0, 0, 0, 0, 0}, GtidFormatClassic, 1}, + } + + for _, tc := range testcases { + format, uuidCount := decodeSid(tc.input) + assert.Equal(t, tc.gtidFormat, format) + assert.Equal(t, tc.uuidCount, uuidCount) + } +} From 4a647d04e622d3974f20ed0b583d570d9902e9de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Eeden?= Date: Thu, 21 Nov 2024 13:48:28 +0100 Subject: [PATCH 3/7] Add more tests --- replication/event_test.go | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/replication/event_test.go b/replication/event_test.go index 7dacb2539..91184412c 100644 --- a/replication/event_test.go +++ b/replication/event_test.go @@ -159,3 +159,34 @@ func TestDecodeSid(t *testing.T) { assert.Equal(t, tc.uuidCount, uuidCount) } } + +func TestPreviousGTIDEvent(t *testing.T) { + testcases := []struct { + input []byte + GTIDSets string + }{ + { + []byte{0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1}, + "", + }, + { + []byte{0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x89, 0x6e, 0x78, 0x82, 0x18, 0xfe, 0x11, 0xef, 0xab, 0x88, 0x22, 0x22, 0x2d, 0x34, 0xd4, 0x11, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + "896e7882-18fe-11ef-ab88-22222d34d411:1-3", + }, + { + []byte{0x1, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x89, 0x6e, 0x78, 0x82, 0x18, 0xfe, 0x11, 0xef, 0xab, 0x88, 0x22, 0x22, 0x2d, 0x34, 0xd4, 0x11, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x89, 0x6e, 0x78, 0x82, 0x18, 0xfe, 0x11, 0xef, 0xab, 0x88, 0x22, 0x22, 0x2d, 0x34, 0xd4, 0x11, 0x8, 0x61, 0x61, 0x61, 0x61, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + "896e7882-18fe-11ef-ab88-22222d34d411:1-4:aaaa:1", + }, + { + []byte{0x1, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x89, 0x6e, 0x78, 0x82, 0x18, 0xfe, 0x11, 0xef, 0xab, 0x88, 0x22, 0x22, 0x2d, 0x34, 0xd4, 0x11, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x89, 0x6e, 0x78, 0x82, 0x18, 0xfe, 0x11, 0xef, 0xab, 0x88, 0x22, 0x22, 0x2d, 0x34, 0xd4, 0x11, 0x8, 0x61, 0x61, 0x61, 0x61, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x89, 0x6e, 0x78, 0x82, 0x18, 0xfe, 0x11, 0xef, 0xab, 0x88, 0x22, 0x22, 0x2d, 0x34, 0xd4, 0x11, 0x6, 0x61, 0x62, 0x63, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x89, 0x6e, 0x78, 0x82, 0x18, 0xfe, 0x11, 0xef, 0xab, 0x88, 0x22, 0x22, 0x2d, 0x34, 0xd4, 0x11, 0xa, 0x62, 0x62, 0x62, 0x62, 0x62, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x89, 0x6e, 0x78, 0x82, 0x18, 0xfe, 0x11, 0xef, 0xab, 0x88, 0x22, 0x22, 0x2d, 0x34, 0xd4, 0x11, 0xc, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x89, 0x6e, 0x78, 0x82, 0x18, 0xfe, 0x11, 0xef, 0xab, 0x88, 0x22, 0x22, 0x2d, 0x34, 0xd4, 0x11, 0x2, 0x78, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x89, 0x6e, 0x78, 0x82, 0x18, 0xfe, 0x11, 0xef, 0xab, 0x88, 0x22, 0x22, 0x2d, 0x34, 0xd4, 0x12, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + "896e7882-18fe-11ef-ab88-22222d34d411:1-4:aaaa:1:abc:1-3:bbbbb:1:bbbbbb:1:x:1,896e7882-18fe-11ef-ab88-22222d34d412:1-2", + }, + } + + for _, tc := range testcases { + e := PreviousGTIDsEvent{} + err := e.Decode(tc.input) + require.NoError(t, err) + require.Equal(t, tc.GTIDSets, e.GTIDSets) + } +} From ea2270af43c1b8d747d7440514aa8072c77d2177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Eeden?= Date: Wed, 27 Nov 2024 16:45:24 +0100 Subject: [PATCH 4/7] Update replication/event.go Co-authored-by: lance6716 --- replication/event.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/replication/event.go b/replication/event.go index 599a336b2..4d900fea9 100644 --- a/replication/event.go +++ b/replication/event.go @@ -268,7 +268,7 @@ func (e *PreviousGTIDsEvent) Decode(data []byte) error { previousGTIDSets := make([]string, uuidCount) currentSetnr := 0 - for _ = range previousGTIDSets { + for range previousGTIDSets { uuid := e.decodeUuid(data[pos : pos+16]) pos += 16 isTag := false From 944fbdd5c9c1ec40df6f8dea6ffc78e460ff77fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Eeden?= Date: Tue, 3 Dec 2024 10:35:26 +0100 Subject: [PATCH 5/7] Update based on review --- replication/event.go | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/replication/event.go b/replication/event.go index 4d900fea9..20c7b0c1f 100644 --- a/replication/event.go +++ b/replication/event.go @@ -236,8 +236,20 @@ const ( GtidFormatTagged ) +// Decode the number of sids (source identifiers) and if it is using +// tagged GTIDs or classic (non-tagged) GTIDs. +// +// Note that each gtid tag increases the sidno here, so a single UUID +// might turn up multiple times if there are multipl tags. +// +// see also: +// decode_nsids_format in mysql/mysql-server +// https://github.com/mysql/mysql-server/blob/61a3a1d8ef15512396b4c2af46e922a19bf2b174/sql/rpl_gtid_set.cc#L1363-L1378 func decodeSid(data []byte) (format GtidFormat, sidnr uint64) { - if data[7] == 1 { + gtidinfo := make([]byte, 8) + copy(gtidinfo, data[:8]) + + if gtidinfo[7] == 1 { format = GtidFormatTagged } @@ -245,25 +257,23 @@ func decodeSid(data []byte) (format GtidFormat, sidnr uint64) { sid_mask := []byte{0, 255, 255, 255, 255, 255, 255, 0} // Apply the mask - for i, _ := range data[:8] { - data[i] &= sid_mask[i] + for i, _ := range gtidinfo[:8] { + gtidinfo[i] &= sid_mask[i] } - data = append(data, 0) + gtidinfo = append(gtidinfo, 0) // sidnr - sidnr = binary.LittleEndian.Uint64(data[1:]) + sidnr = binary.LittleEndian.Uint64(gtidinfo[1:]) return } - sidnr = binary.LittleEndian.Uint64(data) + sidnr = binary.LittleEndian.Uint64(gtidinfo) return } func (e *PreviousGTIDsEvent) Decode(data []byte) error { pos := 0 - gtidinfo := make([]byte, 8) - copy(gtidinfo, data[:8]) - format, uuidCount := decodeSid(gtidinfo) + format, uuidCount := decodeSid(data) pos += 8 previousGTIDSets := make([]string, uuidCount) @@ -276,7 +286,7 @@ func (e *PreviousGTIDsEvent) Decode(data []byte) error { if format == GtidFormatTagged { tagLength := int(data[pos]) / 2 pos += 1 - if tagLength > 0 { + if tagLength > 0 { // 0 == no tag, >0 == tag isTag = true tag = string(data[pos : pos+tagLength]) pos += tagLength From 3e5951141f188ab6b46af11b78ddec7bb1a663f8 Mon Sep 17 00:00:00 2001 From: lance6716 Date: Mon, 16 Dec 2024 15:51:16 +0800 Subject: [PATCH 6/7] add my suggestions Signed-off-by: lance6716 --- replication/event.go | 54 +++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/replication/event.go b/replication/event.go index 20c7b0c1f..c151f32ba 100644 --- a/replication/event.go +++ b/replication/event.go @@ -246,27 +246,17 @@ const ( // decode_nsids_format in mysql/mysql-server // https://github.com/mysql/mysql-server/blob/61a3a1d8ef15512396b4c2af46e922a19bf2b174/sql/rpl_gtid_set.cc#L1363-L1378 func decodeSid(data []byte) (format GtidFormat, sidnr uint64) { - gtidinfo := make([]byte, 8) - copy(gtidinfo, data[:8]) - - if gtidinfo[7] == 1 { + if data[7] == 1 { format = GtidFormatTagged } if format == GtidFormatTagged { - sid_mask := []byte{0, 255, 255, 255, 255, 255, 255, 0} - - // Apply the mask - for i, _ := range gtidinfo[:8] { - gtidinfo[i] &= sid_mask[i] - } - gtidinfo = append(gtidinfo, 0) - - // sidnr - sidnr = binary.LittleEndian.Uint64(gtidinfo[1:]) + masked := make([]byte, 8) + copy(masked, data[1:7]) + sidnr = binary.LittleEndian.Uint64(masked) return } - sidnr = binary.LittleEndian.Uint64(gtidinfo) + sidnr = binary.LittleEndian.Uint64(data[:8]) return } @@ -277,45 +267,53 @@ func (e *PreviousGTIDsEvent) Decode(data []byte) error { pos += 8 previousGTIDSets := make([]string, uuidCount) + currentSetnr := 0 + var buf strings.Builder for range previousGTIDSets { uuid := e.decodeUuid(data[pos : pos+16]) pos += 16 - isTag := false var tag string if format == GtidFormatTagged { tagLength := int(data[pos]) / 2 pos += 1 if tagLength > 0 { // 0 == no tag, >0 == tag - isTag = true tag = string(data[pos : pos+tagLength]) pos += tagLength } } + + if len(tag) > 0 { + buf.WriteString(":") + buf.WriteString(tag) + } else { + if currentSetnr != 0 { + buf.WriteString(",") + } + buf.WriteString(uuid) + currentSetnr += 1 + } + sliceCount := binary.LittleEndian.Uint16(data[pos : pos+8]) pos += 8 - intervals := make([]string, sliceCount) - for i := range intervals { + for range sliceCount { + buf.WriteString(":") + start := e.decodeInterval(data[pos : pos+8]) pos += 8 stop := e.decodeInterval(data[pos : pos+8]) pos += 8 - interval := "" if stop == start+1 { - interval = fmt.Sprintf("%d", start) + fmt.Fprintf(&buf, "%d", start) } else { - interval = fmt.Sprintf("%d-%d", start, stop-1) + fmt.Fprintf(&buf, "%d-%d", start, stop-1) } - intervals[i] = interval } - if isTag { - previousGTIDSets[currentSetnr-1] += fmt.Sprintf(":%s:%s", tag, strings.Join(intervals, ":")) - } else { - previousGTIDSets[currentSetnr] = fmt.Sprintf("%s:%s", uuid, strings.Join(intervals, ":")) + if len(tag) == 0 { currentSetnr += 1 } } - e.GTIDSets = strings.Join(previousGTIDSets[:currentSetnr], ",") + e.GTIDSets = buf.String() return nil } From 5599af2e15afa0cd4d50405bb3a6fed0bf293ae5 Mon Sep 17 00:00:00 2001 From: lance6716 Date: Tue, 17 Dec 2024 16:15:08 +0800 Subject: [PATCH 7/7] add test for long tag Signed-off-by: lance6716 --- replication/event_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/replication/event_test.go b/replication/event_test.go index 91184412c..d7fa43927 100644 --- a/replication/event_test.go +++ b/replication/event_test.go @@ -181,6 +181,10 @@ func TestPreviousGTIDEvent(t *testing.T) { []byte{0x1, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x89, 0x6e, 0x78, 0x82, 0x18, 0xfe, 0x11, 0xef, 0xab, 0x88, 0x22, 0x22, 0x2d, 0x34, 0xd4, 0x11, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x89, 0x6e, 0x78, 0x82, 0x18, 0xfe, 0x11, 0xef, 0xab, 0x88, 0x22, 0x22, 0x2d, 0x34, 0xd4, 0x11, 0x8, 0x61, 0x61, 0x61, 0x61, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x89, 0x6e, 0x78, 0x82, 0x18, 0xfe, 0x11, 0xef, 0xab, 0x88, 0x22, 0x22, 0x2d, 0x34, 0xd4, 0x11, 0x6, 0x61, 0x62, 0x63, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x89, 0x6e, 0x78, 0x82, 0x18, 0xfe, 0x11, 0xef, 0xab, 0x88, 0x22, 0x22, 0x2d, 0x34, 0xd4, 0x11, 0xa, 0x62, 0x62, 0x62, 0x62, 0x62, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x89, 0x6e, 0x78, 0x82, 0x18, 0xfe, 0x11, 0xef, 0xab, 0x88, 0x22, 0x22, 0x2d, 0x34, 0xd4, 0x11, 0xc, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x89, 0x6e, 0x78, 0x82, 0x18, 0xfe, 0x11, 0xef, 0xab, 0x88, 0x22, 0x22, 0x2d, 0x34, 0xd4, 0x11, 0x2, 0x78, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x89, 0x6e, 0x78, 0x82, 0x18, 0xfe, 0x11, 0xef, 0xab, 0x88, 0x22, 0x22, 0x2d, 0x34, 0xd4, 0x12, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, "896e7882-18fe-11ef-ab88-22222d34d411:1-4:aaaa:1:abc:1-3:bbbbb:1:bbbbbb:1:x:1,896e7882-18fe-11ef-ab88-22222d34d412:1-2", }, + { + []byte{0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x2f, 0x20, 0xcc, 0xbc, 0x4c, 0x11, 0xef, 0xa1, 0xd0, 0x02, 0x42, 0xac, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x2f, 0x20, 0xcc, 0xbc, 0x4c, 0x11, 0xef, 0xa1, 0xd0, 0x02, 0x42, 0xac, 0x11, 0x00, 0x02, 0x06, 0x61, 0x61, 0x61, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x2f, 0x20, 0xcc, 0xbc, 0x4c, 0x11, 0xef, 0xa1, 0xd0, 0x02, 0x42, 0xac, 0x11, 0x00, 0x02, 0x28, 0x74, 0x61, 0x67, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x2f, 0x20, 0xcc, 0xbc, 0x4c, 0x11, 0xef, 0xa1, 0xd0, 0x02, 0x42, 0xac, 0x11, 0x00, 0x02, 0x40, 0x74, 0x61, 0x67, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + "042f20cc-bc4c-11ef-a1d0-0242ac110002:1-7:aaa:1:tag45678901234567890:1:tag45678901234567890123456789012:1", + }, } for _, tc := range testcases {