Skip to content

Commit aead96c

Browse files
PS-10245 feature: Implement receiving binlog events in GTID mode (part 1) (#81)
https://perconadev.atlassian.net/browse/PS-10245 Implemented encoding functions for GTID sets ('binsrv::gtids::gtid_set') that can be used to prepare GTID set payloads passed to the 'gtid_set_arg' when calling 'mysql_binlog_open()'. Similarly to "util/byte_span_extractors.hpp" added new "util/byte_span_inserters.hpp" header that includes helper functions for encoding various primitives (fixed signed / unsigned integers, varlen signed / unsigned integers, packed signed / unsigned integers, byte arrays and spans). Added new 'byte_span_encoding_test' unit test that checks varlen conversions for both signed and unsigned integers of different sizes. 'gtid_set_test' unit test extended with encoding-decoding (roundtrip) checks for untagged, tagged, and mixed GTID sets.
1 parent 4d485d9 commit aead96c

22 files changed

Lines changed: 890 additions & 77 deletions

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,8 @@ set(source_files
298298
src/util/byte_span_fwd.hpp
299299
src/util/byte_span.hpp
300300
src/util/byte_span_extractors.hpp
301+
src/util/byte_span_inserters.hpp
302+
src/util/byte_span_packed_int_constants.hpp
301303

302304
src/util/command_line_helpers_fwd.hpp
303305
src/util/command_line_helpers.hpp

src/binsrv/event/gtid_log_body.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ gtid_log_body::gtid_log_body(util::const_byte_span portion) {
9797
util::extract_fixed_int_from_byte_span(remainder, commit_group_ticket_);
9898
}
9999
}
100-
if (std::size(remainder) != 0U) {
100+
if (!remainder.empty()) {
101101
util::exception_location().raise<std::invalid_argument>(
102102
"extra bytes in the gtid_log event body");
103103
}

src/binsrv/event/gtid_tagged_log_body_impl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ generic_body_impl<code_type::gtid_tagged_log>::generic_body_impl(
134134
last_seen_field_id = field_id;
135135
}
136136

137-
if (std::size(remainder) != 0U) {
137+
if (!remainder.empty()) {
138138
util::exception_location().raise<std::invalid_argument>(
139139
"extra bytes in the gtid_log event body");
140140
}

src/binsrv/gtids/gtid_set.cpp

Lines changed: 119 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <utility>
2727

2828
#include <boost/icl/concept/interval.hpp>
29+
#include <boost/icl/concept/interval_associator.hpp>
2930
#include <boost/icl/concept/interval_set.hpp>
3031

3132
#include "binsrv/gtids/common_types.hpp"
@@ -35,6 +36,7 @@
3536

3637
#include "util/byte_span_extractors.hpp"
3738
#include "util/byte_span_fwd.hpp"
39+
#include "util/byte_span_inserters.hpp"
3840
#include "util/exception_location_helpers.hpp"
3941

4042
namespace binsrv::gtids {
@@ -73,7 +75,7 @@ gtid_set::gtid_set(util::const_byte_span portion) {
7375
// as a <gtid_format>:
7476
// '0' for untagged GTIDs,
7577
// '1' for tagged GTIDs.
76-
// MySQL developers also decided to duplicate this <gtid_format> also in
78+
// MySQL developers also decided to duplicate this <gtid_format> in
7779
// the very first byte (byte 0).
7880
// To sum up, the extraction rules are the following:
7981
// - if the highest byte is equal to '1', extract bytes 1..6 and put them
@@ -90,11 +92,10 @@ gtid_set::gtid_set(util::const_byte_span portion) {
9092
// tagged and untagget encodings)
9193
const auto header_parser{[](std::uint64_t value) {
9294
static constexpr std::size_t format_bit_width{8U};
93-
static constexpr std::size_t format_bit_position{
94-
std::numeric_limits<decltype(value)>::digits - format_bit_width};
9595

96-
const auto gtid_format_field{
97-
static_cast<std::uint8_t>(value >> format_bit_position)};
96+
const auto gtid_format_field{static_cast<std::uint8_t>(
97+
value >>
98+
(std::numeric_limits<std::uint64_t>::digits - format_bit_width))};
9899
// if gtid_format_field is anything but 0 or 1
99100
if ((gtid_format_field >> 1U) != 0U) {
100101
util::exception_location().raise<std::invalid_argument>(
@@ -148,6 +149,118 @@ gtid_set::gtid_set(util::const_byte_span portion) {
148149

149150
process_intervals(remainder, current_uuid, current_tag);
150151
}
152+
153+
if (!remainder.empty()) {
154+
util::exception_location().raise<std::invalid_argument>(
155+
"extra bytes in the encoded gtid_set");
156+
}
157+
}
158+
159+
[[nodiscard]] bool gtid_set::contains_tags() const noexcept {
160+
for (const auto &[current_uuid, current_tagged_gnos] : data_) {
161+
for (const auto &[current_tag, current_gnos] : current_tagged_gnos) {
162+
if (!current_tag.is_empty()) {
163+
return true;
164+
}
165+
}
166+
}
167+
return false;
168+
}
169+
170+
[[nodiscard]] std::size_t gtid_set::calculate_encoded_size() const noexcept {
171+
const auto tagged_flag{contains_tags()};
172+
// 8 bytes for the header (for both tahgged and untagged versions)
173+
std::size_t result{sizeof(std::uint64_t)};
174+
for (const auto &[current_uuid, current_tagged_gnos] : data_) {
175+
for (const auto &[current_tag, current_gnos] : current_tagged_gnos) {
176+
// 16 bytes for UUID
177+
result += uuid::calculate_encoded_size();
178+
if (tagged_flag) {
179+
result += current_tag.calculate_encoded_size();
180+
}
181+
// 8 bytes for the number of intervals
182+
result += sizeof(std::uint64_t);
183+
// 16 bytes for each interval
184+
result +=
185+
boost::icl::interval_count(current_gnos) * 2U * sizeof(std::uint64_t);
186+
}
187+
}
188+
return result;
189+
}
190+
191+
void gtid_set::encode_to(util::byte_span &destination) const {
192+
const auto tagged_flag{contains_tags()};
193+
194+
util::byte_span remainder{destination};
195+
// skipping 8 bytes for the encoded header (number of tsids + tagged flag)
196+
remainder = remainder.subspan(sizeof(std::uint64_t));
197+
198+
// a helper lambda to form encoded GTID set header (supports both
199+
// tagged and untagget encodings)
200+
const auto header_encoder{[](bool tagged, std::size_t number_of_tsids) {
201+
static constexpr std::size_t format_bit_width{8U};
202+
if (!tagged) {
203+
// ensuring that the value is less then 2^56
204+
if (number_of_tsids >=
205+
(1ULL << (std::numeric_limits<std::uint64_t>::digits -
206+
format_bit_width))) {
207+
util::exception_location().raise<std::invalid_argument>(
208+
"the number of TSIDs in the untagged GTID set being encoded is too "
209+
"large");
210+
}
211+
return std::uint64_t{number_of_tsids};
212+
}
213+
214+
// ensuring that the value is less then 2^48
215+
if (number_of_tsids >=
216+
(1ULL << (std::numeric_limits<std::uint64_t>::digits -
217+
2U * format_bit_width))) {
218+
util::exception_location().raise<std::invalid_argument>(
219+
"the number of TSIDs in the tagged GTID set being encoded is too "
220+
"large");
221+
}
222+
// shifting 1 to 48 bits
223+
std::uint64_t result{1ULL << (std::numeric_limits<std::uint64_t>::digits -
224+
2U * format_bit_width)};
225+
result |= std::uint64_t{number_of_tsids};
226+
result <<= format_bit_width;
227+
result |= 1ULL;
228+
return result;
229+
}};
230+
231+
std::size_t number_of_tsids{0ULL};
232+
for (const auto &[current_uuid, current_tagged_gnos] : data_) {
233+
for (const auto &[current_tag, current_gnos] : current_tagged_gnos) {
234+
// 16 bytes for UUID
235+
current_uuid.encode_to(remainder);
236+
if (tagged_flag) {
237+
// varlen bytes for tag size
238+
// 1 byte for each character in the tag
239+
current_tag.encode_to(remainder);
240+
}
241+
// 8 bytes for the number of intervals
242+
util::insert_fixed_int_to_byte_span(
243+
remainder, std::uint64_t{boost::icl::interval_count(current_gnos)});
244+
for (const auto &interval : current_gnos) {
245+
// 16 bytes for each interval
246+
util::insert_fixed_int_to_byte_span(
247+
remainder, std::uint64_t{boost::icl::lower(interval)});
248+
// here we need to uncrement upper bound as we have a half-open interval
249+
// in the encoded representation and use closed interval in
250+
// boost::icl::interval_set
251+
util::insert_fixed_int_to_byte_span(
252+
remainder, std::uint64_t{boost::icl::upper(interval) + 1ULL});
253+
}
254+
++number_of_tsids;
255+
}
256+
}
257+
258+
// writing header
259+
util::byte_span header{destination};
260+
util::insert_fixed_int_to_byte_span(
261+
header, header_encoder(tagged_flag, number_of_tsids));
262+
263+
destination = remainder;
151264
}
152265

153266
[[nodiscard]] bool gtid_set::contains(const gtid &value) const noexcept {
@@ -256,7 +369,7 @@ void gtid_set::process_intervals(util::const_byte_span &remainder,
256369
// TODO: validate that interval boundary values are increasing between
257370
// iterations
258371

259-
// here we need to decrement upper bound as we have a halp-open interval
372+
// here we need to decrement upper bound as we have a half-open interval
260373
// in the encoded representation and use closed interval in the
261374
// gtid_set::add_interval() method
262375
--current_interval_upper;

src/binsrv/gtids/gtid_set.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ class gtid_set {
5353
~gtid_set();
5454

5555
[[nodiscard]] bool is_empty() const noexcept { return data_.empty(); }
56+
[[nodiscard]] bool contains_tags() const noexcept;
57+
58+
[[nodiscard]] std::size_t calculate_encoded_size() const noexcept;
59+
void encode_to(util::byte_span &destination) const;
5660

5761
[[nodiscard]] bool contains(const gtid &value) const noexcept;
5862

src/binsrv/gtids/tag.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
#include "binsrv/gtids/common_types.hpp"
2626

27+
#include "util/byte_span_fwd.hpp"
28+
#include "util/byte_span_inserters.hpp"
2729
#include "util/exception_location_helpers.hpp"
2830

2931
namespace binsrv::gtids {
@@ -63,6 +65,27 @@ tag::tag(std::string_view name) {
6365
}
6466
}
6567

68+
[[nodiscard]] std::size_t tag::calculate_encoded_size() const noexcept {
69+
// varlen bytes for tag size
70+
// 1 byte for each character in the tag
71+
const auto tag_size{get_size()};
72+
return util::calculate_varlen_int_size(tag_size) + tag_size;
73+
}
74+
75+
void tag::encode_to(util::byte_span &destination) const {
76+
util::byte_span remainder{destination};
77+
if (!util::insert_varlen_int_to_byte_span_checked(remainder, get_size())) {
78+
util::exception_location().raise<std::invalid_argument>(
79+
"cannot encode tag length");
80+
}
81+
if (!util::insert_byte_span_to_byte_span_checked(
82+
remainder, util::const_byte_span{data_})) {
83+
util::exception_location().raise<std::invalid_argument>(
84+
"cannot encode tag data");
85+
}
86+
destination = remainder;
87+
}
88+
6689
std::ostream &operator<<(std::ostream &output, const tag &obj) {
6790
return output << obj.get_name();
6891
}

src/binsrv/gtids/tag.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ class tag {
4141

4242
[[nodiscard]] bool is_empty() const noexcept { return data_.empty(); }
4343

44+
[[nodiscard]] std::size_t get_size() const noexcept {
45+
return std::size(data_);
46+
}
47+
48+
[[nodiscard]] std::size_t calculate_encoded_size() const noexcept;
49+
void encode_to(util::byte_span &destination) const;
50+
4451
private:
4552
tag_storage data_{};
4653
};

src/binsrv/gtids/uuid.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "binsrv/gtids/uuid.hpp"
1717

1818
#include <algorithm>
19+
#include <cstddef>
1920
#include <exception>
2021
#include <iterator>
2122
#include <ostream>
@@ -29,6 +30,8 @@
2930

3031
#include "binsrv/gtids/common_types.hpp"
3132

33+
#include "util/byte_span_fwd.hpp"
34+
#include "util/byte_span_inserters.hpp"
3235
#include "util/exception_location_helpers.hpp"
3336

3437
namespace binsrv::gtids {
@@ -56,6 +59,17 @@ uuid::uuid(const uuid_storage &data) {
5659
return boost::uuids::to_string(data_);
5760
}
5861

62+
void uuid::encode_to(util::byte_span &destination) const {
63+
const util::const_byte_span data_span{
64+
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
65+
reinterpret_cast<const std::byte *>(std::begin(data_)),
66+
boost::uuids::uuid::static_size()};
67+
if (!util::insert_byte_span_to_byte_span_checked(destination, data_span)) {
68+
util::exception_location().raise<std::invalid_argument>(
69+
"cannot encode uuid");
70+
}
71+
}
72+
5973
std::ostream &operator<<(std::ostream &output, const uuid &obj) {
6074
return output << obj.str();
6175
}

src/binsrv/gtids/uuid.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
#include "binsrv/gtids/common_types.hpp"
2727

28+
#include "util/byte_span_fwd.hpp"
29+
2830
namespace binsrv::gtids {
2931

3032
class uuid {
@@ -39,6 +41,11 @@ class uuid {
3941

4042
[[nodiscard]] std::string str() const;
4143

44+
[[nodiscard]] static std::size_t calculate_encoded_size() noexcept {
45+
return uuid_length;
46+
}
47+
void encode_to(util::byte_span &destination) const;
48+
4249
[[nodiscard]] friend auto operator<=>(const uuid &first,
4350
const uuid &second) noexcept = default;
4451

src/binsrv/s3_storage_backend.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ s3_storage_backend::aws_context::list_objects(
408408
util::exception_location().raise<std::logic_error>(
409409
"encountered an object with unexpected prefix");
410410
}
411-
key.remove_prefix(prefix_str.size());
411+
key.remove_prefix(std::size(prefix_str));
412412
}
413413
if (!key.empty()) {
414414
result.emplace(key, model_object.GetSize());

0 commit comments

Comments
 (0)