Skip to content

Commit 38084d8

Browse files
PS-11080 fix: Spoiled logical clock information in rewritten binlog (part 5) (#137)
https://perconadev.atlassian.net/browse/PS-11080 We now require that if we enable "rewrite" in gtid-based replication mode, all the events coming from the MySQL server should include checksums (should be generated on the server with '@@global.binlog_checksum' set to 'CRC32'). We detect a violation of this rule during the inspection of the FORMAT_DESCRIPTION event, report an error, and terminate the process with an error status code. This limitation is required for the scenario when MySQL server has one binlog file with checksums enabled and another one with checksums disabled and we need to combine them on the PBS side into a single one. To do this we need to add / remove checksums from one group of events to make sure that all the events in the rewritten binlog file have the same footer (either with or without checksum). However, this cannot be easily done, because adding / removing footers to events changes their sizes which in turn should be reflected in the 'transaction_length' field of the GTID event preceding them. In other words, we cannot recalculate this 'transaction_length' on the fly without receiving all events in its transaction. Added new 'binlog_streaming.gtid_rewrite_enforce_checksums' MTR test case with 2 combinations: * we start with checksums enabled and rotate binlog to a new file with checksums disabled * we start with checksums disabled and rotate binlog to a new file with checksums enabled In both cases, we check that Binlog Server utility identifies the problem, logs an error and terminates with an error status code.
1 parent 4d3ebb5 commit 38084d8

5 files changed

Lines changed: 145 additions & 1 deletion

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ Note: you should specify either `<connection.host>` / `<connection.port>` pair o
403403
- `<replication.mode>` - the replication mode, can be either `position` for position-based replication or `gtid` for GTID-based replication.
404404

405405
#### \<replication.rewrite\> section
406-
If this section is present, then the utility will not split binlog events the same way as they were on the original MySQL server. Instead, it will generate its own binlog file name sequence (based on the `<replication.rewrite.base_file_name>`) and will change to a new binary log file when the size of the previous one riches the specified value (`<replication.rewrite.file_size>`). Having this section requires `<replication.mode>` to be set to `gtid`.
406+
If this section is present, then the utility will not split binlog events the same way as they were on the original MySQL server. Instead, it will generate its own binlog file name sequence (based on the `<replication.rewrite.base_file_name>`) and will change to a new binary log file when the size of the previous one riches the specified value (`<replication.rewrite.file_size>`). Having this section requires `<replication.mode>` to be set to `gtid`. Also, please notice that currently the utility can properly operate in 'rewrite' mode only when all binlog events received from the MySQL server have checksums (were generated on a server that had '@@global.binlog_checksum' set to 'CRC32').
407407
- `<replication.rewrite.base_file_name>` - the base name of the generated binlog file names in the "rewrite" mode. E.g. `rewritten_binlog` will cause `rewritten_binlog.000001`, `rewritten_binlog.000002`, etc. file names to be generated.
408408
- `<replication.rewrite.file_size>` - the maximum individual binlog file size after reaching which the utility will switch to a new one. The value is expected to be a string containing an integer followed by an optional suffix 'K' / 'M' / 'G' / 'T' / 'P', e.g. /\d+\[KMGTP\]?/. The minimal allowed value of this parameter is `1024` bytes.
409409

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
*** Resetting replication at the very beginning of the test.
2+
3+
*** Generating a configuration file in JSON format for the Binlog
4+
*** Server utility.
5+
6+
*** Determining binlog file directory from the server.
7+
8+
*** Creating a temporary directory <BINSRV_STORAGE_PATH> for storing
9+
*** binlog files downloaded via the Binlog Server utility.
10+
11+
*** Creating a simple table and filling it with some data.
12+
CREATE TABLE t1(id INT UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY(id)) ENGINE=InnoDB;
13+
INSERT INTO t1 VALUES(DEFAULT);
14+
15+
*** Inverting the value of the @@global.binlog_checksum system variable.
16+
*** This must cause server binlog rotation.
17+
18+
*** Filling the table with more data after binlog file rotation.
19+
INSERT INTO t1 VALUES(DEFAULT);
20+
21+
*** Executing the Binlog Server utility in GTID + rewrite mode.
22+
*** One of the binlogs generated on the server should not have
23+
*** checksums in its events, so we should see a dedicated error.
24+
25+
*** Checking that Binlog Server utility reported
26+
*** "unsupported checksum-less event found" error.
27+
include/assert_grep.inc [server events must meet the checksum enforcement requirement]
28+
29+
*** Dropping the table.
30+
DROP TABLE t1;
31+
32+
*** Removing the Binlog Server utility storage directory.
33+
34+
*** Removing the Binlog Server utility log file.
35+
36+
*** Removing the Binlog Server utility configuration file.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[gtid_on_checksum_on]
2+
enforce-gtid-consistency = ON
3+
gtid-mode = ON
4+
5+
[gtid_on_checksum_off]
6+
enforce-gtid-consistency = ON
7+
gtid-mode = ON
8+
binlog-checksum=NONE
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
--source ../include/have_binsrv.inc
2+
3+
--source ../include/v80_v84_compatibility_defines.inc
4+
5+
# in case of --repeat=N, we need to start from a fresh binary log to make
6+
# this test deterministic
7+
--echo *** Resetting replication at the very beginning of the test.
8+
--disable_query_log
9+
eval $stmt_reset_binary_logs_and_gtids;
10+
--enable_query_log
11+
12+
# identifying backend storage type ('file' or 's3')
13+
--source ../include/identify_storage_backend.inc
14+
15+
# creating data directory, configuration file, etc.
16+
--let $binsrv_connect_timeout = 20
17+
--let $binsrv_read_timeout = 60
18+
--let $binsrv_idle_time = 10
19+
--let $binsrv_verify_checksum = TRUE
20+
--let $binsrv_replication_mode = gtid
21+
--let $binsrv_rewrite_file_size = 1G
22+
--source ../include/set_up_binsrv_environment.inc
23+
24+
--echo
25+
--echo *** Creating a simple table and filling it with some data.
26+
CREATE TABLE t1(id INT UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY(id)) ENGINE=InnoDB;
27+
INSERT INTO t1 VALUES(DEFAULT);
28+
29+
--echo
30+
--echo *** Inverting the value of the @@global.binlog_checksum system variable.
31+
--echo *** This must cause server binlog rotation.
32+
33+
--let $initial_binlog_checksum = `SELECT @@global.binlog_checksum`
34+
--disable_query_log
35+
if ($initial_binlog_checksum == CRC32)
36+
{
37+
SET GLOBAL binlog_checksum = NONE;
38+
}
39+
if ($initial_binlog_checksum == NONE)
40+
{
41+
SET GLOBAL binlog_checksum = CRC32;
42+
}
43+
--enable_query_log
44+
45+
--echo
46+
--echo *** Filling the table with more data after binlog file rotation.
47+
INSERT INTO t1 VALUES(DEFAULT);
48+
49+
--echo
50+
--echo *** Executing the Binlog Server utility in GTID + rewrite mode.
51+
--echo *** One of the binlogs generated on the server should not have
52+
--echo *** checksums in its events, so we should see a dedicated error.
53+
--error 1
54+
--exec $BINSRV fetch $binsrv_config_file_path > /dev/null
55+
56+
--echo
57+
--echo *** Checking that Binlog Server utility reported
58+
--echo *** "unsupported checksum-less event found" error.
59+
--let $assert_text = server events must meet the checksum enforcement requirement
60+
--let $assert_file = $binsrv_log_path
61+
--let $assert_count = 1
62+
--let $assert_select = rewrite is supported in gtid replication mode only when all events received from the MySQL server have checksums
63+
--source include/assert_grep.inc
64+
65+
--echo
66+
--echo *** Dropping the table.
67+
DROP TABLE t1;
68+
69+
# restoring original state of the @@global.binlog_checksum system variable
70+
--disable_query_log
71+
eval SET GLOBAL binlog_checksum = $initial_binlog_checksum;
72+
--enable_query_log
73+
74+
# cleaning up
75+
--source ../include/tear_down_binsrv_environment.inc

src/app.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,7 @@ void rewrite_and_process_binlog_event(
645645
binsrv::basic_logger &logger, binsrv::events::reader_context &context,
646646
binsrv::storage &storage, std::uint32_t server_id,
647647
std::string_view base_file_name, std::uint64_t file_size) {
648+
assert(storage.is_in_gtid_replication_mode());
648649
const auto current_common_header_v = current_event_v.get_common_header_view();
649650
const auto code = current_common_header_v.get_type_code();
650651

@@ -655,6 +656,30 @@ void rewrite_and_process_binlog_event(
655656
code == binsrv::events::code_type::previous_gtids_log ||
656657
code == binsrv::events::code_type::rotate ||
657658
code == binsrv::events::code_type::stop) {
659+
// making sure that there will be no events without checksums in the rewrite
660+
// mode because when recalculating the value of 'transaction_length' field
661+
// in GTID events we rely on the fact that upcoming events from the same
662+
// transaction will not change their size after rewriting (currently, to
663+
// avoid scenarios when one binlog file with checksums enabled is followed
664+
// by another file without checksums, and we have to combine them in the
665+
// same storage binlog file, we enforce that all events in the rewrite mode
666+
// must have checksums)
667+
668+
// TODO: this restriction can be lifted if we implement whole transaction
669+
// rewrite logic (we do not add incomplete transaction events into
670+
// the storage buffer unless we receive all of them and perform
671+
// necessary checksum addition/removal)
672+
if (code == binsrv::events::code_type::format_description) {
673+
const auto format_description_body{binsrv::events::generic_body<
674+
binsrv::events::code_type::format_description>{
675+
current_event_v.get_body_raw()}};
676+
if (!format_description_body.has_checksum_algorithm()) {
677+
util::exception_location().raise<std::logic_error>(
678+
"rewrite is supported in gtid replication mode only when "
679+
"all events received from the MySQL server have checksums");
680+
}
681+
}
682+
658683
const auto readable_flags{current_common_header_v.get_readable_flags()};
659684
logger.log(
660685
binsrv::log_severity::info,

0 commit comments

Comments
 (0)