From 75a2da35ff0e9055448e35da23db20bf3ba6cbbe Mon Sep 17 00:00:00 2001 From: ParveezBaig Date: Tue, 19 May 2026 10:44:28 +0530 Subject: [PATCH] Added tests for binlog_server. --- .../include/diff_with_storage_object.inc | 2 +- .../include/set_up_binsrv_environment.inc | 6 +- .../include/tear_down_binsrv_environment.inc | 2 +- .../include/v80_v84_compatibility_defines.inc | 4 + mtr/binlog_streaming/r/binsrv.result | 4 + .../r/checkpointing_verification.result | 116 +++++ mtr/binlog_streaming/r/cli_validation.result | 64 +++ .../r/config_validation.result | 117 +++++ .../r/connection_failures.result | 93 ++++ mtr/binlog_streaming/r/pull_mode.result | 59 ++- .../r/read_timeout_flush.result | 86 ++++ mtr/binlog_streaming/r/rewrite_mode.result | 99 +++++ ...ewrite_mode_transition_and_failover.result | 99 +++++ .../r/search_by_gtid_set_gtid.result | 12 + .../r/search_by_gtid_set_tagged_gtid.result | 66 +++ .../r/search_by_timestamp.result | 6 + mtr/binlog_streaming/r/ssl_negative.result | 111 +++++ .../r/storage_corruption.result | 190 ++++++++ mtr/binlog_streaming/t/binsrv.test | 32 ++ .../t/checkpointing_verification-master.opt | 3 + .../t/checkpointing_verification.combinations | 9 + .../t/checkpointing_verification.test | 417 ++++++++++++++++++ mtr/binlog_streaming/t/cli_validation.test | 195 ++++++++ mtr/binlog_streaming/t/config_validation.test | 329 ++++++++++++++ .../t/connection_failures.test | 330 ++++++++++++++ mtr/binlog_streaming/t/pull_mode.test | 134 +++++- .../t/read_timeout_flush-master.opt | 4 + .../t/read_timeout_flush.test | 279 ++++++++++++ .../t/rewrite_mode-master.opt | 2 + mtr/binlog_streaming/t/rewrite_mode.test | 290 ++++++++++++ ...te_mode_transition_and_failover-master.opt | 4 + ...ite_mode_transition_and_failover-slave.opt | 4 + .../rewrite_mode_transition_and_failover.cnf | 12 + .../rewrite_mode_transition_and_failover.test | 344 +++++++++++++++ .../t/search_by_gtid_set_gtid.test | 31 ++ .../search_by_gtid_set_tagged_gtid-master.opt | 2 + .../t/search_by_gtid_set_tagged_gtid.test | 160 +++++++ .../t/search_by_timestamp.test | 32 ++ mtr/binlog_streaming/t/ssl_negative.test | 138 ++++++ .../t/storage_corruption.test | 365 +++++++++++++++ 40 files changed, 4244 insertions(+), 8 deletions(-) create mode 100644 mtr/binlog_streaming/r/checkpointing_verification.result create mode 100644 mtr/binlog_streaming/r/cli_validation.result create mode 100644 mtr/binlog_streaming/r/config_validation.result create mode 100644 mtr/binlog_streaming/r/connection_failures.result create mode 100644 mtr/binlog_streaming/r/read_timeout_flush.result create mode 100644 mtr/binlog_streaming/r/rewrite_mode.result create mode 100644 mtr/binlog_streaming/r/rewrite_mode_transition_and_failover.result create mode 100644 mtr/binlog_streaming/r/search_by_gtid_set_tagged_gtid.result create mode 100644 mtr/binlog_streaming/r/ssl_negative.result create mode 100644 mtr/binlog_streaming/r/storage_corruption.result create mode 100644 mtr/binlog_streaming/t/checkpointing_verification-master.opt create mode 100644 mtr/binlog_streaming/t/checkpointing_verification.combinations create mode 100644 mtr/binlog_streaming/t/checkpointing_verification.test create mode 100644 mtr/binlog_streaming/t/cli_validation.test create mode 100644 mtr/binlog_streaming/t/config_validation.test create mode 100644 mtr/binlog_streaming/t/connection_failures.test create mode 100644 mtr/binlog_streaming/t/read_timeout_flush-master.opt create mode 100644 mtr/binlog_streaming/t/read_timeout_flush.test create mode 100644 mtr/binlog_streaming/t/rewrite_mode-master.opt create mode 100644 mtr/binlog_streaming/t/rewrite_mode.test create mode 100644 mtr/binlog_streaming/t/rewrite_mode_transition_and_failover-master.opt create mode 100644 mtr/binlog_streaming/t/rewrite_mode_transition_and_failover-slave.opt create mode 100644 mtr/binlog_streaming/t/rewrite_mode_transition_and_failover.cnf create mode 100644 mtr/binlog_streaming/t/rewrite_mode_transition_and_failover.test create mode 100644 mtr/binlog_streaming/t/search_by_gtid_set_tagged_gtid-master.opt create mode 100644 mtr/binlog_streaming/t/search_by_gtid_set_tagged_gtid.test create mode 100644 mtr/binlog_streaming/t/ssl_negative.test create mode 100644 mtr/binlog_streaming/t/storage_corruption.test diff --git a/mtr/binlog_streaming/include/diff_with_storage_object.inc b/mtr/binlog_streaming/include/diff_with_storage_object.inc index bee576d..8925e0f 100644 --- a/mtr/binlog_streaming/include/diff_with_storage_object.inc +++ b/mtr/binlog_streaming/include/diff_with_storage_object.inc @@ -33,7 +33,7 @@ if ($storage_backend == file) if ($storage_backend == s3) { --let $downloaded_file_path = $MYSQL_TMP_DIR/diff_with_storage_object.downloaded - --exec $aws_cli s3 cp s3://$aws_s3_bucket$storage_object $downloaded_file_path > /dev/null + --exec $aws_cli s3 cp s3://$aws_s3_bucket/$storage_object $downloaded_file_path > /dev/null --diff_files $local_file $downloaded_file_path --remove_file $downloaded_file_path } diff --git a/mtr/binlog_streaming/include/set_up_binsrv_environment.inc b/mtr/binlog_streaming/include/set_up_binsrv_environment.inc index 6f8d5e1..2785b9a 100644 --- a/mtr/binlog_streaming/include/set_up_binsrv_environment.inc +++ b/mtr/binlog_streaming/include/set_up_binsrv_environment.inc @@ -38,11 +38,11 @@ if ($storage_backend == file) if ($storage_backend == s3) { --let $binsrv_buffer_path = $MYSQL_TMP_DIR/buffer - --let $binsrv_storage_path = `SELECT CONCAT('/mtr-', UUID())` + --let $binsrv_storage_path = `SELECT CONCAT('mtr-', UUID())` --let $aws_s3_bucket = $MTR_BINSRV_AWS_S3_BUCKET if ($MTR_BINSRV_AWS_S3_ENDPOINT != '') { - eval SET @storage_uri = CONCAT('http://', '$MTR_BINSRV_AWS_ACCESS_KEY_ID', ':', '$MTR_BINSRV_AWS_SECRET_ACCESS_KEY', '@', '$MTR_BINSRV_AWS_S3_ENDPOINT', '/$MTR_BINSRV_AWS_S3_BUCKET', '$binsrv_storage_path'); + eval SET @storage_uri = CONCAT('http://', '$MTR_BINSRV_AWS_ACCESS_KEY_ID', ':', '$MTR_BINSRV_AWS_SECRET_ACCESS_KEY', '@', '$MTR_BINSRV_AWS_S3_ENDPOINT', '/$MTR_BINSRV_AWS_S3_BUCKET', '/$binsrv_storage_path'); } if ($MTR_BINSRV_AWS_S3_ENDPOINT == '') { @@ -51,7 +51,7 @@ if ($storage_backend == s3) { --let $qualified_bucket = $qualified_bucket.$MTR_BINSRV_AWS_S3_REGION } - eval SET @storage_uri = CONCAT('s3://', '$MTR_BINSRV_AWS_ACCESS_KEY_ID', ':', '$MTR_BINSRV_AWS_SECRET_ACCESS_KEY', '@', '$qualified_bucket', '$binsrv_storage_path'); + eval SET @storage_uri = CONCAT('s3://', '$MTR_BINSRV_AWS_ACCESS_KEY_ID', ':', '$MTR_BINSRV_AWS_SECRET_ACCESS_KEY', '@', '$qualified_bucket', '/$binsrv_storage_path'); } } diff --git a/mtr/binlog_streaming/include/tear_down_binsrv_environment.inc b/mtr/binlog_streaming/include/tear_down_binsrv_environment.inc index 0f92706..2df1d63 100644 --- a/mtr/binlog_streaming/include/tear_down_binsrv_environment.inc +++ b/mtr/binlog_streaming/include/tear_down_binsrv_environment.inc @@ -6,7 +6,7 @@ if ($storage_backend == file) } if ($storage_backend == s3) { - --exec $aws_cli s3 rm s3://$aws_s3_bucket$binsrv_storage_path/ --recursive > /dev/null + --exec $aws_cli s3 rm s3://$aws_s3_bucket/$binsrv_storage_path/ --recursive > /dev/null --force-rmdir $binsrv_buffer_path } diff --git a/mtr/binlog_streaming/include/v80_v84_compatibility_defines.inc b/mtr/binlog_streaming/include/v80_v84_compatibility_defines.inc index 0dfe660..a33d684 100644 --- a/mtr/binlog_streaming/include/v80_v84_compatibility_defines.inc +++ b/mtr/binlog_streaming/include/v80_v84_compatibility_defines.inc @@ -16,9 +16,13 @@ if ($lts_series == v80) { --let $stmt_reset_binary_logs_and_gtids = RESET MASTER --let $stmt_show_binary_log_status = SHOW MASTER STATUS + --let $init_rpl_inc = include/master-slave.inc + --let $deinit_rpl_inc = include/rpl_end.inc } if ($lts_series == v84) { --let $stmt_reset_binary_logs_and_gtids = RESET BINARY LOGS AND GTIDS --let $stmt_show_binary_log_status = SHOW BINARY LOG STATUS + --let $init_rpl_inc = include/rpl/init_source_replica.inc + --let $deinit_rpl_inc = include/rpl/deinit.inc } diff --git a/mtr/binlog_streaming/r/binsrv.result b/mtr/binlog_streaming/r/binsrv.result index 398be76..3c845c1 100644 --- a/mtr/binlog_streaming/r/binsrv.result +++ b/mtr/binlog_streaming/r/binsrv.result @@ -29,6 +29,10 @@ INSERT INTO t1 VALUES(DEFAULT); *** Checking that the Binlog Server utility detected an empty storage include/assert_grep.inc [Binlog storage must be initialized on an empty directory] +*** Verifying that binlog files were written to storage +include/assert_grep.inc [First binlog should exist in storage] +include/assert_grep.inc [Second binlog also hould exist in storage] + *** Comparing server and downloaded versions of the first binlog file. *** Patching the server version of the second binlog file to clear the diff --git a/mtr/binlog_streaming/r/checkpointing_verification.result b/mtr/binlog_streaming/r/checkpointing_verification.result new file mode 100644 index 0000000..a14c220 --- /dev/null +++ b/mtr/binlog_streaming/r/checkpointing_verification.result @@ -0,0 +1,116 @@ +*** Testing checkpointing verification +*** For S3 backend, each checkpoint triggers a full object re-upload. + +*** Resetting replication at the very beginning of the test. + +*** Creating a table and generating large binlog events +CREATE TABLE t1( +id SERIAL, +val CHAR(64) CHARACTER SET ascii NOT NULL, +PRIMARY KEY(id) +) ENGINE=InnoDB; + +*** Generating data to trigger checkpointing + +*** Determining current open binlog file name + +*** Setting up checkpointing configuration + +*** Setting up configuration +SET @save_binsrv_checkpoint_size = 'binsrv_checkpoint_size'; +SET @save_binsrv_checkpoint_interval = 'binsrv_checkpoint_interval'; + +*** Test 1: Test checkpointing with existing open binary log +*** Expectation: +*** - Without checkpointing: storage object stays at magic payload size while binsrv runs +*** - With size checkpointing: storage object grows beyond magic payload while binsrv runs +*** Note: Checkpointing only works with S3 backend, not with file backend + +Executing binlog_server in pull mode without checkpointing + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +include/read_file_to_var.inc +include/read_file_to_var.inc + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. + +Executing binlog_server in pull mode with checkpointing + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +include/read_file_to_var.inc +include/read_file_to_var.inc + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. +*** Test 2: Test that new updates in binary log on server will be flushed +*** to the binlog_server storage directory due to checkpointing. + + +*** Starting pull mode in background to monitor checkpointing + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +include/read_file_to_var.inc + +*** Capturing current storage object size (before workload) +include/read_file_to_var.inc + +*** Generating more data to trigger checkpointing during pull + +*** Waiting a bit to allow checkpointing to occur + +*** Capturing storage object size after checkpointing workload +include/read_file_to_var.inc + +*** Verifying checkpointing occurred (by storage object size change) + +*** Patching the server version of the binlog file to clear the +*** LOG_EVENT_BINLOG_IN_USE_F (currently in use) flag. +*** (The binlog is still open on the server, so we patch it instead of flushing +*** to avoid adding a ROTATE event that wouldn't exist in the storage object) + +*** Terminating pull mode + +*** Verifying binlog file integrity after checkpointing + +*** Starting pull mode again to verify it resumes correctly without any issues. +include/read_file_to_var.inc + +*** Adding more data +INSERT INTO t1(val) VALUES(SHA2(LAST_INSERT_ID(), 256)); +FLUSH BINARY LOGS; + +*** Waiting a bit then terminating + +*** Verifying that resume worked correctly (no duplicate data) + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. +DROP TABLE t1; diff --git a/mtr/binlog_streaming/r/cli_validation.result b/mtr/binlog_streaming/r/cli_validation.result new file mode 100644 index 0000000..a6620f6 --- /dev/null +++ b/mtr/binlog_streaming/r/cli_validation.result @@ -0,0 +1,64 @@ + +*** Testing command line argument validation + +*** Test 1: No arguments (should fail) + +*** Test 2: Invalid operation mode (should fail) + +*** Test 3: Version mode without config file (should succeed) + +*** Test 3a: Verify version output format (semantic version) +include/assert_grep.inc [Version output should match semantic version format (e.g., 0.1.0)] + +*** Test 3b: Verify version output is only on stdout (not stderr) +include/read_file_to_var.inc + +*** Test 3c: Verify version output is a single line +include/read_file_to_var.inc + +*** Test 4: Fetch mode without config file (should fail) + +*** Test 5: Pull mode without config file (should fail) + +*** Test 6: Fetch mode with non-existent config file (should fail) +include/assert_grep.inc [Fetch failed since the config file specified doesn't exist.] + +*** Test 7: Pull mode with non-existent config file (should fail) +include/assert_grep.inc [Pull failed since the config file specified doesn't exist.] + +*** Test 8: Too many arguments (should fail) + +*** Test 9: Version mode with extra argument (should fail) + +*** Test 10: Version mode with multiple extra arguments (should fail) + +*** Test 11: Version mode case sensitivity - uppercase (should fail) + +*** Test 12: Version mode case sensitivity - mixed case (should fail) + +*** Test 13: Version mode case sensitivity - VeRsIoN (should fail) + +*** Test 14: Verify version command exit code is 0 +include/read_file_to_var.inc + +*** Test 15: Invalid replication mode value (should fail) + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +include/assert_grep.inc [Config validation should fail with invalid replication mode] + +*** Test 16: Missing replication mode (should fail) + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +include/assert_grep.inc [Config validation should fail with missing replication mode] diff --git a/mtr/binlog_streaming/r/config_validation.result b/mtr/binlog_streaming/r/config_validation.result new file mode 100644 index 0000000..9300137 --- /dev/null +++ b/mtr/binlog_streaming/r/config_validation.result @@ -0,0 +1,117 @@ + +*** Testing configuration parsing and validation failures + +*** Test 1: Empty configuration file +include/assert_grep.inc [Empty config file should be rejected] + +*** Test 2: Malformed JSON configuration file +include/assert_grep.inc [Malformed JSON config should be rejected] + +*** Test 3: Oversized configuration file +include/assert_grep.inc [Oversized config file should be rejected] + +*** Preparing reusable local storage directory and config values +SET @config_validation_log_path = 'MYSQL_TMP_DIR/binsrv_config_validation.log'; +SET @config_validation_storage_uri = CONCAT('file://', 'MYSQL_TMP_DIR/config_validation_storage'); + +*** Test 4: Rewrite mode cannot be used with position replication +SET @binsrv_config_json = JSON_OBJECT( +'logger', JSON_OBJECT('level', 'error', 'file', @config_validation_log_path), +'connection', JSON_OBJECT( +'host', '127.0.0.1', +'port', @@global.port, +'user', 'root', +'password', '', +'connect_timeout', 20, +'read_timeout', 60, +'write_timeout', 60 +), +'replication', JSON_OBJECT( +'server_id', @@server_id + 1, +'idle_time', 10, +'verify_checksum', TRUE, +'mode', 'position', +'rewrite', JSON_OBJECT('base_file_name', 'rewritten', 'file_size', '1K') +), +'storage', JSON_OBJECT( +'backend', 'file', +'uri', @config_validation_storage_uri +) +); +include/assert_grep.inc [Rewrite mode in position replication should fail validation] + +*** Test 5: Rewrite file size below minimum +SET @binsrv_config_json = JSON_SET(@binsrv_config_json, +'$.replication.mode', 'gtid', +'$.replication.rewrite.file_size', '1023' +); +include/assert_grep.inc [Rewrite file size below 1024 bytes should fail validation] + +*** Test 6: Invalid checkpoint_size unit +SET @binsrv_config_json = JSON_REMOVE(@binsrv_config_json, '$.replication.rewrite'); +SET @binsrv_config_json = JSON_SET(@binsrv_config_json, +'$.replication.mode', 'position', +'$.storage.checkpoint_size', 'abc' +); +include/assert_grep.inc [Invalid checkpoint size should fail parsing] + +*** Test 7: Invalid checkpoint_interval unit +SET @binsrv_config_json = JSON_REMOVE(@binsrv_config_json, '$.storage.checkpoint_size'); +SET @binsrv_config_json = JSON_SET(@binsrv_config_json, +'$.storage.checkpoint_interval', '5w' +); +include/assert_grep.inc [Invalid checkpoint interval should fail parsing] + +*** Test 8: Connection config without host/port or DNS SRV name +SET @binsrv_config_json = JSON_REMOVE(@binsrv_config_json, '$.storage.checkpoint_interval'); +SET @binsrv_config_json = JSON_REMOVE(@binsrv_config_json, '$.connection.host'); +SET @binsrv_config_json = JSON_REMOVE(@binsrv_config_json, '$.connection.port'); +include/assert_grep.inc [Connection config must specify DNS SRV or host and port] + +*** Test 9: File URI with userinfo +SET @file_uri_with_userinfo = CONCAT('file://user:pass@', 'MYSQL_TMP_DIR/config_validation_storage'); +SET @binsrv_config_json = JSON_SET(@binsrv_config_json, +'$.connection.host', '127.0.0.1', +'$.connection.port', @@global.port, +'$.storage.uri', @file_uri_with_userinfo +); +include/assert_grep.inc [File URI with userinfo should be rejected] + +*** Test 10: File URI with query string +SET @file_uri_with_query = CONCAT('file://', 'MYSQL_TMP_DIR/config_validation_storage', '?x=1'); +SET @binsrv_config_json = JSON_SET(@binsrv_config_json, +'$.storage.uri', @file_uri_with_query +); +include/assert_grep.inc [File URI with query string should be rejected] + +*** Test 11: File URI with missing path +SET @binsrv_config_json = JSON_SET(@binsrv_config_json, +'$.storage.uri', 'file://' +); +include/assert_grep.inc [File URI with missing path should be rejected] + +*** Test 12: File URI with host +SET @binsrv_config_json = JSON_SET(@binsrv_config_json, +'$.storage.uri', 'file://host/path' +); +include/assert_grep.inc [File URI with host should be rejected] + +*** Test 13: Storage directory does not exist +SET @missing_file_uri = CONCAT('file://', 'MYSQL_TMP_DIR/nonexistent_config_validation_storage'); +SET @binsrv_config_json = JSON_SET(@binsrv_config_json, +'$.storage.uri', @missing_file_uri +); +include/assert_grep.inc [Missing storage directory should be rejected] + +*** Test 14: Storage path is a file, not a directory +SET @storage_file_uri = CONCAT('file://', 'MYSQL_TMP_DIR/config_validation_storage_file'); +SET @binsrv_config_json = JSON_SET(@binsrv_config_json, +'$.storage.uri', @storage_file_uri +); +include/assert_grep.inc [Storage path that is a file should be rejected] + +*** Test 15: Storage directory containing a subdirectory +SET @binsrv_config_json = JSON_SET(@binsrv_config_json, +'$.storage.uri', @config_validation_storage_uri +); +include/assert_grep.inc [Storage directory entries must be regular files] diff --git a/mtr/binlog_streaming/r/connection_failures.result b/mtr/binlog_streaming/r/connection_failures.result new file mode 100644 index 0000000..0c5e4e8 --- /dev/null +++ b/mtr/binlog_streaming/r/connection_failures.result @@ -0,0 +1,93 @@ + +*** Testing connection failure scenarios + +*** Test 1: Invalid host (should fail in fetch mode) + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +include/assert_grep.inc [Invalid host should fail while establishing MySQL connection] + +*** Cleaning up test environment + +*** Test 2: Invalid port (should fail in fetch mode) + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +include/assert_grep.inc [Invalid port should fail config extraction] + +*** Cleaning up test environment + +*** Test 3: Invalid user credentials (should fail in fetch mode) + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +include/assert_grep.inc [Invalid credentials should fail with access denied] + +*** Cleaning up test environment + +*** Test 4: User without REPLICATION SLAVE privilege (should fail) + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +include/assert_grep.inc [User without replication privilege should fail with access denied] + +*** Cleaning up test environment + +*** Test 5: No mysql server running on host (fail in fetch mode) + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +include/assert_grep.inc [Connection timeout should fail while establishing MySQL connection] + +*** Cleaning up test environment + +*** Test 6: DNS SRV name with host/port (should fail - validation error) + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +include/assert_grep.inc [Config validation should fail when both host/port and dns_srv_name are specified] + +*** Cleaning up test environment + +*** Test 7: Invalid DNS SRV name (should fail in fetch mode) + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +include/assert_grep.inc [Invalid DNS SRV name should fail while establishing MySQL connection] + +*** Cleaning up test environment diff --git a/mtr/binlog_streaming/r/pull_mode.result b/mtr/binlog_streaming/r/pull_mode.result index 7d9e95e..89153e1 100644 --- a/mtr/binlog_streaming/r/pull_mode.result +++ b/mtr/binlog_streaming/r/pull_mode.result @@ -60,4 +60,61 @@ FLUSH BINARY LOGS; *** Removing the Binlog Server utility log file. *** Removing the Binlog Server utility configuration file. -KILL CONNECTION ; + +*** Pull mode: zero idle_time + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. + +*** Starting Binlog Server Utility in background in pull mode with zero idle time +include/read_file_to_var.inc + +*** Creating data. +CREATE TABLE t1(id INT UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY(id)) ENGINE=InnoDB; +INSERT INTO t1 VALUES(DEFAULT); +*** Waiting for read timeout to trigger reconnection. +INSERT INTO t1 VALUES(DEFAULT); + +*** Terminating the utility + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. + +*** Pull mode: very short timeouts and rapid reconnection + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. + +*** Starting Binlog Server Utility in background +include/read_file_to_var.inc + +*** Creating data and restarting server multiple times +INSERT INTO t1 VALUES(DEFAULT); +FLUSH BINARY LOGS; +# restart +INSERT INTO t1 VALUES(DEFAULT); +FLUSH BINARY LOGS; +# restart +INSERT INTO t1 VALUES(DEFAULT); + +*** Terminating the utility + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. +DROP TABLE t1; diff --git a/mtr/binlog_streaming/r/read_timeout_flush.result b/mtr/binlog_streaming/r/read_timeout_flush.result new file mode 100644 index 0000000..0b5f66e --- /dev/null +++ b/mtr/binlog_streaming/r/read_timeout_flush.result @@ -0,0 +1,86 @@ + +*** Test : Storage flush after read_timeout disconnection (PS-10320 fix) +*** This test verifies that local binary log data is flushed to storage +*** upon disconnection due to read_timeout +*** This test also verifies existence of teporary files in buffer directory +*** in S3 storage mode while binlog_server run is in progress. +SET SESSION binlog_transaction_compression=ON; +CREATE TABLE t2(id INT UNSIGNED NOT NULL AUTO_INCREMENT, val CHAR(64), PRIMARY KEY(id)) ENGINE=InnoDB; + +*** Starting with a fresh binlog +FLUSH BINARY LOGS; + +*** Setting up binsrv with short read_timeout to trigger timeout + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. + +*** Starting Binlog Server Utility in background in pull mode +include/read_file_to_var.inc + +*** Waiting for binsrv to initialize and create binlog file in storage + +*** Step 1: Check initial binlog size (baseline) +include/read_file_to_var.inc + +*** Step 2: Execute INSERT and wait for read_timeout to trigger flush (read_timeout=20, wait 25 seconds) +INSERT INTO t2(val) VALUES('test_data_before_timeout'); +include/assert_grep.inc [Buffer directory should contain temporary files (UUID format)] + +*** Step 3: Check binlog size after read_timeout flush (should have increased) +include/read_file_to_var.inc + +*** Verifying that data was flushed to storage after first read_timeout (PS-10320 fix) + +*** Step 4: Wait around idle_time and generate new data in the binary log. verify either in idle_time or upon reconnection data was not flushed to storage. +INSERT INTO t2(val) VALUES('test_data_after_timeout'); +SET @save_binlog_row_value_options = @@GLOBAL.binlog_row_value_options; +SET GLOBAL binlog_row_value_options = PARTIAL_JSON; +Warnings: +Warning 3647 When binlog_row_image=FULL, the option binlog_row_value_options=PARTIAL_JSON will be used only for the after-image. Full values will be written in the before-image, so the saving in disk space due to binlog_row_value_options is limited to less than 50%. +UPDATE t2 set val='update_test_data_after_timeout' where val='test_data_after_timeout'; +CREATE TABLE tj (j JSON); +INSERT INTO tj VALUES('{"name":"john","age":30,"city":"blr"}'); +INSERT INTO tj VALUES('{"arr":[1,2,3],"details":{"salary":5000}}'); +UPDATE tj SET j = JSON_REPLACE(j, '$.city', 'mysore') WHERE JSON_EXTRACT(j, '$.name') = 'john'; +UPDATE tj SET j = JSON_REPLACE(j, '$.arr[1]', 999); +DROP TABLE tj; +SET GLOBAL binlog_row_value_options = @save_binlog_row_value_options; + +*** Step 5: Check binlog size after around idle time (should NOT have changed - since no flush) +include/read_file_to_var.inc + +*** Verifying that size did NOT change after reconnection (no flush) + +*** Step 6: Execute INSERT again +INSERT INTO t2(val) VALUES('test_data_after_timeout'); + +*** Step 7: Check size immediately after INSERT ie around idle/reconnection time (should NOT have changed - data in buffer) +include/read_file_to_var.inc + +*** Verifying that size did NOT change immediately after INSERT (data still in buffer) + +*** Step 8: Wait for read_timeout to trigger flush again (read_timeout=20) + +*** Step 9: Check binlog size after second read_timeout flush (should have increased) +include/read_file_to_var.inc + +*** Verifying that size increased after second read_timeout flush +FLUSH BINARY LOGS; + +*** Terminating the utility +include/assert_grep.inc [Temporary files shall be cleaned up in buffer directory (UUID format)] + +*** Final verification: Compare storage object with server binlog + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. +DROP TABLE t2; diff --git a/mtr/binlog_streaming/r/rewrite_mode.result b/mtr/binlog_streaming/r/rewrite_mode.result new file mode 100644 index 0000000..280c187 --- /dev/null +++ b/mtr/binlog_streaming/r/rewrite_mode.result @@ -0,0 +1,99 @@ + +*** Testing rewrite mode with custom base file name and split size + +*** Resetting replication at the very beginning of the test. + +*** Determining the original server binlog name. + +*** Creating a table and capturing GTIDs before source binlog rotation. +CREATE TABLE t1( +id SERIAL, +val CHAR(64) CHARACTER SET ascii NOT NULL, +PRIMARY KEY(id) +) ENGINE=InnoDB; +INSERT INTO t1(val) VALUES(SHA2('before-rotation', 256)); + +*** Rotating source binlog; rewrite mode should ignore source file names. +FLUSH BINARY LOGS; + +*** Generating enough GTID transactions to force rewrite file splitting. + +*** Setting up rewrite mode with a custom base file name and 1K split size. + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +SET @binsrv_config_json = JSON_SET(@binsrv_config_json, '$.replication.rewrite.base_file_name', 'custom_rewrite'); + +*** Test 1: Fetching in rewrite mode should create rewritten binlogs. +include/assert_grep.inc [Rewrite mode should use the configured base file name] +include/assert_grep.inc [Rewrite mode should split files when configured size is reached] +include/assert_grep.inc [Rewrite mode should not keep the original first source binlog name] +include/assert_grep.inc [Rewrite mode should not keep the original second source binlog name] + +*** Test 2: GTID search over split workload should span rewritten files. +include/read_file_to_var.inc + +*** Generating more data after the first fetch to verify rewrite resume. + +*** Test 3: Fetching again should resume rewritten storage. +include/assert_grep.inc [Rewrite resume should preserve the configured base file name] +include/read_file_to_var.inc + +*** Test 4: Timestamp search should also expose rewritten file names. +include/read_file_to_var.inc + +*** Removing temporary files. + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. + +*** Testing rewrite mode with large file size across source rotations. + +*** Creating a table and generating transactions in the first source binlog. +CREATE TABLE t2( +id SERIAL, +val CHAR(64) CHARACTER SET ascii NOT NULL, +PRIMARY KEY(id) +) ENGINE=InnoDB; + +*** Rotating source binlog and generating more transactions. +FLUSH BINARY LOGS; + +*** Rotating source binlog again and generating final transactions. +FLUSH BINARY LOGS; + +*** Setting up rewrite mode with a large file size. + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +SET @binsrv_config_json = JSON_SET(@binsrv_config_json, '$.replication.rewrite.base_file_name', 'large_rewrite'); + +*** Test 5: Large rewrite file size should keep all source rotations in one rewritten binlog. +include/assert_grep.inc [Large rewrite file size should create the first rewritten binlog] +include/assert_grep.inc [Large rewrite file size should not rotate to a second rewritten binlog] + +*** Test 6: GTID search across source rotations should return one rewritten binlog. +include/read_file_to_var.inc + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. + +*** Dropping the tables. +DROP TABLE t1; +DROP TABLE t2; diff --git a/mtr/binlog_streaming/r/rewrite_mode_transition_and_failover.result b/mtr/binlog_streaming/r/rewrite_mode_transition_and_failover.result new file mode 100644 index 0000000..6cb1923 --- /dev/null +++ b/mtr/binlog_streaming/r/rewrite_mode_transition_and_failover.result @@ -0,0 +1,99 @@ +include/rpl/init_source_replica.inc +[connection master] + +*** Testing transition from normal GTID fetch to rewrite-mode and failover to replica. + +*** Creating initial source workload for a non-rewrite fetch. +CREATE TABLE t1( +id SERIAL, +val CHAR(64) CHARACTER SET ascii NOT NULL, +PRIMARY KEY(id) +) ENGINE=InnoDB; + +*** Rotating source logs before the first fetch. +FLUSH BINARY LOGS; + +*** Setting up normal GTID fetch from source, without rewrite mode. + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +SET @binsrv_config_json = JSON_SET(@binsrv_config_json, +'$.connection.port', SOURCE_PORT +); + +*** Test 1: First fetch should store source binlog names. +include/assert_grep.inc [Fetch should keep the source first binlog name] +include/assert_grep.inc [Fetch should keep the source second binlog name] + +*** Generating workload for transition scenario from normal to rewrite mode. + +*** Rotating source and syncing replica. +FLUSH BINARY LOGS; + +*** Rotating replica logs independently from source. +FLUSH BINARY LOGS; +FLUSH BINARY LOGS; + +*** Test 2: Enabling rewrite mode. +SET @binsrv_config_json = JSON_INSERT(@binsrv_config_json, +'$.replication.rewrite', +JSON_OBJECT('base_file_name', 'transition_rewrite', 'file_size', '1K') +); +include/assert_grep.inc [Fetch should create binlogs with the rewrite file_size] + +*** Test 3: The new events fetched should be searchable. +include/read_file_to_var.inc + +*** Test 4: GTIDs fetched before rewrite mode was enabled remain searchable. +include/read_file_to_var.inc + +*** Generating extra load before changing rewrite base name and file size. + +*** Test 5: Changing rewrite base name and file size before fetch. +SET @binsrv_config_json = JSON_SET(@binsrv_config_json, +'$.replication.rewrite.base_file_name', 'changed_rewrite', +'$.replication.rewrite.file_size', '2K' +); +include/assert_grep.inc [Fetch should keep using the existing binlog base name] +include/assert_grep.inc [Fetch should create binlogs with new rewrite file_size] +include/read_file_to_var.inc + + +*** Generating additional workload for source to replica failover + +*** Rotating source logs differently from replica before failover fetch. +FLUSH BINARY LOGS; + +*** Rotating replica logs after it receives the new GTIDs. +FLUSH BINARY LOGS; + +*** Test 6: Repointing existing storage config from source to replica. +SET @binsrv_config_json = JSON_SET(@binsrv_config_json, +'$.connection.port', REPLICA_PORT +); +include/assert_grep.inc [Rewrite mode should not store replica pre-existing binlog names] +include/assert_grep.inc [Rewrite mode should not store independently rotated replica binlog names] +include/assert_grep.inc [Rewrite mode should not store replica post-workload binlog names] +include/assert_grep.inc [Rewrite mode should keep using source binlog base name after replica fetch] + +*** Test 7: New GTIDs fetched from replica should be searchable in rewritten files. +include/read_file_to_var.inc + +*** Test 8: Previously fetched GTIDs should remain searchable after failover. +include/read_file_to_var.inc + + +*** Dropping test table on source. +DROP TABLE t1; + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. +include/rpl/deinit.inc diff --git a/mtr/binlog_streaming/r/search_by_gtid_set_gtid.result b/mtr/binlog_streaming/r/search_by_gtid_set_gtid.result index 013169c..b27fefd 100644 --- a/mtr/binlog_streaming/r/search_by_gtid_set_gtid.result +++ b/mtr/binlog_streaming/r/search_by_gtid_set_gtid.result @@ -70,6 +70,18 @@ include/read_file_to_var.inc *** mode with a non-existing GTID range include/read_file_to_var.inc +*** 12. Executing the Binlog Server utility in the 'search_by_gtid_set' +*** mode with GTIDs listed in reverse order +include/read_file_to_var.inc + +*** 13. Executing the Binlog Server utility in the 'search_by_gtid_set' +*** mode with overlapping requested GTID sets +include/read_file_to_var.inc + +*** 14. Executing the Binlog Server utility in the 'search_by_gtid_set' +*** mode with the full stored GTID set +include/read_file_to_var.inc + *** Removing the search result file. *** Dropping the table. diff --git a/mtr/binlog_streaming/r/search_by_gtid_set_tagged_gtid.result b/mtr/binlog_streaming/r/search_by_gtid_set_tagged_gtid.result new file mode 100644 index 0000000..0b798ae --- /dev/null +++ b/mtr/binlog_streaming/r/search_by_gtid_set_tagged_gtid.result @@ -0,0 +1,66 @@ +*** Resetting replication at the very beginning of the test. + +*** Determining the first binary log name. + +*** Creating a table and capturing an untagged GTID. +CREATE TABLE t1(id INT UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY(id)) ENGINE=InnoDB; +INSERT INTO t1 VALUES(); + +*** Creating tagged GTIDs with varied tag lengths in separate binlogs. + +*** Writing a short-tagged GTID into the second binlog. +FLUSH BINARY LOGS; +START TRANSACTION; +INSERT INTO t1 VALUES(); +COMMIT; + +*** Writing a medium-tagged GTID range into the third binlog. +FLUSH BINARY LOGS; +START TRANSACTION; +INSERT INTO t1 VALUES(); +COMMIT; +START TRANSACTION; +INSERT INTO t1 VALUES(); +COMMIT; + +*** Writing a max-length-tagged GTID into the fourth binlog. +FLUSH BINARY LOGS; +START TRANSACTION; +INSERT INTO t1 VALUES(); +COMMIT; + +*** Setting up GTID-mode Binlog Server storage. + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. + +*** Test 1: Tagged GTID search should find a short tag in its binlog. +include/read_file_to_var.inc + +*** Test 2: Tagged GTID range should be covered by a single binlog. +include/read_file_to_var.inc + +*** Test 3: Mixed tagged GTIDs should be returned in binlog order. +include/read_file_to_var.inc + +*** Test 4: Tagged and untagged GTIDs should be searchable together. +include/read_file_to_var.inc + +*** Test 5: A missing tag should not be covered by matching gtid with other tag. +include/read_file_to_var.inc + +*** Removing the search result file. + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. + +*** Dropping the table. +DROP TABLE t1; diff --git a/mtr/binlog_streaming/r/search_by_timestamp.result b/mtr/binlog_streaming/r/search_by_timestamp.result index 0a5f682..9f261bd 100644 --- a/mtr/binlog_streaming/r/search_by_timestamp.result +++ b/mtr/binlog_streaming/r/search_by_timestamp.result @@ -54,6 +54,12 @@ include/read_file_to_var.inc *** files to be returned include/read_file_to_var.inc +*** Search response should include useful metadata fields. + +*** Storage URI in the response should match the configured backend. + +*** GTID fields should only be present for GTID storage. + *** Removing the search result file. *** Dropping the table. diff --git a/mtr/binlog_streaming/r/ssl_negative.result b/mtr/binlog_streaming/r/ssl_negative.result new file mode 100644 index 0000000..9825863 --- /dev/null +++ b/mtr/binlog_streaming/r/ssl_negative.result @@ -0,0 +1,111 @@ + +*** Testing SSL/TLS negative scenarios + +*** Creating test user without SSL requirement +CREATE USER IF NOT EXISTS 'test_ssl_user'@'127.0.0.1' IDENTIFIED BY 'test_password'; +GRANT REPLICATION SLAVE ON *.* TO 'test_ssl_user'@'127.0.0.1'; + +*** Test 1: SSL mode verify_ca without CA certificate (should fail) + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +include/assert_grep.inc [verify_ca without CA certificate should fail while establishing MySQL connection] + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. + +*** Test 2: SSL mode verify_identity without CA certificate (should fail) + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +include/assert_grep.inc [verify_identity without CA certificate should fail while establishing MySQL connection] + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. + +*** Test 3: SSL mode verify_ca with non-existent CA file (should fail) + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +include/assert_grep.inc [verify_ca with non-existent CA file should fail while establishing MySQL connection] + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. + +*** Test 4: SSL mode verify_identity with invalid certificate (should fail) + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +include/assert_grep.inc [verify_identity with invalid certificate should fail while establishing MySQL connection] + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. + +*** Test 5: TLS version mismatch (if server doesn't support requested version) + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +include/assert_grep.inc [Invalid TLS version should fail while setting MySQL TLS version] + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. + +*** Test 6: SSL cert without key (should fail) + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +include/assert_grep.inc [SSL certificate without key should fail while establishing MySQL connection] + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. + +*** Dropping test user +DROP USER IF EXISTS 'test_ssl_user'@'127.0.0.1'; diff --git a/mtr/binlog_streaming/r/storage_corruption.result b/mtr/binlog_streaming/r/storage_corruption.result new file mode 100644 index 0000000..3b796d4 --- /dev/null +++ b/mtr/binlog_streaming/r/storage_corruption.result @@ -0,0 +1,190 @@ + +*** Testing storage corruption scenarios + +*** Resetting replication at the very beginning of the test. + +*** Creating a simple table and filling it with some data. +CREATE TABLE t1(id INT UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY(id)) ENGINE=InnoDB; +INSERT INTO t1 VALUES(DEFAULT); +FLUSH BINARY LOGS; + +*** Test 1: Storage with missing binlog.index but containing binlog files + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. + +*** Corrupting storage: removing binlog.index but keeping binlog files +include/assert_grep.inc [Storage without binlog.index should be rejected] + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. + +*** Test 2: Storage with binlog.index containing duplicate entries + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. + +*** Corrupting storage: adding duplicate entry to binlog.index +include/assert_grep.inc [Duplicate binlog.index entries should be rejected] + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. + +*** Test 3: Storage with binlog.index referencing non-existent binlog file + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. + +*** Corrupting storage: adding reference to non-existent binlog +include/assert_grep.inc [binlog.index references to non-existing objects should be rejected] + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. + +*** Test 4: Storage with binlog file not referenced in binlog.index + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. + +*** Corrupting storage: adding orphaned binlog file +include/assert_grep.inc [Binlog objects not referenced in binlog.index should be rejected] + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. + +*** Test 5: Storage with binlog.index containing invalid path + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. + +*** Corrupting storage: adding invalid path to binlog.index +include/assert_grep.inc [binlog.index entries with invalid paths should be rejected] + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. + +*** Test 6: Unsupported top-level storage metadata version + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +include/assert_grep.inc [Unsupported storage metadata version should be rejected] + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. + +*** Test 7: Replication mode mismatch with initialized storage + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +include/assert_grep.inc [Replication mode mismatch should be rejected] + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. + +*** Test 8: Missing per-binlog metadata file + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +include/assert_grep.inc [Missing binlog metadata should be rejected] + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. + +*** Test 9: Per-binlog metadata size mismatch + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +include/assert_grep.inc [Metadata size mismatch should be rejected] + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. + +*** Test 10: Metadata file for a non-existing binlog + +*** Generating a configuration file in JSON format for the Binlog +*** Server utility. + +*** Determining binlog file directory from the server. + +*** Creating a temporary directory for storing +*** binlog files downloaded via the Binlog Server utility. +include/assert_grep.inc [Orphaned binlog metadata should be rejected] + +*** Removing the Binlog Server utility storage directory. + +*** Removing the Binlog Server utility log file. + +*** Removing the Binlog Server utility configuration file. +DROP TABLE t1; diff --git a/mtr/binlog_streaming/t/binsrv.test b/mtr/binlog_streaming/t/binsrv.test index 5fa6e0c..240b09a 100644 --- a/mtr/binlog_streaming/t/binsrv.test +++ b/mtr/binlog_streaming/t/binsrv.test @@ -60,6 +60,38 @@ INSERT INTO t1 VALUES(DEFAULT); --let $assert_select = binlog storage initialized on an empty directory --source include/assert_grep.inc +--echo +--echo *** Verifying that binlog files were written to storage +--let $assert_text = First binlog should exist in storage +--let $assert_file = $MYSQL_TMP_DIR/storage_check.log +if ($storage_backend == file) +{ + --exec ls -la $binsrv_storage_path/$first_binlog > $assert_file 2>&1 || true +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 ls s3://$aws_s3_bucket/$binsrv_storage_path/$first_binlog > $assert_file 2>&1 || true +} +--let $assert_count = 2 +--let $assert_select = $first_binlog +--source include/assert_grep.inc +--remove_file $assert_file + +--let $assert_text = Second binlog also hould exist in storage +--let $assert_file = $MYSQL_TMP_DIR/storage_check.log +if ($storage_backend == file) +{ + --exec ls -la $binsrv_storage_path/$second_binlog > $assert_file 2>&1 || true +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 ls s3://$aws_s3_bucket/$binsrv_storage_path/$second_binlog > $assert_file 2>&1 || true +} +--let $assert_count = 2 +--let $assert_select = $second_binlog +--source include/assert_grep.inc +--remove_file $assert_file + # At this point we have 2 binlog files $first_binlog (already closed/rotated # by the server) and $second_binlog (currently open). diff --git a/mtr/binlog_streaming/t/checkpointing_verification-master.opt b/mtr/binlog_streaming/t/checkpointing_verification-master.opt new file mode 100644 index 0000000..9d04374 --- /dev/null +++ b/mtr/binlog_streaming/t/checkpointing_verification-master.opt @@ -0,0 +1,3 @@ +--gtid-mode=on +--enforce-gtid-consistency + diff --git a/mtr/binlog_streaming/t/checkpointing_verification.combinations b/mtr/binlog_streaming/t/checkpointing_verification.combinations new file mode 100644 index 0000000..0a10552 --- /dev/null +++ b/mtr/binlog_streaming/t/checkpointing_verification.combinations @@ -0,0 +1,9 @@ +[checkpointing_size] +init-connect = SET @binsrv_checkpointing = 'size' + +[checkpointing_interval] +init-connect = SET @binsrv_checkpointing = 'interval' + +[checkpointing_both] +init-connect = SET @binsrv_checkpointing = 'both' + diff --git a/mtr/binlog_streaming/t/checkpointing_verification.test b/mtr/binlog_streaming/t/checkpointing_verification.test new file mode 100644 index 0000000..af97155 --- /dev/null +++ b/mtr/binlog_streaming/t/checkpointing_verification.test @@ -0,0 +1,417 @@ +--source ../include/have_binsrv.inc +--source include/big_test.inc + +--source ../include/v80_v84_compatibility_defines.inc + +--source include/count_sessions.inc + +# identifying backend storage type ('file' or 's3') +--source ../include/identify_storage_backend.inc +# identifying interval checkpointing mode from the combination +--let $extracted_init_connect_variable_name = binsrv_checkpointing +--source ../include/extract_init_connect_variable_value.inc + +--echo *** Testing checkpointing verification +--echo *** For S3 backend, each checkpoint triggers a full object re-upload. + +--echo +--echo *** Resetting replication at the very beginning of the test. +--disable_query_log +eval $stmt_reset_binary_logs_and_gtids; +--enable_query_log + +--echo +--echo *** Creating a table and generating large binlog events +CREATE TABLE t1( + id SERIAL, + val CHAR(64) CHARACTER SET ascii NOT NULL, + PRIMARY KEY(id) +) ENGINE=InnoDB; + +--echo +--echo *** Generating data to trigger checkpointing +--disable_query_log +--let $transaction_idx = 0 +while ($transaction_idx < 20) +{ + START TRANSACTION; + --let $statement_idx = 0 + while ($statement_idx < 100) + { + eval INSERT INTO t1(val) VALUES(SHA2(LAST_INSERT_ID(), 256)); + --inc $statement_idx + } + COMMIT; + --inc $transaction_idx +} +--enable_query_log + +--echo +--echo *** Determining current open binlog file name +--let $binlog_name = query_get_value($stmt_show_binary_log_status, File, 1) + +--let $binsrv_replication_mode = position + +--echo +--echo *** Setting up checkpointing configuration + +if ($extracted_init_connect_variable_value == size) +{ + --let $binsrv_checkpoint_size = 1K + --let $binsrv_checkpoint_interval = 0 +} +if ($extracted_init_connect_variable_value == interval) +{ + --let $binsrv_checkpoint_interval = 2s + --let $binsrv_checkpoint_size = 0 +} +if ($extracted_init_connect_variable_value == both) +{ + --let $binsrv_checkpoint_size = 1K + --let $binsrv_checkpoint_interval = 2s +} + +--echo +--echo *** Setting up configuration +--let $binsrv_connect_timeout = 10 +--let $binsrv_read_timeout = 200 +--let $binsrv_idle_time = 1 +--let $binsrv_verify_checksum = TRUE + +--replace_result $binsrv_checkpoint_size binsrv_checkpoint_size +--eval SET @save_binsrv_checkpoint_size = '$binsrv_checkpoint_size' +--replace_result $binsrv_checkpoint_interval binsrv_checkpoint_interval +--eval SET @save_binsrv_checkpoint_interval = '$binsrv_checkpoint_interval' + +--echo +--echo *** Test 1: Test checkpointing with existing open binary log +--echo *** Expectation: +--echo *** - Without checkpointing: storage object stays at magic payload size while binsrv runs +--echo *** - With size checkpointing: storage object grows beyond magic payload while binsrv runs +--echo *** Note: Checkpointing only works with S3 backend, not with file backend + +--echo +--echo Executing binlog_server in pull mode without checkpointing +--let $binsrv_checkpoint_size = +--let $$binsrv_checkpoint_interval = +--let $binsrv_pid_file = $MYSQL_TMP_DIR/binsrv_no_ckpt.pid +--source ../include/set_up_binsrv_environment.inc + +--let $binsrv_spawn_cmd_line = $BINSRV pull $binsrv_config_file_path > $binsrv_log_path 2>&1 & echo \$! > $binsrv_pid_file +--let EXPORTED_BINSRV_SPAWN_CMD_LINE = $binsrv_spawn_cmd_line +--perl + use strict; + use warnings; + my $cmd = $ENV{'EXPORTED_BINSRV_SPAWN_CMD_LINE'}; + system("$cmd"); +EOF + +--let $read_from_file = $binsrv_pid_file +--source include/read_file_to_var.inc +--let $binsrv_pid = $result + +--sleep 10 + +--let $size_file = $MYSQL_TMP_DIR/binsrv_no_ckpt_size + +if ($storage_backend == file) +{ + --exec /bin/sh -c "wc -c < '$binsrv_storage_path/$binlog_name' > '$size_file'" +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3api head-object --bucket $MTR_BINSRV_AWS_S3_BUCKET --key $binsrv_storage_path/$binlog_name --query ContentLength --output text > $size_file 2>/dev/null || echo 0 > $size_file +} + +--let $read_from_file = $size_file +--source include/read_file_to_var.inc +--let $no_ckpt_size = $result +if ($no_ckpt_size != 4) +{ + --echo Binlog size is $no_ckpt_size + --die Binlog size to be at magic payload before flush +} + +--remove_file $size_file + +--let EXPORTED_BINSRV_PID = $binsrv_pid +--exec kill -9 $binsrv_pid +--remove_file $binsrv_pid_file + +--source ../include/tear_down_binsrv_environment.inc + + +--echo +--echo Executing binlog_server in pull mode with checkpointing +--let $binsrv_checkpoint_size = `select @save_binsrv_checkpoint_size` +--let $binsrv_checkpoint_interval =`select @save_binsrv_checkpoint_interval` +--source ../include/set_up_binsrv_environment.inc + +--let $binsrv_pid_file = $MYSQL_TMP_DIR/binsrv_size_ckpt.pid +--let $binsrv_spawn_cmd_line = $BINSRV pull $binsrv_config_file_path > $binsrv_log_path 2>&1 & echo \$! > $binsrv_pid_file +--let EXPORTED_BINSRV_SPAWN_CMD_LINE = $binsrv_spawn_cmd_line +--perl + use strict; + use warnings; + my $cmd = $ENV{'EXPORTED_BINSRV_SPAWN_CMD_LINE'}; + system("$cmd"); +EOF + +--let $read_from_file = $binsrv_pid_file +--source include/read_file_to_var.inc +--let $binsrv_pid = $result + +--sleep 10 + +--let $size_file = $MYSQL_TMP_DIR/binsrv_size_ckpt_size +if ($storage_backend == file) +{ + --exec /bin/sh -c "wc -c < '$binsrv_storage_path/$binlog_name' > '$size_file'" +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3api head-object --bucket $MTR_BINSRV_AWS_S3_BUCKET --key $binsrv_storage_path/$binlog_name --query ContentLength --output text > $size_file 2>/dev/null || echo 0 > $size_file +} +--let $read_from_file = $size_file +--source include/read_file_to_var.inc +--let $size_ckpt_size = $result +--remove_file $size_file +--let $size_ckpt_grew = `SELECT ($size_ckpt_size > $no_ckpt_size)` +if ($binsrv_checkpoint_size == 1K) +{ + if ($size_ckpt_grew != 1) + { + --die expected storage object to grow with size checkpointing + } +} +if ($binsrv_checkpoint_size != 1K) +{ + if ($size_ckpt_size != 4) + { + --die expected storage object size to be at magic payload without size checkpointing + } +} +--exec kill -s TERM $binsrv_pid +--let EXPORTED_BINSRV_PID = $binsrv_pid +--perl + use strict; + use warnings; + use Errno; + my $pid = $ENV{'EXPORTED_BINSRV_PID'}; + my $not_present = (!kill(0, $pid) && $! == Errno::ESRCH); + while (!$not_present) { + sleep(1); + $not_present = (!kill(0, $pid) && $! == Errno::ESRCH); + } +EOF +--remove_file $binsrv_pid_file +--source ../include/tear_down_binsrv_environment.inc + +--echo *** Test 2: Test that new updates in binary log on server will be flushed +--echo *** to the binlog_server storage directory due to checkpointing. +--echo + +--echo +--echo *** Starting pull mode in background to monitor checkpointing +--let $binsrv_pid_file = $MYSQL_TMP_DIR/binsrv_checkpointing.pid +--source ../include/set_up_binsrv_environment.inc +--let $binsrv_spawn_cmd_line = $BINSRV pull $binsrv_config_file_path > $binsrv_log_path 2>&1 & echo \$! > $binsrv_pid_file +--let EXPORTED_BINSRV_SPAWN_CMD_LINE = $binsrv_spawn_cmd_line +--perl + use strict; + use warnings; + my $cmd = $ENV{'EXPORTED_BINSRV_SPAWN_CMD_LINE'}; + system("$cmd"); +EOF + +--let $read_from_file = $binsrv_pid_file +--source include/read_file_to_var.inc +--let $binsrv_pid = $result + +--sleep 10 + +--echo +--echo *** Capturing current storage object size (before workload) + +--let $size_file = $MYSQL_TMP_DIR/binsrv_storage_object_size_before +if ($storage_backend == file) +{ + --exec /bin/sh -c "wc -c < '$binsrv_storage_path/$binlog_name' > '$size_file'" +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3api head-object --bucket $MTR_BINSRV_AWS_S3_BUCKET --key $binsrv_storage_path/$binlog_name --query ContentLength --output text > $size_file 2>/dev/null || echo 0 > $size_file +} +--let $read_from_file = $size_file +--source include/read_file_to_var.inc +--let $storage_object_size_before = $result +--remove_file $size_file + +--echo +--echo *** Generating more data to trigger checkpointing during pull +--disable_query_log +--let $transaction_idx = 0 +while ($transaction_idx < 20) +{ + START TRANSACTION; + --let $statement_idx = 0 + while ($statement_idx < 50) + { + eval INSERT INTO t1(val) VALUES(SHA2(LAST_INSERT_ID(), 256)); + --inc $statement_idx + } + COMMIT; + --inc $transaction_idx +} +--enable_query_log + +--echo +--echo *** Waiting a bit to allow checkpointing to occur +--sleep 10 + +--echo +--echo *** Capturing storage object size after checkpointing workload +--let $size_file = $MYSQL_TMP_DIR/binsrv_storage_object_size_after +if ($storage_backend == file) +{ + --exec /bin/sh -c "wc -c < '$binsrv_storage_path/$binlog_name' > '$size_file'" +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3api head-object --bucket $MTR_BINSRV_AWS_S3_BUCKET --key $binsrv_storage_path/$binlog_name --query ContentLength --output text > $size_file 2>/dev/null || echo 0 > $size_file +} +--let $read_from_file = $size_file +--source include/read_file_to_var.inc +--let $storage_object_size_after = $result +--remove_file $size_file + +--echo +--echo *** Verifying checkpointing occurred (by storage object size change) +if ($extracted_init_connect_variable_value == size) +{ + if ($storage_backend == s3) + { + --let $size_grew = `SELECT ($storage_object_size_after > $storage_object_size_before)` + if ($size_grew != 1) + { + --die storage object size did not grow after checkpointing workload + } + } +} + + + +--echo +--echo *** Patching the server version of the binlog file to clear the +--echo *** LOG_EVENT_BINLOG_IN_USE_F (currently in use) flag. +--echo *** (The binlog is still open on the server, so we patch it instead of flushing +--echo *** to avoid adding a ROTATE event that wouldn't exist in the storage object) +--let PATCHED_BINLOG_FILE = $MYSQL_TMP_DIR/$binlog_name.patched +--copy_file $binlog_base_dir/$binlog_name $PATCHED_BINLOG_FILE + +--perl + use strict; + use warnings; + use constant MAGIC_OFFSET => 21; + my $binlog_file_perl = $ENV{'PATCHED_BINLOG_FILE'}; + + open(my $fh, '+<:raw', $binlog_file_perl) or die "Failed to open file: $!"; + + seek($fh, MAGIC_OFFSET, 0); + my $byte; + read($fh, $byte, 1); + + $byte = ord($byte) & 0xFE; + + seek($fh, MAGIC_OFFSET, 0); + print $fh pack('C', $byte); + + close($fh); +EOF + +--echo +--echo *** Terminating pull mode +--exec kill -s TERM $binsrv_pid +--let EXPORTED_BINSRV_PID = $binsrv_pid +--perl + use strict; + use warnings; + use Errno; + my $pid = $ENV{'EXPORTED_BINSRV_PID'}; + my $not_present = (!kill(0, $pid) && $! == Errno::ESRCH); + while (!$not_present) { + sleep(1); + $not_present = (!kill(0, $pid) && $! == Errno::ESRCH); + } +EOF + +--remove_file $binsrv_pid_file + +--echo +--echo *** Verifying binlog file integrity after checkpointing +--let $local_file = $PATCHED_BINLOG_FILE +--let $storage_object = $binsrv_storage_path/$binlog_name +--source ../include/diff_with_storage_object.inc + +--remove_file $PATCHED_BINLOG_FILE + +--echo +--echo *** Starting pull mode again to verify it resumes correctly without any issues. +--let $binsrv_pid_file = $MYSQL_TMP_DIR/binsrv_checkpointing_resume.pid +--let $binsrv_spawn_cmd_line = $BINSRV pull $binsrv_config_file_path > $binsrv_log_path 2>&1 & echo \$! > $binsrv_pid_file +--let EXPORTED_BINSRV_SPAWN_CMD_LINE = $binsrv_spawn_cmd_line +--perl + use strict; + use warnings; + my $cmd = $ENV{'EXPORTED_BINSRV_SPAWN_CMD_LINE'}; + system("$cmd"); +EOF + +--let $read_from_file = $binsrv_pid_file +--source include/read_file_to_var.inc +--let $binsrv_pid = $result + +--echo +--echo *** Adding more data +eval INSERT INTO t1(val) VALUES(SHA2(LAST_INSERT_ID(), 256)); +FLUSH BINARY LOGS; + +--echo +--echo *** Waiting a bit then terminating +--sleep 10 +--exec kill -s TERM $binsrv_pid +--let EXPORTED_BINSRV_PID = $binsrv_pid +--perl + use strict; + use warnings; + use Errno; + my $pid = $ENV{'EXPORTED_BINSRV_PID'}; + my $not_present = (!kill(0, $pid) && $! == Errno::ESRCH); + while (!$not_present) { + sleep(1); + $not_present = (!kill(0, $pid) && $! == Errno::ESRCH); + } +EOF +--remove_file $binsrv_pid_file + +--echo +--echo *** Verifying that resume worked correctly (no duplicate data) +--let $local_file = $binlog_base_dir/$binlog_name +--let $storage_object = $binsrv_storage_path/$binlog_name +--source ../include/diff_with_storage_object.inc + +--source ../include/tear_down_binsrv_environment.inc + +# Clean up any remaining binlog dump connections +--let $binlog_dump_connection_id = `SELECT ID FROM performance_schema.processlist WHERE COMMAND = 'Binlog Dump'` +if ($binlog_dump_connection_id != '') +{ + --disable_query_log + --replace_result $binlog_dump_connection_id + eval KILL CONNECTION $binlog_dump_connection_id; + --enable_query_log +} + +--source include/wait_until_count_sessions.inc + +DROP TABLE t1; diff --git a/mtr/binlog_streaming/t/cli_validation.test b/mtr/binlog_streaming/t/cli_validation.test new file mode 100644 index 0000000..d370721 --- /dev/null +++ b/mtr/binlog_streaming/t/cli_validation.test @@ -0,0 +1,195 @@ +--source ../include/have_binsrv.inc + +--echo +--echo *** Testing command line argument validation + +--echo +--echo *** Test 1: No arguments (should fail) +--error 1 +--exec $BINSRV + +--echo +--echo *** Test 2: Invalid operation mode (should fail) +--error 1 +--exec $BINSRV invalid_mode + +--echo +--echo *** Test 3: Version mode without config file (should succeed) +--let $binsrv_version_output = $MYSQL_TMP_DIR/binsrv_version_output.txt +--exec $BINSRV version > $binsrv_version_output 2>&1 + +--echo +--echo *** Test 3a: Verify version output format (semantic version) +--let $assert_text = Version output should match semantic version format (e.g., 0.1.0) +--let $assert_file = $binsrv_version_output +--let $assert_count = 1 +--let $assert_select = [0-9]+\.[0-9]+\.[0-9]+ +--source include/assert_grep.inc + +--echo +--echo *** Test 3b: Verify version output is only on stdout (not stderr) +--let $binsrv_version_stderr = $MYSQL_TMP_DIR/binsrv_version_stderr.txt +--exec $BINSRV version > $binsrv_version_output 2> $binsrv_version_stderr +--let $read_from_file = $binsrv_version_stderr +--source include/read_file_to_var.inc +--let $stderr_content = $result +--let $stderr_empty = `SELECT LENGTH('$stderr_content') = 0` +if ($stderr_empty != 1) +{ + --die Version command should not output anything to stderr +} +--remove_file $binsrv_version_stderr + +--echo +--echo *** Test 3c: Verify version output is a single line +--let $read_from_file = $binsrv_version_output +--source include/read_file_to_var.inc +--let $version_output = $result +--let $line_count = `SELECT (LENGTH('$version_output') - LENGTH(REPLACE('$version_output', '\n', '')) + 1)` +if ($line_count != 1) +{ + --die Version command should output exactly one line +} +--remove_file $binsrv_version_output + +--echo +--echo *** Test 4: Fetch mode without config file (should fail) +--error 1 +--exec $BINSRV fetch + +--echo +--echo *** Test 5: Pull mode without config file (should fail) +--error 1 +--exec $BINSRV pull + +--let $binsrv_log_path = $MYSQL_TMP_DIR/binsrv_utility.log + +--echo +--echo *** Test 6: Fetch mode with non-existent config file (should fail) +--error 1 +--exec $BINSRV fetch /nonexistent/path/config.json > $binsrv_log_path 2>&1 + +--let $assert_text = Fetch failed since the config file specified doesn't exist. +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = exception caught.*cannot open configuration file +--source include/assert_grep.inc +--remove_file $binsrv_log_path + +--echo +--echo *** Test 7: Pull mode with non-existent config file (should fail) +--error 1 +--exec $BINSRV pull /nonexistent/path/config.json > $binsrv_log_path 2>&1 + +--let $assert_text = Pull failed since the config file specified doesn't exist. +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = exception caught.*cannot open configuration file +--source include/assert_grep.inc +--remove_file $binsrv_log_path + +--echo +--echo *** Test 8: Too many arguments (should fail) +--error 1 +--exec $BINSRV fetch config.json extra_arg + +--echo +--echo *** Test 9: Version mode with extra argument (should fail) +--error 1 +--exec $BINSRV version /nonexistent/path/config.json + +--echo +--echo *** Test 10: Version mode with multiple extra arguments (should fail) +--error 1 +--exec $BINSRV version arg1 arg2 arg3 + +--echo +--echo *** Test 11: Version mode case sensitivity - uppercase (should fail) +--error 1 +--exec $BINSRV VERSION + +--echo +--echo *** Test 12: Version mode case sensitivity - mixed case (should fail) +--error 1 +--exec $BINSRV Version + +--echo +--echo *** Test 13: Version mode case sensitivity - VeRsIoN (should fail) +--error 1 +--exec $BINSRV VeRsIoN + +--echo +--echo *** Test 14: Verify version command exit code is 0 +--let EXPORTED_BINSRV = $BINSRV +--let EXPORTED_VERSION_OUTPUT = $MYSQL_TMP_DIR/binsrv_version_output_test.txt +--let EXPORTED_EXIT_CODE_FILE = $MYSQL_TMP_DIR/binsrv_exit_code.txt +--perl + use strict; + use warnings; + my $binsrv = $ENV{'EXPORTED_BINSRV'}; + my $output_file = $ENV{'EXPORTED_VERSION_OUTPUT'}; + my $exit_code_file = $ENV{'EXPORTED_EXIT_CODE_FILE'}; + + system("$binsrv version > $output_file 2>&1 & echo \$? > $exit_code_file"); +EOF +--let $read_from_file = $MYSQL_TMP_DIR/binsrv_exit_code.txt +--source include/read_file_to_var.inc +--let $exit_code = $result +if ($exit_code != 0) +{ + --die Version command should exit with code 0, but got $exit_code +} +--remove_file $MYSQL_TMP_DIR/binsrv_exit_code.txt +--remove_file $MYSQL_TMP_DIR/binsrv_version_output_test.txt + +--echo +--echo *** Test 15: Invalid replication mode value (should fail) +--let $binsrv_log_path = $MYSQL_TMP_DIR/binsrv_utility.log +--let $binsrv_connection_host = 127.0.0.1 +--let $binsrv_connect_timeout = 20 +--let $binsrv_read_timeout = 60 +--let $binsrv_idle_time = 10 +--let $binsrv_verify_checksum = TRUE +--let $binsrv_replication_mode = position +--source ../include/set_up_binsrv_environment.inc + +# Manually modify config to use invalid replication mode +--exec sed -i 's/"mode": "position"/"mode": "invalid_mode"/' $binsrv_config_file_path + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 + +--let $assert_text = Config validation should fail with invalid replication mode +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = exception caught|invalid|replication.*mode +--source include/assert_grep.inc +--remove_file $binsrv_log_path +--exec rm -f $binsrv_config_file_path + +--echo +--echo *** Test 16: Missing replication mode (should fail) +--let $binsrv_log_path = $MYSQL_TMP_DIR/binsrv_utility.log +--let $binsrv_connection_host = 127.0.0.1 +--let $binsrv_connect_timeout = 20 +--let $binsrv_read_timeout = 60 +--let $binsrv_idle_time = 10 +--let $binsrv_verify_checksum = TRUE +--let $binsrv_replication_mode = position +--source ../include/set_up_binsrv_environment.inc + +# Manually remove replication mode from config +--exec sed -i 's/,"mode": "[^"]*"//' $binsrv_config_file_path + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 + +--let $assert_text = Config validation should fail with missing replication mode +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = exception caught|missing|replication.*mode +--source include/assert_grep.inc +--remove_file $binsrv_log_path +--exec rm -f $binsrv_config_file_path + + diff --git a/mtr/binlog_streaming/t/config_validation.test b/mtr/binlog_streaming/t/config_validation.test new file mode 100644 index 0000000..2e8d93f --- /dev/null +++ b/mtr/binlog_streaming/t/config_validation.test @@ -0,0 +1,329 @@ +--source ../include/have_binsrv.inc + +--echo +--echo *** Testing configuration parsing and validation failures + +--let $binsrv_cmd_output = $MYSQL_TMP_DIR/binsrv_config_validation.out +--let $binsrv_config_file_path = $MYSQL_TMP_DIR/binsrv_config_validation.json +--let $binsrv_log_path = $MYSQL_TMP_DIR/binsrv_config_validation.log +--let $binsrv_storage_path = $MYSQL_TMP_DIR/config_validation_storage +--let EXPORTED_BINSRV_CONFIG_FILE_PATH = $binsrv_config_file_path +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +--echo +--echo *** Test 1: Empty configuration file +--perl + use strict; + use warnings; + my $config_file = $ENV{'EXPORTED_BINSRV_CONFIG_FILE_PATH'}; + open(my $fh, '>', $config_file) or die "Failed to open config file: $!"; + close($fh) or die "Failed to close config file: $!"; +EOF + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_cmd_output 2>&1 +--let $assert_text = Empty config file should be rejected +--let $assert_file = $binsrv_cmd_output +--let $assert_count = 1 +--let $assert_select = configuration file is empty +--source include/assert_grep.inc + +--remove_file $binsrv_config_file_path +--remove_file $binsrv_cmd_output + +--echo +--echo *** Test 2: Malformed JSON configuration file +--perl + use strict; + use warnings; + my $config_file = $ENV{'EXPORTED_BINSRV_CONFIG_FILE_PATH'}; + open(my $fh, '>', $config_file) or die "Failed to open config file: $!"; + print {$fh} "{\n"; + close($fh) or die "Failed to close config file: $!"; +EOF + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_cmd_output 2>&1 +--let $assert_text = Malformed JSON config should be rejected +--let $assert_file = $binsrv_cmd_output +--let $assert_count = 1 +--let $assert_select = exception caught +--source include/assert_grep.inc + +--remove_file $binsrv_config_file_path +--remove_file $binsrv_cmd_output + +--echo +--echo *** Test 3: Oversized configuration file +--perl + use strict; + use warnings; + my $config_file = $ENV{'EXPORTED_BINSRV_CONFIG_FILE_PATH'}; + open(my $fh, '>', $config_file) or die "Failed to open config file: $!"; + print {$fh} 'x' x (1024 * 1024 + 1); + close($fh) or die "Failed to close config file: $!"; +EOF + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_cmd_output 2>&1 +--let $assert_text = Oversized config file should be rejected +--let $assert_file = $binsrv_cmd_output +--let $assert_count = 1 +--let $assert_select = configuration file is too large +--source include/assert_grep.inc + +--remove_file $binsrv_config_file_path +--remove_file $binsrv_cmd_output + +--echo +--echo *** Preparing reusable local storage directory and config values +--mkdir $binsrv_storage_path +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +eval SET @config_validation_log_path = '$binsrv_log_path'; +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +eval SET @config_validation_storage_uri = CONCAT('file://', '$binsrv_storage_path'); + +--echo +--echo *** Test 4: Rewrite mode cannot be used with position replication +eval SET @binsrv_config_json = JSON_OBJECT( + 'logger', JSON_OBJECT('level', 'error', 'file', @config_validation_log_path), + 'connection', JSON_OBJECT( + 'host', '127.0.0.1', + 'port', @@global.port, + 'user', 'root', + 'password', '', + 'connect_timeout', 20, + 'read_timeout', 60, + 'write_timeout', 60 + ), + 'replication', JSON_OBJECT( + 'server_id', @@server_id + 1, + 'idle_time', 10, + 'verify_checksum', TRUE, + 'mode', 'position', + 'rewrite', JSON_OBJECT('base_file_name', 'rewritten', 'file_size', '1K') + ), + 'storage', JSON_OBJECT( + 'backend', 'file', + 'uri', @config_validation_storage_uri + ) +); +--let $write_var = `SELECT @binsrv_config_json` +--let $write_to_file = $binsrv_config_file_path +--source include/write_var_to_file.inc + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_cmd_output 2>&1 +--let $assert_text = Rewrite mode in position replication should fail validation +--let $assert_file = $binsrv_cmd_output +--let $assert_count = 1 +--let $assert_select = rewrite can only be enabled in gtid replication mode +--source include/assert_grep.inc + +--echo +--echo *** Test 5: Rewrite file size below minimum +eval SET @binsrv_config_json = JSON_SET(@binsrv_config_json, + '$.replication.mode', 'gtid', + '$.replication.rewrite.file_size', '1023' +); +--let $write_var = `SELECT @binsrv_config_json` +--source include/write_var_to_file.inc + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_cmd_output 2>&1 +--let $assert_text = Rewrite file size below 1024 bytes should fail validation +--let $assert_file = $binsrv_cmd_output +--let $assert_count = 1 +--let $assert_select = file size must be >= 1024 bytes +--source include/assert_grep.inc + +--echo +--echo *** Test 6: Invalid checkpoint_size unit +eval SET @binsrv_config_json = JSON_REMOVE(@binsrv_config_json, '$.replication.rewrite'); +eval SET @binsrv_config_json = JSON_SET(@binsrv_config_json, + '$.replication.mode', 'position', + '$.storage.checkpoint_size', 'abc' +); +--let $write_var = `SELECT @binsrv_config_json` +--source include/write_var_to_file.inc + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_cmd_output 2>&1 +--let $assert_text = Invalid checkpoint size should fail parsing +--let $assert_file = $binsrv_cmd_output +--let $assert_count = 1 +--let $assert_select = unable to extract "storage.checkpoint_size" +--source include/assert_grep.inc + +--echo +--echo *** Test 7: Invalid checkpoint_interval unit +eval SET @binsrv_config_json = JSON_REMOVE(@binsrv_config_json, '$.storage.checkpoint_size'); +eval SET @binsrv_config_json = JSON_SET(@binsrv_config_json, + '$.storage.checkpoint_interval', '5w' +); +--let $write_var = `SELECT @binsrv_config_json` +--source include/write_var_to_file.inc + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_cmd_output 2>&1 +--let $assert_text = Invalid checkpoint interval should fail parsing +--let $assert_file = $binsrv_cmd_output +--let $assert_count = 1 +--let $assert_select = unable to extract "storage.checkpoint_interval" +--source include/assert_grep.inc + +--echo +--echo *** Test 8: Connection config without host/port or DNS SRV name +eval SET @binsrv_config_json = JSON_REMOVE(@binsrv_config_json, '$.storage.checkpoint_interval'); +eval SET @binsrv_config_json = JSON_REMOVE(@binsrv_config_json, '$.connection.host'); +eval SET @binsrv_config_json = JSON_REMOVE(@binsrv_config_json, '$.connection.port'); +--let $write_var = `SELECT @binsrv_config_json` +--source include/write_var_to_file.inc + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_cmd_output 2>&1 +--let $assert_text = Connection config must specify DNS SRV or host and port +--let $assert_file = $binsrv_cmd_output +--let $assert_count = 1 +--let $assert_select = either dns_srv_name or both host and port must be specified +--source include/assert_grep.inc + +--echo +--echo *** Test 9: File URI with userinfo +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +eval SET @file_uri_with_userinfo = CONCAT('file://user:pass@', '$binsrv_storage_path'); +eval SET @binsrv_config_json = JSON_SET(@binsrv_config_json, + '$.connection.host', '127.0.0.1', + '$.connection.port', @@global.port, + '$.storage.uri', @file_uri_with_userinfo +); +--let $write_var = `SELECT @binsrv_config_json` +--source include/write_var_to_file.inc + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_cmd_output 2>&1 +--let $assert_text = File URI with userinfo should be rejected +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = file URI must not have userinfo +--source include/assert_grep.inc +--remove_file $binsrv_log_path + +--echo +--echo *** Test 10: File URI with query string +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +eval SET @file_uri_with_query = CONCAT('file://', '$binsrv_storage_path', '?x=1'); +eval SET @binsrv_config_json = JSON_SET(@binsrv_config_json, + '$.storage.uri', @file_uri_with_query +); +--let $write_var = `SELECT @binsrv_config_json` +--source include/write_var_to_file.inc + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_cmd_output 2>&1 +--let $assert_text = File URI with query string should be rejected +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = file URI must not have query +--source include/assert_grep.inc +--remove_file $binsrv_log_path + +--echo +--echo *** Test 11: File URI with missing path +eval SET @binsrv_config_json = JSON_SET(@binsrv_config_json, + '$.storage.uri', 'file://' +); +--let $write_var = `SELECT @binsrv_config_json` +--source include/write_var_to_file.inc + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_cmd_output 2>&1 +--let $assert_text = File URI with missing path should be rejected +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = root path does not exist +--source include/assert_grep.inc +--remove_file $binsrv_log_path + +--echo +--echo *** Test 12: File URI with host +eval SET @binsrv_config_json = JSON_SET(@binsrv_config_json, + '$.storage.uri', 'file://host/path' +); +--let $write_var = `SELECT @binsrv_config_json` +--source include/write_var_to_file.inc + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_cmd_output 2>&1 +--let $assert_text = File URI with host should be rejected +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = file URI must not have host +--source include/assert_grep.inc +--remove_file $binsrv_log_path + +--echo +--echo *** Test 13: Storage directory does not exist +--let $missing_binsrv_storage_path = $MYSQL_TMP_DIR/nonexistent_config_validation_storage +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +eval SET @missing_file_uri = CONCAT('file://', '$missing_binsrv_storage_path'); +eval SET @binsrv_config_json = JSON_SET(@binsrv_config_json, + '$.storage.uri', @missing_file_uri +); +--let $write_var = `SELECT @binsrv_config_json` +--source include/write_var_to_file.inc + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_cmd_output 2>&1 +--let $assert_text = Missing storage directory should be rejected +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = root path does not exist +--source include/assert_grep.inc +--remove_file $binsrv_log_path + +--echo +--echo *** Test 14: Storage path is a file, not a directory +--let $storage_file_path = $MYSQL_TMP_DIR/config_validation_storage_file +--exec echo "dummy file content" > $storage_file_path +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +eval SET @storage_file_uri = CONCAT('file://', '$storage_file_path'); +eval SET @binsrv_config_json = JSON_SET(@binsrv_config_json, + '$.storage.uri', @storage_file_uri +); +--let $write_var = `SELECT @binsrv_config_json` +--source include/write_var_to_file.inc + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_cmd_output 2>&1 +--let $assert_text = Storage path that is a file should be rejected +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = root path is not a directory +--source include/assert_grep.inc +--remove_file $binsrv_log_path + +--echo +--echo *** Test 15: Storage directory containing a subdirectory +eval SET @binsrv_config_json = JSON_SET(@binsrv_config_json, + '$.storage.uri', @config_validation_storage_uri +); +--let $write_var = `SELECT @binsrv_config_json` +--source include/write_var_to_file.inc +--mkdir $binsrv_storage_path/nested_directory + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_cmd_output 2>&1 +--let $assert_text = Storage directory entries must be regular files +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = storage directory contains an entry that is not a regular file +--source include/assert_grep.inc + +--remove_file $storage_file_path +--force-rmdir $binsrv_storage_path/nested_directory +--force-rmdir $binsrv_storage_path +--remove_file $binsrv_config_file_path +--remove_file $binsrv_cmd_output +--remove_file $binsrv_log_path + + diff --git a/mtr/binlog_streaming/t/connection_failures.test b/mtr/binlog_streaming/t/connection_failures.test new file mode 100644 index 0000000..d1f5210 --- /dev/null +++ b/mtr/binlog_streaming/t/connection_failures.test @@ -0,0 +1,330 @@ +--source ../include/have_binsrv.inc + +--source ../include/v80_v84_compatibility_defines.inc + +# identifying backend storage type ('file' or 's3') +--source ../include/identify_storage_backend.inc + +--echo +--echo *** Testing connection failure scenarios + +--echo +--echo *** Test 1: Invalid host (should fail in fetch mode) +--let $binsrv_connection_host = 999.999.999.999 +--let $binsrv_connection_port = 3306 +--let $binsrv_connection_user = root +--let $binsrv_connection_password = +--let $binsrv_connect_timeout = 2 +--let $binsrv_read_timeout = 60 +--let $binsrv_idle_time = 10 +--let $binsrv_verify_checksum = TRUE +--let $binsrv_replication_mode = position +--source ../include/set_up_binsrv_environment.inc + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 + +--let $assert_text = Invalid host should fail while establishing MySQL connection +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = cannot establish MySQL connection: Unknown MySQL server host +--source include/assert_grep.inc + +--echo +--echo *** Cleaning up test environment +if ($storage_backend == file) +{ + --force-rmdir $binsrv_storage_path +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 rm s3://$aws_s3_bucket/$binsrv_storage_path/ --recursive > /dev/null 2>&1 || true + --force-rmdir $binsrv_buffer_path +} +--exec rm -f $binsrv_log_path $binsrv_config_file_path + +--echo +--echo *** Test 2: Invalid port (should fail in fetch mode) +--let $binsrv_connection_host = 127.0.0.1 +--let $binsrv_connection_user = root +--let $binsrv_connection_password = +--let $binsrv_connect_timeout = 2 +--let $binsrv_read_timeout = 60 +--let $binsrv_idle_time = 10 +--let $binsrv_verify_checksum = TRUE +--source ../include/set_up_binsrv_environment.inc + +# Manually modify config to use invalid port using sed +--exec sed -i 's/"port": *[0-9][0-9]*/"port": 99999/' $binsrv_config_file_path + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 + +--let $assert_text = Invalid port should fail config extraction +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = unable to extract "connection.port" +--source include/assert_grep.inc + +--echo +--echo *** Cleaning up test environment +if ($storage_backend == file) +{ + --force-rmdir $binsrv_storage_path +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 rm s3://$aws_s3_bucket/$binsrv_storage_path/ --recursive > /dev/null 2>&1 || true + --force-rmdir $binsrv_buffer_path +} +--exec rm -f $binsrv_log_path $binsrv_config_file_path + +--echo +--echo *** Test 3: Invalid user credentials (should fail in fetch mode) +--let $binsrv_connection_host = 127.0.0.1 +--let $binsrv_connection_user = nonexistent_user_12345 +--let $binsrv_connection_password = wrong_password +--let $binsrv_connect_timeout = 2 +--let $binsrv_read_timeout = 60 +--let $binsrv_idle_time = 10 +--let $binsrv_verify_checksum = TRUE +--source ../include/set_up_binsrv_environment.inc + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 + +--let $assert_text = Invalid credentials should fail with access denied +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = Access denied +--source include/assert_grep.inc + +--echo +--echo *** Cleaning up test environment +if ($storage_backend == file) +{ + --force-rmdir $binsrv_storage_path +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 rm s3://$aws_s3_bucket/$binsrv_storage_path/ --recursive > /dev/null 2>&1 || true + --force-rmdir $binsrv_buffer_path +} +--exec rm -f $binsrv_log_path $binsrv_config_file_path + +--echo +--echo *** Test 4: User without REPLICATION SLAVE privilege (should fail) +--disable_query_log +CREATE USER IF NOT EXISTS 'test_user_no_priv'@'127.0.0.1' IDENTIFIED BY 'testpass'; +--enable_query_log + +--let $binsrv_connection_host = 127.0.0.1 +--let $binsrv_connection_user = test_user_no_priv +--let $binsrv_connection_password = testpass +--let $binsrv_connect_timeout = 2 +--let $binsrv_read_timeout = 60 +--let $binsrv_idle_time = 10 +--let $binsrv_verify_checksum = TRUE +--source ../include/set_up_binsrv_environment.inc + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 + +--let $assert_text = User without replication privilege should fail with access denied +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = Access denied +--source include/assert_grep.inc + +--echo +--echo *** Cleaning up test environment +if ($storage_backend == file) +{ + --force-rmdir $binsrv_storage_path +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 rm s3://$aws_s3_bucket/$binsrv_storage_path/ --recursive > /dev/null 2>&1 || true + --force-rmdir $binsrv_buffer_path +} +--exec rm -f $binsrv_log_path $binsrv_config_file_path + +--disable_query_log +DROP USER IF EXISTS 'test_user_no_priv'@'127.0.0.1'; +--enable_query_log + +--echo +--echo *** Test 5: No mysql server running on host (fail in fetch mode) +--let $binsrv_connection_host = 192.0.2.0 +--let $binsrv_connection_user = root +--let $binsrv_connection_password = +--let $binsrv_connect_timeout = 1 +--let $binsrv_read_timeout = 60 +--let $binsrv_idle_time = 2 +--let $binsrv_verify_checksum = TRUE +--source ../include/set_up_binsrv_environment.inc + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 + +--let $assert_text = Connection timeout should fail while establishing MySQL connection +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = cannot establish MySQL connection: Can't connect to MySQL server on +--source include/assert_grep.inc + +--echo +--echo *** Cleaning up test environment +if ($storage_backend == file) +{ + --force-rmdir $binsrv_storage_path +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 rm s3://$aws_s3_bucket/$binsrv_storage_path/ --recursive > /dev/null 2>&1 || true + --force-rmdir $binsrv_buffer_path +} +--exec rm -f $binsrv_log_path $binsrv_config_file_path + + +--echo +--echo *** Test 6: DNS SRV name with host/port (should fail - validation error) +--let $binsrv_connection_host = 127.0.0.1 +--let $binsrv_connection_user = root +--let $binsrv_connection_password = +--let $binsrv_connect_timeout = 2 +--let $binsrv_read_timeout = 60 +--let $binsrv_idle_time = 10 +--let $binsrv_verify_checksum = TRUE +--let $binsrv_replication_mode = position +--source ../include/set_up_binsrv_environment.inc + +# Manually add dns_srv_name to config (should conflict with host/port) +--exec sed -i 's/"host": "127.0.0.1"/"host": "127.0.0.1",\n "dns_srv_name": "_mysql._tcp.example.com"/' $binsrv_config_file_path + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 + +--let $assert_text = Config validation should fail when both host/port and dns_srv_name are specified +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = either dns_srv_name or both host and port must be specified +--source include/assert_grep.inc + +--echo +--echo *** Cleaning up test environment +if ($storage_backend == file) +{ + --force-rmdir $binsrv_storage_path +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 rm s3://$aws_s3_bucket/$binsrv_storage_path/ --recursive > /dev/null 2>&1 || true + --force-rmdir $binsrv_buffer_path +} +--exec rm -f $binsrv_log_path $binsrv_config_file_path + +--echo +--echo *** Test 7: Invalid DNS SRV name (should fail in fetch mode) +--let $binsrv_connection_user = root +--let $binsrv_connection_password = +--let $binsrv_connect_timeout = 2 +--let $binsrv_read_timeout = 60 +--let $binsrv_idle_time = 10 +--let $binsrv_verify_checksum = TRUE +--let $binsrv_replication_mode = position +--source ../include/set_up_binsrv_environment.inc + +# Manually modify config to use dns_srv_name instead of host/port +--exec sed -i 's/"host": "[^"]*",//' $binsrv_config_file_path +--exec sed -i 's/"port": *[0-9][0-9]*,//' $binsrv_config_file_path +--exec sed -i 's/"user":/"dns_srv_name": "_mysql._tcp.nonexistent.invalid",\n "user":/' $binsrv_config_file_path + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 + +--let $assert_text = Invalid DNS SRV name should fail while establishing MySQL connection +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = cannot establish MySQL connection: DNS SRV lookup failed with error +--source include/assert_grep.inc + +--echo +--echo *** Cleaning up test environment +if ($storage_backend == file) +{ + --force-rmdir $binsrv_storage_path +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 rm s3://$aws_s3_bucket/$binsrv_storage_path/ --recursive > /dev/null 2>&1 || true + --force-rmdir $binsrv_buffer_path +} +--exec rm -f $binsrv_log_path $binsrv_config_file_path + +if ($MTR_BINSRV_DNS_SRV_NAME != '') +{ +--echo +--echo *** Test 8: Valid DNS SRV name (optional - requires DNS configuration) +--echo *** This test is skipped unless MTR_BINSRV_DNS_SRV_NAME environment +--echo *** variable is set to a valid DNS SRV record pointing to the MySQL server. + +--echo +--echo *** Resetting replication at the very beginning of the test. +--disable_query_log +eval $stmt_reset_binary_logs_and_gtids; +--enable_query_log + +--echo +--echo *** Creating a simple table and filling it with some data. +CREATE TABLE t1(id INT UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY(id)) ENGINE=InnoDB; +INSERT INTO t1 VALUES(DEFAULT); +FLUSH BINARY LOGS; + +--let $binsrv_connection_user = root +--let $binsrv_connection_password = +--let $binsrv_connect_timeout = 20 +--let $binsrv_read_timeout = 60 +--let $binsrv_idle_time = 10 +--let $binsrv_verify_checksum = TRUE +--let $binsrv_replication_mode = position +--source ../include/set_up_binsrv_environment.inc + +# Manually modify config to use dns_srv_name instead of host/port +--exec sed -i 's/"host": "[^"]*",//' $binsrv_config_file_path +--exec sed -i 's/"port": *[0-9][0-9]*,//' $binsrv_config_file_path +--exec sed -i "s/\"user\":/\"dns_srv_name\": \"$MTR_BINSRV_DNS_SRV_NAME\",\n \"user\":/" $binsrv_config_file_path + +--echo +--echo *** Testing fetch with valid DNS SRV name (should succeed) +--exec $BINSRV fetch $binsrv_config_file_path > /dev/null + +--echo +--echo *** Verifying that binlog was fetched successfully +if ($storage_backend == file) +{ + --let $assert_text = Binlog file should exist in storage + --let $assert_file = $MYSQL_TMP_DIR/dns_srv_check.log + --exec ls -la $binsrv_storage_path > $assert_file 2>&1 || true + --let $assert_count = 1 + --let $assert_select = binlog + --source include/assert_grep.inc + --remove_file $assert_file +} + +DROP TABLE t1; + +--echo +--echo *** Cleaning up test environment +if ($storage_backend == file) +{ + --force-rmdir $binsrv_storage_path +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 rm s3://$aws_s3_bucket/$binsrv_storage_path/ --recursive > /dev/null 2>&1 || true + --force-rmdir $binsrv_buffer_path +} +--exec rm -f $binsrv_log_path $binsrv_config_file_path +} + diff --git a/mtr/binlog_streaming/t/pull_mode.test b/mtr/binlog_streaming/t/pull_mode.test index e5c2ccb..e1a1b72 100644 --- a/mtr/binlog_streaming/t/pull_mode.test +++ b/mtr/binlog_streaming/t/pull_mode.test @@ -161,12 +161,142 @@ EOF # cleaning up --source ../include/tear_down_binsrv_environment.inc +--echo +--echo *** Pull mode: zero idle_time +--let $binsrv_connect_timeout = 3 +--let $binsrv_read_timeout = 3 +--let $binsrv_idle_time = 0 +--let $binsrv_verify_checksum = TRUE +--let $binsrv_replication_mode = position +--source ../include/set_up_binsrv_environment.inc + +--echo +--echo *** Starting Binlog Server Utility in background in pull mode with zero idle time +--let $binsrv_pid_file = $MYSQL_TMP_DIR/binsrv_pull_zero_idle.pid +--let $binsrv_spawn_cmd_line = $BINSRV pull $binsrv_config_file_path > /dev/null & echo \$! > $binsrv_pid_file + +--let EXPORTED_BINSRV_SPAWN_CMD_LINE = $binsrv_spawn_cmd_line +--perl + use strict; + use warnings; + my $cmd = $ENV{'EXPORTED_BINSRV_SPAWN_CMD_LINE'}; + system("$cmd"); +EOF + +--let $read_from_file = $binsrv_pid_file +--source include/read_file_to_var.inc +--let $binsrv_pid = $result + +--echo +--echo *** Creating data. +CREATE TABLE t1(id INT UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY(id)) ENGINE=InnoDB; +INSERT INTO t1 VALUES(DEFAULT); + +--echo *** Waiting for read timeout to trigger reconnection. +--sleep 4 +INSERT INTO t1 VALUES(DEFAULT); + +--echo +--echo *** Terminating the utility +--sleep 2 +--exec kill -s TERM $binsrv_pid + +--let EXPORTED_BINSRV_PID = $binsrv_pid +--perl + use strict; + use warnings; + use Errno; + my $pid = $ENV{'EXPORTED_BINSRV_PID'}; + my $not_present = (!kill(0, $pid) && $! == Errno::ESRCH); + while (!$not_present) { + sleep(1); + $not_present = (!kill(0, $pid) && $! == Errno::ESRCH); + } +EOF + +--remove_file $binsrv_pid_file + +--source ../include/tear_down_binsrv_environment.inc + +--echo +--echo *** Pull mode: very short timeouts and rapid reconnection +--let $binsrv_connect_timeout = 1 +--let $binsrv_read_timeout = 1 +--let $binsrv_idle_time = 1 +--let $binsrv_verify_checksum = TRUE +--let $binsrv_replication_mode = position +--source ../include/set_up_binsrv_environment.inc + +--echo +--echo *** Starting Binlog Server Utility in background +--let $binsrv_pid_file = $MYSQL_TMP_DIR/binsrv_pull_short_timeout.pid +--let $binsrv_spawn_cmd_line = $BINSRV pull $binsrv_config_file_path > /dev/null & echo \$! > $binsrv_pid_file + +--let EXPORTED_BINSRV_SPAWN_CMD_LINE = $binsrv_spawn_cmd_line +--perl + use strict; + use warnings; + my $cmd = $ENV{'EXPORTED_BINSRV_SPAWN_CMD_LINE'}; + system("$cmd"); +EOF + +--let $read_from_file = $binsrv_pid_file +--source include/read_file_to_var.inc +--let $binsrv_pid = $result + +--echo +--echo *** Creating data and restarting server multiple times +INSERT INTO t1 VALUES(DEFAULT); +FLUSH BINARY LOGS; + +--source include/shutdown_mysqld.inc +--sleep 2 +--source include/start_mysqld.inc + +INSERT INTO t1 VALUES(DEFAULT); +FLUSH BINARY LOGS; + +--source include/shutdown_mysqld.inc +--sleep 2 +--source include/start_mysqld.inc + +INSERT INTO t1 VALUES(DEFAULT); + +--echo +--echo *** Terminating the utility +--exec kill -s TERM $binsrv_pid + +--let EXPORTED_BINSRV_PID = $binsrv_pid +--perl + use strict; + use warnings; + use Errno; + my $pid = $ENV{'EXPORTED_BINSRV_PID'}; + my $not_present = (!kill(0, $pid) && $! == Errno::ESRCH); + while (!$not_present) { + sleep(1); + $not_present = (!kill(0, $pid) && $! == Errno::ESRCH); + } +EOF + +--remove_file $binsrv_pid_file + +--source ../include/tear_down_binsrv_environment.inc + +DROP TABLE t1; + # As the Binlog Server Utility interrupts the connection upon timeout, here we # need to close it on the MySQL server side as well in order to make sure that # MTR 'check-test' before and after the test produces the same output. --let $binlog_dump_connection_id = `SELECT ID FROM performance_schema.processlist WHERE COMMAND = 'Binlog Dump'` ---replace_result $binlog_dump_connection_id -eval KILL CONNECTION $binlog_dump_connection_id; +if ($binlog_dump_connection_id != '') +{ + --disable_query_log + --disable_result_log + eval KILL CONNECTION $binlog_dump_connection_id; + --enable_result_log + --enable_query_log +} # Also, we use 'count_sessions' include files to make sure that 'Binlog Dump' # connection is indeed closed. diff --git a/mtr/binlog_streaming/t/read_timeout_flush-master.opt b/mtr/binlog_streaming/t/read_timeout_flush-master.opt new file mode 100644 index 0000000..cc29e98 --- /dev/null +++ b/mtr/binlog_streaming/t/read_timeout_flush-master.opt @@ -0,0 +1,4 @@ +--gtid-mode=on +--enforce-gtid-consistency +--log-slave-updates + diff --git a/mtr/binlog_streaming/t/read_timeout_flush.test b/mtr/binlog_streaming/t/read_timeout_flush.test new file mode 100644 index 0000000..9684b38 --- /dev/null +++ b/mtr/binlog_streaming/t/read_timeout_flush.test @@ -0,0 +1,279 @@ +--source ../include/have_binsrv.inc + +--source ../include/v80_v84_compatibility_defines.inc + +--source include/count_sessions.inc + +# identifying backend storage type ('file' or 's3') +--source ../include/identify_storage_backend.inc + +--echo +--echo *** Test : Storage flush after read_timeout disconnection (PS-10320 fix) +--echo *** This test verifies that local binary log data is flushed to storage +--echo *** upon disconnection due to read_timeout +--echo *** This test also verifies existence of teporary files in buffer directory +--echo *** in S3 storage mode while binlog_server run is in progress. +--disable_query_log +eval $stmt_reset_binary_logs_and_gtids; +--enable_query_log + +# To test compressed events with binlog_server +SET SESSION binlog_transaction_compression=ON; + +CREATE TABLE t2(id INT UNSIGNED NOT NULL AUTO_INCREMENT, val CHAR(64), PRIMARY KEY(id)) ENGINE=InnoDB; + +--echo +--echo *** Starting with a fresh binlog +FLUSH BINARY LOGS; +--let $test_binlog = query_get_value($stmt_show_binary_log_status, File, 1) + +--echo +--echo *** Setting up binsrv with short read_timeout to trigger timeout +--let $binsrv_connect_timeout = 10 +--let $binsrv_read_timeout = 20 +--let $binsrv_idle_time = 30 +--let $binsrv_verify_checksum = TRUE +--let $binsrv_replication_mode = position +--source ../include/set_up_binsrv_environment.inc + +--echo +--echo *** Starting Binlog Server Utility in background in pull mode +--let $binsrv_pid_file = $MYSQL_TMP_DIR/binsrv_read_timeout_test.pid +--let $binsrv_spawn_cmd_line = $BINSRV pull $binsrv_config_file_path > /dev/null & echo \$! > $binsrv_pid_file + +--let EXPORTED_BINSRV_SPAWN_CMD_LINE = $binsrv_spawn_cmd_line +--perl + use strict; + use warnings; + my $cmd = $ENV{'EXPORTED_BINSRV_SPAWN_CMD_LINE'}; + system("$cmd"); +EOF + +--let $read_from_file = $binsrv_pid_file +--source include/read_file_to_var.inc +--let $binsrv_pid = $result + +--echo +--echo *** Waiting for binsrv to initialize and create binlog file in storage +--sleep 4 + +--echo +--echo *** Step 1: Check initial binlog size (baseline) +--let $size_file_initial = $MYSQL_TMP_DIR/storage_size_initial.txt +if ($storage_backend == file) +{ + --exec /bin/sh -c "wc -c < '$binsrv_storage_path/$test_binlog' > '$size_file_initial'" +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3api head-object --bucket $MTR_BINSRV_AWS_S3_BUCKET --key $binsrv_storage_path/$test_binlog --query ContentLength --output text > $size_file_initial 2>&1 || echo 0 > $size_file_initial +} +--let $read_from_file = $size_file_initial +--source include/read_file_to_var.inc +--let $size_initial = $result +--remove_file $size_file_initial + +--echo +--echo *** Step 2: Execute INSERT and wait for read_timeout to trigger flush (read_timeout=20, wait 25 seconds) +INSERT INTO t2(val) VALUES('test_data_before_timeout'); + +--disable_result_log +if ($storage_backend == s3) +{ + --let $assert_text = Buffer directory should contain temporary files (UUID format) + --let $assert_file = $MYSQL_TMP_DIR/buffer_check.log + --disable_result_log + --exec ls -1 $binsrv_buffer_path 2>&1 | grep -v '^\.$' | grep -v '^\.\.$' > $assert_file || true + --let $assert_count = 1 + --let $assert_select = -.*-.*-.*- + --source include/assert_grep.inc + --enable_result_log + --remove_file $assert_file +} +--enable_result_log + +--sleep 25 + +--echo +--echo *** Step 3: Check binlog size after read_timeout flush (should have increased) +--let $size_file_after_first_timeout = $MYSQL_TMP_DIR/storage_size_after_first_timeout.txt +if ($storage_backend == file) +{ + --exec /bin/sh -c "wc -c < '$binsrv_storage_path/$test_binlog' > '$size_file_after_first_timeout'" +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3api head-object --bucket $MTR_BINSRV_AWS_S3_BUCKET --key $binsrv_storage_path/$test_binlog --query ContentLength --output text > $size_file_after_first_timeout 2>/dev/null || echo 0 > $size_file_after_first_timeout +} +--let $read_from_file = $size_file_after_first_timeout +--source include/read_file_to_var.inc +--let $size_after_first_timeout = $result + +--remove_file $size_file_after_first_timeout + +--echo +--echo *** Verifying that data was flushed to storage after first read_timeout (PS-10320 fix) +--let $size_increased_after_first = `SELECT IF($size_after_first_timeout > $size_initial, 1, 0)` +if ($size_increased_after_first != 1) +{ + --die storage object size should have increased after first read_timeout flush, but it did not +} + +--echo +--echo *** Step 4: Wait around idle_time and generate new data in the binary log. verify either in idle_time or upon reconnection data was not flushed to storage. +--sleep 10 +INSERT INTO t2(val) VALUES('test_data_after_timeout'); + +# To test update_rows_partial event with binlog_server +SET @save_binlog_row_value_options = @@GLOBAL.binlog_row_value_options; +SET GLOBAL binlog_row_value_options = PARTIAL_JSON; + +UPDATE t2 set val='update_test_data_after_timeout' where val='test_data_after_timeout'; + +CREATE TABLE tj (j JSON); +INSERT INTO tj VALUES('{"name":"john","age":30,"city":"blr"}'); +INSERT INTO tj VALUES('{"arr":[1,2,3],"details":{"salary":5000}}'); +UPDATE tj SET j = JSON_REPLACE(j, '$.city', 'mysore') WHERE JSON_EXTRACT(j, '$.name') = 'john'; +UPDATE tj SET j = JSON_REPLACE(j, '$.arr[1]', 999); +DROP TABLE tj; +SET GLOBAL binlog_row_value_options = @save_binlog_row_value_options; + +--echo +--echo *** Step 5: Check binlog size after around idle time (should NOT have changed - since no flush) +--let $size_file_after_reconnection = $MYSQL_TMP_DIR/storage_size_after_reconnection.txt +if ($storage_backend == file) +{ + --exec /bin/sh -c "wc -c < '$binsrv_storage_path/$test_binlog' > '$size_file_after_reconnection'" +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3api head-object --bucket $MTR_BINSRV_AWS_S3_BUCKET --key $binsrv_storage_path/$test_binlog --query ContentLength --output text > $size_file_after_reconnection 2>/dev/null || echo 0 > $size_file_after_reconnection +} +--let $read_from_file = $size_file_after_reconnection +--source include/read_file_to_var.inc +--let $size_after_reconnection = $result +--remove_file $size_file_after_reconnection + +--echo +--echo *** Verifying that size did NOT change after reconnection (no flush) +--let $size_unchanged = `SELECT IF($size_after_reconnection = $size_after_first_timeout, 1, 0)` +if ($size_unchanged != 1) +{ + --die storage object size should NOT have changed after reconnection (no new data), but it did +} + +--echo +--echo *** Step 6: Execute INSERT again +INSERT INTO t2(val) VALUES('test_data_after_timeout'); + +--echo +--echo *** Step 7: Check size immediately after INSERT ie around idle/reconnection time (should NOT have changed - data in buffer) +--let $size_file_immediate = $MYSQL_TMP_DIR/storage_size_immediate.txt +if ($storage_backend == file) +{ + --exec /bin/sh -c "wc -c < '$binsrv_storage_path/$test_binlog' > '$size_file_immediate'" +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3api head-object --bucket $MTR_BINSRV_AWS_S3_BUCKET --key $binsrv_storage_path/$test_binlog --query ContentLength --output text > $size_file_immediate 2>/dev/null || echo 0 > $size_file_immediate +} +--let $read_from_file = $size_file_immediate +--source include/read_file_to_var.inc +--let $size_immediate = $result +--remove_file $size_file_immediate + +--echo +--echo *** Verifying that size did NOT change immediately after INSERT (data still in buffer) +--let $size_unchanged_immediate = `SELECT IF($size_immediate = $size_after_reconnection, 1, 0)` +if ($size_unchanged_immediate != 1) +{ + --die storage object size should NOT have changed immediately after INSERT (data in buffer), but it did +} + +--echo +--echo *** Step 8: Wait for read_timeout to trigger flush again (read_timeout=20) +--sleep 35 + +--echo +--echo *** Step 9: Check binlog size after second read_timeout flush (should have increased) +--let $size_file_after_second_timeout = $MYSQL_TMP_DIR/storage_size_after_second_timeout.txt +if ($storage_backend == file) +{ + --exec /bin/sh -c "wc -c < '$binsrv_storage_path/$test_binlog' > '$size_file_after_second_timeout'" +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3api head-object --bucket $MTR_BINSRV_AWS_S3_BUCKET --key $binsrv_storage_path/$test_binlog --query ContentLength --output text > $size_file_after_second_timeout 2>/dev/null || echo 0 > $size_file_after_second_timeout +} +--let $read_from_file = $size_file_after_second_timeout +--source include/read_file_to_var.inc +--let $size_after_second_timeout = $result +--remove_file $size_file_after_second_timeout + +--echo +--echo *** Verifying that size increased after second read_timeout flush +--let $size_increased_after_second = `SELECT IF($size_after_second_timeout > $size_immediate, 1, 0)` +if ($size_increased_after_second != 1) +{ + --die storage object size should have increased after second read_timeout flush, but it did not +} + +FLUSH BINARY LOGS; + +--echo +--echo *** Terminating the utility +--exec kill -s TERM $binsrv_pid + +--let EXPORTED_BINSRV_PID = $binsrv_pid +--perl + use strict; + use warnings; + use Errno; + my $pid = $ENV{'EXPORTED_BINSRV_PID'}; + my $not_present = (!kill(0, $pid) && $! == Errno::ESRCH); + while (!$not_present) { + sleep(1); + $not_present = (!kill(0, $pid) && $! == Errno::ESRCH); + } +EOF + +--remove_file $binsrv_pid_file + +--disable_result_log +if ($storage_backend == s3) +{ + --let $assert_text = Temporary files shall be cleaned up in buffer directory (UUID format) + --let $assert_file = $MYSQL_TMP_DIR/buffer_check.log + --disable_result_log + --exec ls -1 $binsrv_buffer_path 2>&1 | grep -v '^\.$' | grep -v '^\.\.$' > $assert_file || true + --let $assert_count = 0 + --let $assert_select = -.*-.*-.*- + --source include/assert_grep.inc + --enable_result_log + --remove_file $assert_file +} +--enable_result_log + +--exec $BINSRV fetch $binsrv_config_file_path > /dev/null + +--echo +--echo *** Final verification: Compare storage object with server binlog +--let $local_file = $binlog_base_dir/$test_binlog +--let $storage_object = $binsrv_storage_path/$test_binlog +--source ../include/diff_with_storage_object.inc + +--source ../include/tear_down_binsrv_environment.inc + +DROP TABLE t2; + +# Clean up any remaining binlog dump connections +--let $binlog_dump_connection_id = `SELECT ID FROM performance_schema.processlist WHERE COMMAND = 'Binlog Dump'` +if ($binlog_dump_connection_id != '') +{ + --disable_query_log + --replace_result $binlog_dump_connection_id + eval KILL CONNECTION $binlog_dump_connection_id; + --enable_query_log +} + +--source include/wait_until_count_sessions.inc diff --git a/mtr/binlog_streaming/t/rewrite_mode-master.opt b/mtr/binlog_streaming/t/rewrite_mode-master.opt new file mode 100644 index 0000000..c7529f0 --- /dev/null +++ b/mtr/binlog_streaming/t/rewrite_mode-master.opt @@ -0,0 +1,2 @@ +--gtid-mode=on +--enforce-gtid-consistency diff --git a/mtr/binlog_streaming/t/rewrite_mode.test b/mtr/binlog_streaming/t/rewrite_mode.test new file mode 100644 index 0000000..02597e9 --- /dev/null +++ b/mtr/binlog_streaming/t/rewrite_mode.test @@ -0,0 +1,290 @@ +--source ../include/have_binsrv.inc + +--source ../include/v80_v84_compatibility_defines.inc + +# identifying backend storage type ('file' or 's3') +--source ../include/identify_storage_backend.inc + +--echo +--echo *** Testing rewrite mode with custom base file name and split size + +--echo +--echo *** Resetting replication at the very beginning of the test. +--disable_query_log +eval $stmt_reset_binary_logs_and_gtids; +--enable_query_log + +--echo +--echo *** Determining the original server binlog name. +--let $original_first_binlog = query_get_value($stmt_show_binary_log_status, File, 1) + +--let $gtid_executed_initial = `SELECT @@global.gtid_executed` + +--echo +--echo *** Creating a table and capturing GTIDs before source binlog rotation. +CREATE TABLE t1( + id SERIAL, + val CHAR(64) CHARACTER SET ascii NOT NULL, + PRIMARY KEY(id) +) ENGINE=InnoDB; + +INSERT INTO t1(val) VALUES(SHA2('before-rotation', 256)); +--let $gtid_executed_after_first_insert = `SELECT @@global.gtid_executed` + +--echo +--echo *** Rotating source binlog; rewrite mode should ignore source file names. +FLUSH BINARY LOGS; +--let $original_second_binlog = query_get_value($stmt_show_binary_log_status, File, 1) + +--echo +--echo *** Generating enough GTID transactions to force rewrite file splitting. +--disable_query_log +--let $transaction_idx = 0 +while ($transaction_idx < 20) +{ + eval INSERT INTO t1(val) VALUES(SHA2(CONCAT('rewrite-split-', $transaction_idx), 256)); + --inc $transaction_idx +} +--enable_query_log +--let $gtid_executed_after_split_workload = `SELECT @@global.gtid_executed` +--let $split_workload_gtids = `SELECT GTID_SUBTRACT(@@global.gtid_executed, '$gtid_executed_after_first_insert')` + +--echo +--echo *** Setting up rewrite mode with a custom base file name and 1K split size. +--let $binsrv_connect_timeout = 20 +--let $binsrv_read_timeout = 60 +--let $binsrv_idle_time = 10 +--let $binsrv_verify_checksum = TRUE +--let $binsrv_replication_mode = gtid +--let $binsrv_checkpoint_size = 1 +--let $binsrv_rewrite_file_size = 1K +--source ../include/set_up_binsrv_environment.inc + +--let $rewrite_base_file_name = custom_rewrite +eval SET @binsrv_config_json = JSON_SET(@binsrv_config_json, '$.replication.rewrite.base_file_name', '$rewrite_base_file_name'); +--let $write_var = `SELECT @binsrv_config_json` +--let $write_to_file = $binsrv_config_file_path +--source include/write_var_to_file.inc + +--echo +--echo *** Test 1: Fetching in rewrite mode should create rewritten binlogs. +--exec $BINSRV fetch $binsrv_config_file_path > /dev/null + +--let $storage_index_file = $MYSQL_TMP_DIR/rewrite_mode_binlog.index +if ($storage_backend == file) +{ + --copy_file $binsrv_storage_path/binlog.index $storage_index_file +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 cp s3://$aws_s3_bucket/$binsrv_storage_path/binlog.index $storage_index_file > /dev/null +} + +--let $assert_text = Rewrite mode should use the configured base file name +--let $assert_file = $storage_index_file +--let $assert_count = 1 +--let $assert_select = ./$rewrite_base_file_name.000001 +--source include/assert_grep.inc + +--let $assert_text = Rewrite mode should split files when configured size is reached +--let $assert_file = $storage_index_file +--let $assert_count = 1 +--let $assert_select = ./$rewrite_base_file_name.000002 +--source include/assert_grep.inc + +--let $assert_text = Rewrite mode should not keep the original first source binlog name +--let $assert_file = $storage_index_file +--let $assert_count = 0 +--let $assert_select = $original_first_binlog +--source include/assert_grep.inc + +--let $assert_text = Rewrite mode should not keep the original second source binlog name +--let $assert_file = $storage_index_file +--let $assert_count = 0 +--let $assert_select = $original_second_binlog +--source include/assert_grep.inc + +--echo +--echo *** Test 2: GTID search over split workload should span rewritten files. +--let $read_from_file = $MYSQL_TMP_DIR/rewrite_mode_search_result.json +--exec $BINSRV search_by_gtid_set $binsrv_config_file_path "$split_workload_gtids" > $read_from_file +--source include/read_file_to_var.inc +--assert(`SELECT JSON_EXTRACT('$result', '$.status') = 'success'`) +--assert(`SELECT JSON_LENGTH(JSON_EXTRACT('$result', '$.result')) >= 2`) +--assert(`SELECT JSON_UNQUOTE(JSON_EXTRACT('$result', '$.result[0].name')) LIKE '$rewrite_base_file_name.%'`) +--assert(`SELECT JSON_UNQUOTE(JSON_EXTRACT('$result', '$.result[1].name')) LIKE '$rewrite_base_file_name.%'`) +--assert(`SELECT JSON_EXTRACT('$result', '$.result[0].name') != '$original_first_binlog'`) +--assert(`SELECT JSON_CONTAINS_PATH('$result', 'one', '$.result[0].previous_gtids')`) +--assert(`SELECT JSON_CONTAINS_PATH('$result', 'one', '$.result[0].added_gtids')`) + +--echo +--echo *** Generating more data after the first fetch to verify rewrite resume. +--disable_query_log +--let $transaction_idx = 0 +while ($transaction_idx < 10) +{ + eval INSERT INTO t1(val) VALUES(SHA2(CONCAT('rewrite-resume-', $transaction_idx), 256)); + --inc $transaction_idx +} +--enable_query_log +--let $gtid_executed_after_resume_workload = `SELECT @@global.gtid_executed` +--let $resume_workload_gtids = `SELECT GTID_SUBTRACT(@@global.gtid_executed, '$gtid_executed_after_split_workload')` + +--echo +--echo *** Test 3: Fetching again should resume rewritten storage. +--exec $BINSRV fetch $binsrv_config_file_path > /dev/null + +--remove_file $storage_index_file +if ($storage_backend == file) +{ + --copy_file $binsrv_storage_path/binlog.index $storage_index_file +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 cp s3://$aws_s3_bucket/$binsrv_storage_path/binlog.index $storage_index_file > /dev/null +} + +--let $assert_text = Rewrite resume should preserve the configured base file name +--let $assert_file = $storage_index_file +--let $assert_count = 0 +--let $assert_select = $original_first_binlog +--source include/assert_grep.inc + +--exec $BINSRV search_by_gtid_set $binsrv_config_file_path "$resume_workload_gtids" > $read_from_file +--source include/read_file_to_var.inc +--assert(`SELECT JSON_EXTRACT('$result', '$.status') = 'success'`) +--assert(`SELECT JSON_LENGTH(JSON_EXTRACT('$result', '$.result')) >= 1`) +--assert(`SELECT JSON_UNQUOTE(JSON_EXTRACT('$result', '$.result[0].name')) LIKE '$rewrite_base_file_name.%'`) + +--echo +--echo *** Test 4: Timestamp search should also expose rewritten file names. +--let $future_timestamp = `SELECT DATE_FORMAT(CONVERT_TZ(NOW() + INTERVAL 1 DAY, @@session.time_zone, '+00:00'),'%Y-%m-%dT%H:%i:%s')` +--exec $BINSRV search_by_timestamp $binsrv_config_file_path $future_timestamp > $read_from_file +--source include/read_file_to_var.inc +--assert(`SELECT JSON_EXTRACT('$result', '$.status') = 'success'`) +--assert(`SELECT JSON_LENGTH(JSON_EXTRACT('$result', '$.result')) >= 2`) +--assert(`SELECT JSON_EXTRACT('$result', '$.result[0].name') = '$rewrite_base_file_name.000001'`) +--assert(`SELECT JSON_UNQUOTE(JSON_EXTRACT('$result', '$.result[1].name')) LIKE '$rewrite_base_file_name.%'`) + +--echo +--echo *** Removing temporary files. +--remove_file $storage_index_file +--remove_file $read_from_file + +--source ../include/tear_down_binsrv_environment.inc + +--echo +--echo *** Testing rewrite mode with large file size across source rotations. +--disable_query_log +eval $stmt_reset_binary_logs_and_gtids; +--enable_query_log + +--let $large_gtid_executed_initial = `SELECT @@global.gtid_executed` + +--echo +--echo *** Creating a table and generating transactions in the first source binlog. +CREATE TABLE t2( + id SERIAL, + val CHAR(64) CHARACTER SET ascii NOT NULL, + PRIMARY KEY(id) +) ENGINE=InnoDB; + +--disable_query_log +--let $transaction_idx = 0 +while ($transaction_idx < 5) +{ + eval INSERT INTO t2(val) VALUES(SHA2(CONCAT('large-rewrite-first-', $transaction_idx), 256)); + --inc $transaction_idx +} +--enable_query_log + +--echo +--echo *** Rotating source binlog and generating more transactions. +FLUSH BINARY LOGS; + +--disable_query_log +--let $transaction_idx = 0 +while ($transaction_idx < 5) +{ + eval INSERT INTO t2(val) VALUES(SHA2(CONCAT('large-rewrite-second-', $transaction_idx), 256)); + --inc $transaction_idx +} +--enable_query_log + +--echo +--echo *** Rotating source binlog again and generating final transactions. +FLUSH BINARY LOGS; +--disable_query_log +--let $transaction_idx = 0 +while ($transaction_idx < 5) +{ + eval INSERT INTO t2(val) VALUES(SHA2(CONCAT('large-rewrite-third-', $transaction_idx), 256)); + --inc $transaction_idx +} +--enable_query_log +--let $large_workload_gtids = `SELECT GTID_SUBTRACT(@@global.gtid_executed, '$large_gtid_executed_initial')` + +--echo +--echo *** Setting up rewrite mode with a large file size. +--let $binsrv_connect_timeout = 20 +--let $binsrv_read_timeout = 60 +--let $binsrv_idle_time = 10 +--let $binsrv_verify_checksum = TRUE +--let $binsrv_replication_mode = gtid +--let $binsrv_checkpoint_size = 1 +--let $binsrv_rewrite_file_size = 1M +--source ../include/set_up_binsrv_environment.inc + +--let $large_rewrite_base_file_name = large_rewrite +eval SET @binsrv_config_json = JSON_SET(@binsrv_config_json, '$.replication.rewrite.base_file_name', '$large_rewrite_base_file_name'); +--let $write_var = `SELECT @binsrv_config_json` +--let $write_to_file = $binsrv_config_file_path +--source include/write_var_to_file.inc + +--echo +--echo *** Test 5: Large rewrite file size should keep all source rotations in one rewritten binlog. +--exec $BINSRV fetch $binsrv_config_file_path > /dev/null + +--let $storage_index_file = $MYSQL_TMP_DIR/rewrite_mode_large_binlog.index +if ($storage_backend == file) +{ + --copy_file $binsrv_storage_path/binlog.index $storage_index_file +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 cp s3://$aws_s3_bucket/$binsrv_storage_path/binlog.index $storage_index_file > /dev/null +} + +--let $assert_text = Large rewrite file size should create the first rewritten binlog +--let $assert_file = $storage_index_file +--let $assert_count = 1 +--let $assert_select = ./$large_rewrite_base_file_name.000001 +--source include/assert_grep.inc + +--let $assert_text = Large rewrite file size should not rotate to a second rewritten binlog +--let $assert_file = $storage_index_file +--let $assert_count = 0 +--let $assert_select = ./$large_rewrite_base_file_name.000002 +--source include/assert_grep.inc + +--echo +--echo *** Test 6: GTID search across source rotations should return one rewritten binlog. +--let $read_from_file = $MYSQL_TMP_DIR/rewrite_mode_large_search_result.json +--exec $BINSRV search_by_gtid_set $binsrv_config_file_path "$large_workload_gtids" > $read_from_file +--source include/read_file_to_var.inc +--assert(`SELECT JSON_EXTRACT('$result', '$.status') = 'success'`) +--assert(`SELECT JSON_LENGTH(JSON_EXTRACT('$result', '$.result')) = 1`) +--assert(`SELECT JSON_EXTRACT('$result', '$.result[0].name') = '$large_rewrite_base_file_name.000001'`) + +--remove_file $storage_index_file +--remove_file $read_from_file + +--source ../include/tear_down_binsrv_environment.inc + +--echo +--echo *** Dropping the tables. +DROP TABLE t1; +DROP TABLE t2; + +# cleaning up + diff --git a/mtr/binlog_streaming/t/rewrite_mode_transition_and_failover-master.opt b/mtr/binlog_streaming/t/rewrite_mode_transition_and_failover-master.opt new file mode 100644 index 0000000..cc29e98 --- /dev/null +++ b/mtr/binlog_streaming/t/rewrite_mode_transition_and_failover-master.opt @@ -0,0 +1,4 @@ +--gtid-mode=on +--enforce-gtid-consistency +--log-slave-updates + diff --git a/mtr/binlog_streaming/t/rewrite_mode_transition_and_failover-slave.opt b/mtr/binlog_streaming/t/rewrite_mode_transition_and_failover-slave.opt new file mode 100644 index 0000000..cc29e98 --- /dev/null +++ b/mtr/binlog_streaming/t/rewrite_mode_transition_and_failover-slave.opt @@ -0,0 +1,4 @@ +--gtid-mode=on +--enforce-gtid-consistency +--log-slave-updates + diff --git a/mtr/binlog_streaming/t/rewrite_mode_transition_and_failover.cnf b/mtr/binlog_streaming/t/rewrite_mode_transition_and_failover.cnf new file mode 100644 index 0000000..db6816e --- /dev/null +++ b/mtr/binlog_streaming/t/rewrite_mode_transition_and_failover.cnf @@ -0,0 +1,12 @@ +!include suite/rpl/my.cnf + +[mysqld.1] +gtid-mode=ON +enforce-gtid-consistency +log-slave-updates + +[mysqld.2] +gtid-mode=ON +enforce-gtid-consistency +log-slave-updates + diff --git a/mtr/binlog_streaming/t/rewrite_mode_transition_and_failover.test b/mtr/binlog_streaming/t/rewrite_mode_transition_and_failover.test new file mode 100644 index 0000000..36e5097 --- /dev/null +++ b/mtr/binlog_streaming/t/rewrite_mode_transition_and_failover.test @@ -0,0 +1,344 @@ +--source ../include/have_binsrv.inc + +--source ../include/v80_v84_compatibility_defines.inc + +# This test relies on the standard MTR async replication harness from the +# MySQL Server mysql-test tree. + +--disable_warnings +--replace_result $init_rpl_inc include/rpl/init_source_replica.inc +--source $init_rpl_inc +--enable_warnings + +# identifying backend storage type ('file' or 's3') +--source ../include/identify_storage_backend.inc + +--echo +--echo *** Testing transition from normal GTID fetch to rewrite-mode and failover to replica. + +--connection master +--let $source_port = `SELECT @@global.port` +--let $gtid_executed_initial = `SELECT @@global.gtid_executed` +--let $source_first_binlog = query_get_value($stmt_show_binary_log_status, File, 1) +--let $source_binlog_base_file_name = `SELECT REGEXP_REPLACE('$source_first_binlog', '\\.[0-9]+$', '')` + +--echo +--echo *** Creating initial source workload for a non-rewrite fetch. +CREATE TABLE t1( + id SERIAL, + val CHAR(64) CHARACTER SET ascii NOT NULL, + PRIMARY KEY(id) +) ENGINE=InnoDB; +--let $gtid_executed_after_create = `SELECT @@global.gtid_executed` +--let $create_table_gtid = `SELECT GTID_SUBTRACT(@@global.gtid_executed, '$gtid_executed_initial')` + +--disable_query_log +--let $transaction_idx = 0 +while ($transaction_idx < 8) +{ + eval INSERT INTO t1(val) VALUES(SHA2(CONCAT('normal-source-', $transaction_idx), 256)); + --inc $transaction_idx +} +--enable_query_log +--let $gtid_executed_after_initial_workload = `SELECT @@global.gtid_executed` + +--echo +--echo *** Rotating source logs before the first fetch. +FLUSH BINARY LOGS; +--let $source_second_binlog = query_get_value($stmt_show_binary_log_status, File, 1) + +--connection master +--echo +--echo *** Setting up normal GTID fetch from source, without rewrite mode. +--let $binsrv_connection_host = 127.0.0.1 +--let $binsrv_connection_user = root +--let $binsrv_connection_password = +--let $binsrv_connect_timeout = 20 +--let $binsrv_read_timeout = 60 +--let $binsrv_idle_time = 10 +--let $binsrv_verify_checksum = TRUE +--let $binsrv_replication_mode = gtid +--let $binsrv_checkpoint_size = 1 +--source ../include/set_up_binsrv_environment.inc + +--replace_result $source_port SOURCE_PORT +eval SET @binsrv_config_json = JSON_SET(@binsrv_config_json, + '$.connection.port', $source_port +); +--let $write_var = `SELECT @binsrv_config_json` +--let $write_to_file = $binsrv_config_file_path +--source include/write_var_to_file.inc + +--echo +--echo *** Test 1: First fetch should store source binlog names. +--exec $BINSRV fetch $binsrv_config_file_path > /dev/null + +--let $storage_index_file = $MYSQL_TMP_DIR/rewrite_transition_binlog.index +if ($storage_backend == file) +{ + --copy_file $binsrv_storage_path/binlog.index $storage_index_file +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 cp s3://$aws_s3_bucket/$binsrv_storage_path/binlog.index $storage_index_file > /dev/null +} + +--let $assert_text = Fetch should keep the source first binlog name +--let $assert_file = $storage_index_file +--let $assert_count = 1 +--let $assert_select = ./$source_first_binlog +--source include/assert_grep.inc + +--let $assert_text = Fetch should keep the source second binlog name +--let $assert_file = $storage_index_file +--let $assert_count = 1 +--let $assert_select = ./$source_second_binlog +--source include/assert_grep.inc + +--remove_file $storage_index_file + +--echo +--echo *** Generating workload for transition scenario from normal to rewrite mode. +--disable_query_log +--let $transaction_idx = 0 +while ($transaction_idx < 14) +{ + eval INSERT INTO t1(val) VALUES(SHA2(CONCAT('rewrite-transition-', $transaction_idx), 256)); + --inc $transaction_idx +} +--enable_query_log +--let $gtid_executed_after_transition_workload = `SELECT @@global.gtid_executed` +--let $transition_workload_gtids = `SELECT GTID_SUBTRACT(@@global.gtid_executed, '$gtid_executed_after_initial_workload')` + +--echo +--echo *** Rotating source and syncing replica. +FLUSH BINARY LOGS; + +--save_master_pos +--connection slave +--sync_with_master + +--let $replica_port = `SELECT @@global.port` +--let $replica_first_binlog = query_get_value($stmt_show_binary_log_status, File, 1) + +--echo +--echo *** Rotating replica logs independently from source. +FLUSH BINARY LOGS; +FLUSH BINARY LOGS; +--let $replica_rotated_binlog = query_get_value($stmt_show_binary_log_status, File, 1) + +--connection master +--echo +--echo *** Test 2: Enabling rewrite mode. +--let $rewrite_base_file_name = transition_rewrite + +eval SET @binsrv_config_json = JSON_INSERT(@binsrv_config_json, + '$.replication.rewrite', + JSON_OBJECT('base_file_name', '$rewrite_base_file_name', 'file_size', '1K') +); +--let $write_var = `SELECT @binsrv_config_json` +--source include/write_var_to_file.inc + +--exec $BINSRV fetch $binsrv_config_file_path > /dev/null + +if ($storage_backend == file) +{ + --copy_file $binsrv_storage_path/binlog.index $storage_index_file +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 cp s3://$aws_s3_bucket/$binsrv_storage_path/binlog.index $storage_index_file > /dev/null +} + +--let $assert_text = Fetch should create binlogs with the rewrite file_size +--let $assert_file = $storage_index_file +--let $assert_count = 6 +--let $assert_select = ./$source_binlog_base_file_name. +--source include/assert_grep.inc + +--echo +--echo *** Test 3: The new events fetched should be searchable. + +--let $read_from_file = $MYSQL_TMP_DIR/rewrite_transition_search_result.json +--exec $BINSRV search_by_gtid_set $binsrv_config_file_path "$transition_workload_gtids" > $read_from_file +--source include/read_file_to_var.inc +--assert(`SELECT JSON_EXTRACT('$result', '$.status') = 'success'`) +--assert(`SELECT JSON_LENGTH(JSON_EXTRACT('$result', '$.result')) >= 1`) + +--echo +--echo *** Test 4: GTIDs fetched before rewrite mode was enabled remain searchable. +--exec $BINSRV search_by_gtid_set $binsrv_config_file_path "$create_table_gtid" > $read_from_file +--source include/read_file_to_var.inc +--assert(`SELECT JSON_EXTRACT('$result', '$.status') = 'success'`) +--assert(`SELECT JSON_LENGTH(JSON_EXTRACT('$result', '$.result')) = 1`) +--assert(`SELECT JSON_EXTRACT('$result', '$.result[0].name') = '$source_first_binlog'`) + +--remove_file $storage_index_file +--remove_file $read_from_file + +--echo +--echo *** Generating extra load before changing rewrite base name and file size. +--connection master +--disable_query_log +--let $transaction_idx = 0 +while ($transaction_idx < 18) +{ + eval INSERT INTO t1(val) VALUES(SHA2(CONCAT('rewrite-config-change-', $transaction_idx), 256)); + --inc $transaction_idx +} +--enable_query_log +--let $gtid_executed_after_config_change = `SELECT @@global.gtid_executed` +--let $config_change_workload_gtids = `SELECT GTID_SUBTRACT(@@global.gtid_executed, '$gtid_executed_after_transition_workload')` + +--echo +--echo *** Test 5: Changing rewrite base name and file size before fetch. +--let $changed_rewrite_base_file_name = changed_rewrite +eval SET @binsrv_config_json = JSON_SET(@binsrv_config_json, + '$.replication.rewrite.base_file_name', '$changed_rewrite_base_file_name', + '$.replication.rewrite.file_size', '2K' +); +--let $write_var = `SELECT @binsrv_config_json` +--source include/write_var_to_file.inc + +--exec $BINSRV fetch $binsrv_config_file_path > /dev/null + +if ($storage_backend == file) +{ + --copy_file $binsrv_storage_path/binlog.index $storage_index_file +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 cp s3://$aws_s3_bucket/$binsrv_storage_path/binlog.index $storage_index_file > /dev/null +} + +--let $assert_text = Fetch should keep using the existing binlog base name +--let $assert_file = $storage_index_file +--let $assert_count = 0 +--let $assert_select = ./$changed_rewrite_base_file_name. +--source include/assert_grep.inc + +--let $assert_text = Fetch should create binlogs with new rewrite file_size +--let $assert_file = $storage_index_file +--let $assert_count = 9 +--let $assert_select = ./$source_binlog_base_file_name. +--source include/assert_grep.inc + +--exec $BINSRV search_by_gtid_set $binsrv_config_file_path "$config_change_workload_gtids" > $read_from_file +--source include/read_file_to_var.inc +--assert(`SELECT JSON_EXTRACT('$result', '$.status') = 'success'`) +--assert(`SELECT JSON_LENGTH(JSON_EXTRACT('$result', '$.result')) >= 1`) + +--echo +--remove_file $storage_index_file +--remove_file $read_from_file + +--echo +--echo *** Generating additional workload for source to replica failover +--connection master +--disable_query_log +--let $transaction_idx = 0 +while ($transaction_idx < 10) +{ + eval INSERT INTO t1(val) VALUES(SHA2(CONCAT('source-after-failover-', $transaction_idx), 256)); + --inc $transaction_idx +} +--enable_query_log +--let $gtid_executed_after_failover_workload = `SELECT @@global.gtid_executed` +--let $failover_workload_gtids = `SELECT GTID_SUBTRACT(@@global.gtid_executed, '$gtid_executed_after_config_change')` + +--echo +--echo *** Rotating source logs differently from replica before failover fetch. +FLUSH BINARY LOGS; + +--save_master_pos +--connection slave +--sync_with_master +--echo +--echo *** Rotating replica logs after it receives the new GTIDs. +FLUSH BINARY LOGS; +--let $replica_post_workload_binlog = query_get_value($stmt_show_binary_log_status, File, 1) + +--connection master +--echo +--echo *** Test 6: Repointing existing storage config from source to replica. +--replace_result $replica_port REPLICA_PORT +eval SET @binsrv_config_json = JSON_SET(@binsrv_config_json, + '$.connection.port', $replica_port +); +--let $write_var = `SELECT @binsrv_config_json` +--source include/write_var_to_file.inc + +--exec $BINSRV fetch $binsrv_config_file_path > /dev/null + +if ($storage_backend == file) +{ + --copy_file $binsrv_storage_path/binlog.index $storage_index_file +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 cp s3://$aws_s3_bucket/$binsrv_storage_path/binlog.index $storage_index_file > /dev/null +} + +--let $assert_text = Rewrite mode should not store replica pre-existing binlog names +--let $assert_file = $storage_index_file +--let $assert_count = 0 +--let $assert_select = $replica_first_binlog +--source include/assert_grep.inc + +--let $assert_text = Rewrite mode should not store independently rotated replica binlog names +--let $assert_file = $storage_index_file +--let $assert_count = 0 +--let $assert_select = $replica_rotated_binlog +--source include/assert_grep.inc + +--let $assert_text = Rewrite mode should not store replica post-workload binlog names +--let $assert_file = $storage_index_file +--let $assert_count = 0 +--let $assert_select = $replica_post_workload_binlog +--source include/assert_grep.inc + +--let $assert_text = Rewrite mode should keep using source binlog base name after replica fetch +--let $assert_file = $storage_index_file +--let $assert_count = 1 +--let $assert_select = ./$source_binlog_base_file_name.000008 +--source include/assert_grep.inc + +--echo +--echo *** Test 7: New GTIDs fetched from replica should be searchable in rewritten files. +--let $read_from_file = $MYSQL_TMP_DIR/rewrite_replica_failover_search_result.json +--exec $BINSRV search_by_gtid_set $binsrv_config_file_path "$failover_workload_gtids" > $read_from_file +--source include/read_file_to_var.inc +--assert(`SELECT JSON_EXTRACT('$result', '$.status') = 'success'`) +--assert(`SELECT JSON_LENGTH(JSON_EXTRACT('$result', '$.result')) >= 1`) +--assert(`SELECT JSON_UNQUOTE(JSON_EXTRACT('$result', '$.result[0].name')) LIKE '$source_binlog_base_file_name.%'`) +--assert(`SELECT LOCATE('$source_binlog_base_file_name.', JSON_UNQUOTE(JSON_EXTRACT('$result', '$.result[0].uri'))) > 0`) + +--echo +--echo *** Test 8: Previously fetched GTIDs should remain searchable after failover. +--exec $BINSRV search_by_gtid_set $binsrv_config_file_path "$create_table_gtid" > $read_from_file +--source include/read_file_to_var.inc +--assert(`SELECT JSON_EXTRACT('$result', '$.status') = 'success'`) +--assert(`SELECT JSON_LENGTH(JSON_EXTRACT('$result', '$.result')) = 1`) +--assert(`SELECT JSON_EXTRACT('$result', '$.result[0].name') = '$source_binlog_base_file_name.000001'`) + +--echo +--remove_file $storage_index_file +--remove_file $read_from_file + +--connection master +--echo +--echo *** Dropping test table on source. +DROP TABLE t1; +--save_master_pos + +--connection slave +--sync_with_master + +--connection master +# cleaning up +--source ../include/tear_down_binsrv_environment.inc + +--replace_result $deinit_rpl_inc include/rpl/deinit.inc +--source $deinit_rpl_inc + + diff --git a/mtr/binlog_streaming/t/search_by_gtid_set_gtid.test b/mtr/binlog_streaming/t/search_by_gtid_set_gtid.test index 252c5fc..b2f54e2 100644 --- a/mtr/binlog_streaming/t/search_by_gtid_set_gtid.test +++ b/mtr/binlog_streaming/t/search_by_gtid_set_gtid.test @@ -163,6 +163,37 @@ INSERT INTO t1 VALUES(); --assert(`SELECT JSON_EXTRACT('$result', '$.status') = 'error'`) --assert(`SELECT JSON_EXTRACT('$result', '$.message') = 'The specified GTID set cannot be covered'`) +--echo +--echo *** 12. Executing the Binlog Server utility in the 'search_by_gtid_set' +--echo *** mode with GTIDs listed in reverse order +--let $reverse_order_gtids = $second_insert_gtid, $first_insert_gtid +--exec $BINSRV search_by_gtid_set $binsrv_config_file_path "$reverse_order_gtids" > $read_from_file +--source include/read_file_to_var.inc +--assert(`SELECT JSON_EXTRACT('$result', '$.status') = 'success'`) +--assert(`SELECT JSON_LENGTH(JSON_EXTRACT('$result', '$.result')) = 2`) +--assert(`SELECT JSON_EXTRACT('$result', '$.result[0].name') = '$first_binlog'`) +--assert(`SELECT JSON_EXTRACT('$result', '$.result[1].name') = '$second_binlog'`) + +--echo +--echo *** 13. Executing the Binlog Server utility in the 'search_by_gtid_set' +--echo *** mode with overlapping requested GTID sets +--let $overlapping_gtids = $first_insert_gtid, $gtid_executed_after_first_insert +--exec $BINSRV search_by_gtid_set $binsrv_config_file_path "$overlapping_gtids" > $read_from_file +--source include/read_file_to_var.inc +--assert(`SELECT JSON_EXTRACT('$result', '$.status') = 'success'`) +--assert(`SELECT JSON_LENGTH(JSON_EXTRACT('$result', '$.result')) = 1`) +--assert(`SELECT JSON_EXTRACT('$result', '$.result[0].name') = '$first_binlog'`) + +--echo +--echo *** 14. Executing the Binlog Server utility in the 'search_by_gtid_set' +--echo *** mode with the full stored GTID set +--exec $BINSRV search_by_gtid_set $binsrv_config_file_path "$gtid_executed_after_second_insert" > $read_from_file +--source include/read_file_to_var.inc +--assert(`SELECT JSON_EXTRACT('$result', '$.status') = 'success'`) +--assert(`SELECT JSON_LENGTH(JSON_EXTRACT('$result', '$.result')) = 2`) +--assert(`SELECT JSON_EXTRACT('$result', '$.result[0].name') = '$first_binlog'`) +--assert(`SELECT JSON_EXTRACT('$result', '$.result[1].name') = '$second_binlog'`) + --echo --echo *** Removing the search result file. --remove_file $read_from_file diff --git a/mtr/binlog_streaming/t/search_by_gtid_set_tagged_gtid-master.opt b/mtr/binlog_streaming/t/search_by_gtid_set_tagged_gtid-master.opt new file mode 100644 index 0000000..c7529f0 --- /dev/null +++ b/mtr/binlog_streaming/t/search_by_gtid_set_tagged_gtid-master.opt @@ -0,0 +1,2 @@ +--gtid-mode=on +--enforce-gtid-consistency diff --git a/mtr/binlog_streaming/t/search_by_gtid_set_tagged_gtid.test b/mtr/binlog_streaming/t/search_by_gtid_set_tagged_gtid.test new file mode 100644 index 0000000..04bb166 --- /dev/null +++ b/mtr/binlog_streaming/t/search_by_gtid_set_tagged_gtid.test @@ -0,0 +1,160 @@ +--source ../include/have_binsrv.inc + +--source ../include/v80_v84_compatibility_defines.inc +if ($lts_series != v84) +{ + --skip This test must be run on a MySQL Server 8.4 +} + +# in case of --repeat=N, we need to start from a fresh binary log to make +# this test deterministic +--echo *** Resetting replication at the very beginning of the test. +--disable_query_log +eval $stmt_reset_binary_logs_and_gtids; +--enable_query_log + +--echo +--echo *** Determining the first binary log name. +--let $first_binlog = query_get_value($stmt_show_binary_log_status, File, 1) + +# identifying backend storage type ('file' or 's3') +--source ../include/identify_storage_backend.inc + +--let $read_from_file = $MYSQL_TMP_DIR/search_tagged_gtid_result.json + +--let $gtid_executed_initial = `SELECT @@global.gtid_executed` + +--echo +--echo *** Creating a table and capturing an untagged GTID. +CREATE TABLE t1(id INT UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY(id)) ENGINE=InnoDB; +--let $gtid_executed_after_create_table = `SELECT @@global.gtid_executed` + +INSERT INTO t1 VALUES(); +--let $first_insert_gtid = `SELECT GTID_SUBTRACT(@@global.gtid_executed, '$gtid_executed_after_create_table')` + +--echo +--echo *** Creating tagged GTIDs with varied tag lengths in separate binlogs. + +--echo +--echo *** Writing a short-tagged GTID into the second binlog. +FLUSH BINARY LOGS; +--let $tag_short_binlog = query_get_value($stmt_show_binary_log_status, File, 1) +--let $tag_short_gtid = `SELECT CONCAT(@@server_uuid, ':a:900001')` +--disable_query_log +eval SET @@SESSION.GTID_NEXT = '$tag_short_gtid'; +--enable_query_log +START TRANSACTION; +INSERT INTO t1 VALUES(); +COMMIT; +--disable_query_log +SET @@SESSION.GTID_NEXT = 'AUTOMATIC'; +--enable_query_log + +--echo +--echo *** Writing a medium-tagged GTID range into the third binlog. +FLUSH BINARY LOGS; +--let $tag_medium_binlog = query_get_value($stmt_show_binary_log_status, File, 1) +--let $tag_medium_first_gtid = `SELECT CONCAT(@@server_uuid, ':tag_medium_12:900002')` +--let $tag_medium_second_gtid = `SELECT CONCAT(@@server_uuid, ':tag_medium_12:900003')` +--let $tag_medium_range = `SELECT CONCAT(@@server_uuid, ':tag_medium_12:900002-900003')` +--disable_query_log +eval SET @@SESSION.GTID_NEXT = '$tag_medium_first_gtid'; +--enable_query_log +START TRANSACTION; +INSERT INTO t1 VALUES(); +COMMIT; +--disable_query_log +eval SET @@SESSION.GTID_NEXT = '$tag_medium_second_gtid'; +--enable_query_log +START TRANSACTION; +INSERT INTO t1 VALUES(); +COMMIT; +--disable_query_log +SET @@SESSION.GTID_NEXT = 'AUTOMATIC'; +--enable_query_log + +--echo +--echo *** Writing a max-length-tagged GTID into the fourth binlog. +FLUSH BINARY LOGS; +--let $tag_max_binlog = query_get_value($stmt_show_binary_log_status, File, 1) +--let $tag_max_gtid = `SELECT CONCAT(@@server_uuid, ':t_abcdefghijklmnopqrstuvwxyz1234:900004')` +--disable_query_log +eval SET @@SESSION.GTID_NEXT = '$tag_max_gtid'; +--enable_query_log +START TRANSACTION; +INSERT INTO t1 VALUES(); +COMMIT; +--disable_query_log +SET @@SESSION.GTID_NEXT = 'AUTOMATIC'; +--enable_query_log + +--echo +--echo *** Setting up GTID-mode Binlog Server storage. +--let $binsrv_connect_timeout = 20 +--let $binsrv_read_timeout = 60 +--let $binsrv_idle_time = 10 +--let $binsrv_verify_checksum = TRUE +--let $binsrv_replication_mode = gtid +--let $binsrv_checkpoint_size = 1 +--source ../include/set_up_binsrv_environment.inc + +--exec $BINSRV fetch $binsrv_config_file_path > /dev/null + +--echo +--echo *** Test 1: Tagged GTID search should find a short tag in its binlog. +--exec $BINSRV search_by_gtid_set $binsrv_config_file_path "$tag_short_gtid" > $read_from_file +--source include/read_file_to_var.inc +--assert(`SELECT JSON_EXTRACT('$result', '$.status') = 'success'`) +--assert(`SELECT JSON_LENGTH(JSON_EXTRACT('$result', '$.result')) = 1`) +--assert(`SELECT JSON_EXTRACT('$result', '$.result[0].name') = '$tag_short_binlog'`) +--assert(`SELECT LOCATE('$tag_short_gtid', JSON_UNQUOTE(JSON_EXTRACT('$result', '$.result[0].added_gtids'))) > 0`) + +--echo +--echo *** Test 2: Tagged GTID range should be covered by a single binlog. +--exec $BINSRV search_by_gtid_set $binsrv_config_file_path "$tag_medium_range" > $read_from_file +--source include/read_file_to_var.inc +--assert(`SELECT JSON_EXTRACT('$result', '$.status') = 'success'`) +--assert(`SELECT JSON_LENGTH(JSON_EXTRACT('$result', '$.result')) = 1`) +--assert(`SELECT JSON_EXTRACT('$result', '$.result[0].name') = '$tag_medium_binlog'`) +--assert(`SELECT LOCATE('tag_medium_12', JSON_UNQUOTE(JSON_EXTRACT('$result', '$.result[0].added_gtids'))) > 0`) + +--echo +--echo *** Test 3: Mixed tagged GTIDs should be returned in binlog order. +--let $mixed_tagged_gtids = $tag_max_gtid, $tag_short_gtid, $tag_medium_range +--exec $BINSRV search_by_gtid_set $binsrv_config_file_path "$mixed_tagged_gtids" > $read_from_file +--source include/read_file_to_var.inc +--assert(`SELECT JSON_EXTRACT('$result', '$.status') = 'success'`) +--assert(`SELECT JSON_LENGTH(JSON_EXTRACT('$result', '$.result')) = 3`) +--assert(`SELECT JSON_EXTRACT('$result', '$.result[0].name') = '$tag_short_binlog'`) +--assert(`SELECT JSON_EXTRACT('$result', '$.result[1].name') = '$tag_medium_binlog'`) +--assert(`SELECT JSON_EXTRACT('$result', '$.result[2].name') = '$tag_max_binlog'`) + +--echo +--echo *** Test 4: Tagged and untagged GTIDs should be searchable together. +--let $mixed_tagged_and_untagged_gtids = $tag_short_gtid, $first_insert_gtid +--exec $BINSRV search_by_gtid_set $binsrv_config_file_path "$mixed_tagged_and_untagged_gtids" > $read_from_file +--source include/read_file_to_var.inc +--assert(`SELECT JSON_EXTRACT('$result', '$.status') = 'success'`) +--assert(`SELECT JSON_LENGTH(JSON_EXTRACT('$result', '$.result')) = 2`) +--assert(`SELECT JSON_EXTRACT('$result', '$.result[0].name') = '$first_binlog'`) +--assert(`SELECT JSON_EXTRACT('$result', '$.result[1].name') = '$tag_short_binlog'`) + +--echo +--echo *** Test 5: A missing tag should not be covered by matching gtid with other tag. +--let $missing_tag_gtid = `SELECT CONCAT(@@server_uuid, ':missing_tag:900001')` +--error 1 +--exec $BINSRV search_by_gtid_set $binsrv_config_file_path "$missing_tag_gtid" > $read_from_file +--source include/read_file_to_var.inc +--assert(`SELECT JSON_EXTRACT('$result', '$.status') = 'error'`) +--assert(`SELECT JSON_EXTRACT('$result', '$.message') = 'The specified GTID set cannot be covered'`) + +--echo +--echo *** Removing the search result file. +--remove_file $read_from_file + +--source ../include/tear_down_binsrv_environment.inc + +--echo +--echo *** Dropping the table. +DROP TABLE t1; + diff --git a/mtr/binlog_streaming/t/search_by_timestamp.test b/mtr/binlog_streaming/t/search_by_timestamp.test index 05cee27..1985474 100644 --- a/mtr/binlog_streaming/t/search_by_timestamp.test +++ b/mtr/binlog_streaming/t/search_by_timestamp.test @@ -119,6 +119,38 @@ INSERT INTO t1 VALUES(); --assert(`SELECT JSON_EXTRACT('$result', '$.result[0].name') = '$first_binlog'`) --assert(`SELECT JSON_EXTRACT('$result', '$.result[1].name') = '$second_binlog'`) +--echo +--echo *** Search response should include useful metadata fields. +--assert(`SELECT JSON_EXTRACT('$result', '$.result[0].size') > 4`) +--assert(`SELECT JSON_UNQUOTE(JSON_EXTRACT('$result', '$.result[0].uri')) != ''`) +--assert(`SELECT JSON_UNQUOTE(JSON_EXTRACT('$result', '$.result[0].min_timestamp')) != ''`) +--assert(`SELECT JSON_UNQUOTE(JSON_EXTRACT('$result', '$.result[0].max_timestamp')) != ''`) +--assert(`SELECT JSON_UNQUOTE(JSON_EXTRACT('$result', '$.result[0].min_timestamp')) <= JSON_UNQUOTE(JSON_EXTRACT('$result', '$.result[0].max_timestamp'))`) + +--echo +--echo *** Storage URI in the response should match the configured backend. +if ($storage_backend == file) +{ + --assert(`SELECT JSON_UNQUOTE(JSON_EXTRACT('$result', '$.result[0].uri')) LIKE 'file:%'`) +} +if ($storage_backend == s3) +{ + --assert(`SELECT JSON_UNQUOTE(JSON_EXTRACT('$result', '$.result[0].uri')) LIKE 's3:%' OR JSON_UNQUOTE(JSON_EXTRACT('$result', '$.result[0].uri')) LIKE 'http:%' OR JSON_UNQUOTE(JSON_EXTRACT('$result', '$.result[0].uri')) LIKE 'https:%'`) +} + +--echo +--echo *** GTID fields should only be present for GTID storage. +if ($binsrv_replication_mode == gtid) +{ + --assert(`SELECT JSON_CONTAINS_PATH('$result', 'one', '$.result[0].previous_gtids')`) + --assert(`SELECT JSON_CONTAINS_PATH('$result', 'one', '$.result[0].added_gtids')`) +} +if ($binsrv_replication_mode == position) +{ + --assert(`SELECT JSON_CONTAINS_PATH('$result', 'one', '$.result[0].previous_gtids') = 0`) + --assert(`SELECT JSON_CONTAINS_PATH('$result', 'one', '$.result[0].added_gtids') = 0`) +} + --echo --echo *** Removing the search result file. --remove_file $read_from_file diff --git a/mtr/binlog_streaming/t/ssl_negative.test b/mtr/binlog_streaming/t/ssl_negative.test new file mode 100644 index 0000000..f0ce7fb --- /dev/null +++ b/mtr/binlog_streaming/t/ssl_negative.test @@ -0,0 +1,138 @@ +--source ../include/have_binsrv.inc + +--let $localhost_ip = 127.0.0.1 + +--let $user_name = test_ssl_user +--let $user_password = test_password + +--echo +--echo *** Testing SSL/TLS negative scenarios + +--echo +--echo *** Creating test user without SSL requirement +eval CREATE USER IF NOT EXISTS '$user_name'@'$localhost_ip' IDENTIFIED BY '$user_password'; +eval GRANT REPLICATION SLAVE ON *.* TO '$user_name'@'$localhost_ip'; + +# identifying backend storage type ('file' or 's3') +--source ../include/identify_storage_backend.inc + +#--let $skip_binsrv_log_path_cleanup = 1 + +--echo +--echo *** Test 1: SSL mode verify_ca without CA certificate (should fail) +--let $binsrv_connection_host = $localhost_ip +--let $binsrv_connection_user = $user_name +--let $binsrv_connection_password = $user_password +--let $binsrv_connect_timeout = 5 +--let $binsrv_read_timeout = 60 +--let $binsrv_idle_time = 10 +--let $binsrv_verify_checksum = TRUE +--let $binsrv_ssl_mode = verify_ca +--let $binsrv_ssl_ca = +--let $binsrv_replication_mode = position +--source ../include/set_up_binsrv_environment.inc + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 + +--let $assert_text = verify_ca without CA certificate should fail while establishing MySQL connection +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = cannot establish MySQL connection: SSL connection error: CA certificate is required if ssl-mode is VERIFY_CA or VERIFY_IDENTITY +--source include/assert_grep.inc +--source ../include/tear_down_binsrv_environment.inc + +--echo +--echo *** Test 2: SSL mode verify_identity without CA certificate (should fail) +--let $binsrv_ssl_mode = verify_identity +--let $binsrv_ssl_ca = +--source ../include/set_up_binsrv_environment.inc + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 + +--let $assert_text = verify_identity without CA certificate should fail while establishing MySQL connection +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = cannot establish MySQL connection: SSL connection error: CA certificate is required if ssl-mode is VERIFY_CA or VERIFY_IDENTITY +--source include/assert_grep.inc +--source ../include/tear_down_binsrv_environment.inc + +--echo +--echo *** Test 3: SSL mode verify_ca with non-existent CA file (should fail) +--let $binsrv_ssl_mode = verify_ca +--let $binsrv_ssl_ca = /nonexistent/ca.pem +--source ../include/set_up_binsrv_environment.inc + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 + +--let $assert_text = verify_ca with non-existent CA file should fail while establishing MySQL connection +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = cannot establish MySQL connection: SSL connection error: SSL_CTX_set_default_verify_paths +--source include/assert_grep.inc +--source ../include/tear_down_binsrv_environment.inc + +--echo +--echo *** Test 4: SSL mode verify_identity with invalid certificate (should fail) +--let $binsrv_ssl_mode = verify_identity +--let $binsrv_ssl_ca = $MYSQL_TEST_DIR/std_data/ca-cert-verify-san.pem +--let $binsrv_ssl_cert = /nonexistent/cert.pem +--let $binsrv_ssl_key = /nonexistent/key.pem +--source ../include/set_up_binsrv_environment.inc + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 + +--let $assert_text = verify_identity with invalid certificate should fail while establishing MySQL connection +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = cannot establish MySQL connection: SSL connection error: Unable to get certificate +--source include/assert_grep.inc +--source ../include/tear_down_binsrv_environment.inc + +--echo +--echo *** Test 5: TLS version mismatch (if server doesn't support requested version) +--let $binsrv_ssl_mode = required +--let $binsrv_ssl_ca = +--let $binsrv_ssl_cert = +--let $binsrv_ssl_key = +--let $binsrv_tls_version = TLSv1.4 +--source ../include/set_up_binsrv_environment.inc + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 + +--let $assert_text = Invalid TLS version should fail while setting MySQL TLS version +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = cannot set MySQL TLS VERSION +--source include/assert_grep.inc +--source ../include/tear_down_binsrv_environment.inc + +--echo +--echo *** Test 6: SSL cert without key (should fail) +--let $binsrv_ssl_mode = verify_identity +--let $binsrv_ssl_ca = $MYSQL_TEST_DIR/std_data/ca-cert-verify-san.pem +--let $binsrv_ssl_cert = $MYSQL_TEST_DIR/std_data/client-cert-verify-san.pem +--let $binsrv_ssl_key = +--let $binsrv_tls_version = +--source ../include/set_up_binsrv_environment.inc + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 + +--let $assert_text = SSL certificate without key should fail while establishing MySQL connection +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = cannot establish MySQL connection: SSL connection error: Unable to get private key +--source include/assert_grep.inc +--source ../include/tear_down_binsrv_environment.inc +--echo +--echo *** Dropping test user +eval DROP USER IF EXISTS '$user_name'@'$localhost_ip'; + + + + diff --git a/mtr/binlog_streaming/t/storage_corruption.test b/mtr/binlog_streaming/t/storage_corruption.test new file mode 100644 index 0000000..b2d54d9 --- /dev/null +++ b/mtr/binlog_streaming/t/storage_corruption.test @@ -0,0 +1,365 @@ +--source ../include/have_binsrv.inc + +--source ../include/v80_v84_compatibility_defines.inc + +# identifying backend storage type ('file' or 's3') +--source ../include/identify_storage_backend.inc + +--echo +--echo *** Testing storage corruption scenarios + +--echo +--echo *** Resetting replication at the very beginning of the test. +--disable_query_log +eval $stmt_reset_binary_logs_and_gtids; +--enable_query_log + +--echo +--echo *** Creating a simple table and filling it with some data. +CREATE TABLE t1(id INT UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY(id)) ENGINE=InnoDB; +INSERT INTO t1 VALUES(DEFAULT); +FLUSH BINARY LOGS; + +--let $first_binlog = query_get_value($stmt_show_binary_log_status, File, 1) + +--echo +--echo *** Test 1: Storage with missing binlog.index but containing binlog files +--let $binsrv_connect_timeout = 20 +--let $binsrv_read_timeout = 60 +--let $binsrv_idle_time = 10 +--let $binsrv_verify_checksum = TRUE +--let $binsrv_replication_mode = position +--source ../include/set_up_binsrv_environment.inc + +--exec $BINSRV fetch $binsrv_config_file_path > /dev/null + +--echo +--echo *** Corrupting storage: removing binlog.index but keeping binlog files +if ($storage_backend == file) +{ + --remove_file $binsrv_storage_path/binlog.index + --write_file $binsrv_storage_path/dummy.binlog + dummy content + EOF +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 rm s3://$aws_s3_bucket/$binsrv_storage_path/binlog.index > /dev/null + --exec echo "dummy content" | $aws_cli s3 cp - s3://$aws_s3_bucket/$binsrv_storage_path/dummy.binlog > /dev/null +} + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 + +--let $assert_text = Storage without binlog.index should be rejected +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = storage is not empty but does not contain binlog index +--source include/assert_grep.inc + +--source ../include/tear_down_binsrv_environment.inc + +--echo +--echo *** Test 2: Storage with binlog.index containing duplicate entries +--source ../include/set_up_binsrv_environment.inc + +--exec $BINSRV fetch $binsrv_config_file_path > /dev/null + +--echo +--echo *** Corrupting storage: adding duplicate entry to binlog.index +if ($storage_backend == file) +{ + --exec cat $binsrv_storage_path/binlog.index > $MYSQL_TMP_DIR/temp_index + --exec echo "./$first_binlog" >> $MYSQL_TMP_DIR/temp_index + --exec cp $MYSQL_TMP_DIR/temp_index $binsrv_storage_path/binlog.index + --remove_file $MYSQL_TMP_DIR/temp_index +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 cp s3://$aws_s3_bucket/$binsrv_storage_path/binlog.index $MYSQL_TMP_DIR/temp_index > /dev/null + --exec echo "./$first_binlog" >> $MYSQL_TMP_DIR/temp_index + --exec $aws_cli s3 cp $MYSQL_TMP_DIR/temp_index s3://$aws_s3_bucket/$binsrv_storage_path/binlog.index > /dev/null + --remove_file $MYSQL_TMP_DIR/temp_index +} + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 + +--let $assert_text = Duplicate binlog.index entries should be rejected +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = binlog index contains a duplicate entry +--source include/assert_grep.inc + +--source ../include/tear_down_binsrv_environment.inc + +--echo +--echo *** Test 3: Storage with binlog.index referencing non-existent binlog file +--source ../include/set_up_binsrv_environment.inc + +--exec $BINSRV fetch $binsrv_config_file_path > /dev/null + +--echo +--echo *** Corrupting storage: adding reference to non-existent binlog +if ($storage_backend == file) +{ + --exec cat $binsrv_storage_path/binlog.index > $MYSQL_TMP_DIR/temp_index + --exec echo "./nonexistent.999999" >> $MYSQL_TMP_DIR/temp_index + --exec cp $MYSQL_TMP_DIR/temp_index $binsrv_storage_path/binlog.index + --remove_file $MYSQL_TMP_DIR/temp_index +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 cp s3://$aws_s3_bucket/$binsrv_storage_path/binlog.index $MYSQL_TMP_DIR/temp_index > /dev/null + --exec echo "./nonexistent.999999" >> $MYSQL_TMP_DIR/temp_index + --exec $aws_cli s3 cp $MYSQL_TMP_DIR/temp_index s3://$aws_s3_bucket/$binsrv_storage_path/binlog.index > /dev/null + --remove_file $MYSQL_TMP_DIR/temp_index +} + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 + +--let $assert_text = binlog.index references to non-existing objects should be rejected +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = binlog index contains a reference to a non-existing object +--source include/assert_grep.inc + +--source ../include/tear_down_binsrv_environment.inc + +--echo +--echo *** Test 4: Storage with binlog file not referenced in binlog.index +--source ../include/set_up_binsrv_environment.inc + +--exec $BINSRV fetch $binsrv_config_file_path > /dev/null + +--echo +--echo *** Corrupting storage: adding orphaned binlog file +if ($storage_backend == file) +{ + --write_file $binsrv_storage_path/orphaned.binlog + dummy binlog content + EOF +} +if ($storage_backend == s3) +{ + --exec echo "dummy binlog content" | $aws_cli s3 cp - s3://$aws_s3_bucket/$binsrv_storage_path/orphaned.binlog > /dev/null +} + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 + +--let $assert_text = Binlog objects not referenced in binlog.index should be rejected +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = storage contains an object that is not referenced in the binlog index +--source include/assert_grep.inc + +--source ../include/tear_down_binsrv_environment.inc + +--echo +--echo *** Test 5: Storage with binlog.index containing invalid path +--source ../include/set_up_binsrv_environment.inc + +--exec $BINSRV fetch $binsrv_config_file_path > /dev/null + +--echo +--echo *** Corrupting storage: adding invalid path to binlog.index +if ($storage_backend == file) +{ + --exec cat $binsrv_storage_path/binlog.index > $MYSQL_TMP_DIR/temp_index + --exec echo "../invalid/path.binlog" >> $MYSQL_TMP_DIR/temp_index + --exec cp $MYSQL_TMP_DIR/temp_index $binsrv_storage_path/binlog.index + --remove_file $MYSQL_TMP_DIR/temp_index +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 cp s3://$aws_s3_bucket/$binsrv_storage_path/binlog.index $MYSQL_TMP_DIR/temp_index > /dev/null + --exec echo "../invalid/path.binlog" >> $MYSQL_TMP_DIR/temp_index + --exec $aws_cli s3 cp $MYSQL_TMP_DIR/temp_index s3://$aws_s3_bucket/$binsrv_storage_path/binlog.index > /dev/null + --remove_file $MYSQL_TMP_DIR/temp_index +} + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 + +--let $assert_text = binlog.index entries with invalid paths should be rejected +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = binlog index contains an entry that has an invalid path +--source include/assert_grep.inc + +--source ../include/tear_down_binsrv_environment.inc + +--echo +--echo *** Test 6: Unsupported top-level storage metadata version +--source ../include/set_up_binsrv_environment.inc + +--exec $BINSRV fetch $binsrv_config_file_path > /dev/null + +if ($storage_backend == file) +{ + --write_file $binsrv_storage_path/metadata.json +{"version":0,"mode":"position"} +EOF +} +if ($storage_backend == s3) +{ + --let $corrupt_metadata_file = $MYSQL_TMP_DIR/corrupt_metadata.json + --write_file $corrupt_metadata_file +{"version":0,"mode":"position"} +EOF + --exec $aws_cli s3 cp $corrupt_metadata_file s3://$aws_s3_bucket/$binsrv_storage_path/metadata.json > /dev/null + --remove_file $corrupt_metadata_file +} + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 +--let $assert_text = Unsupported storage metadata version should be rejected +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = unsupported storage metadata version +--source include/assert_grep.inc + +--source ../include/tear_down_binsrv_environment.inc + +--echo +--echo *** Test 7: Replication mode mismatch with initialized storage +--source ../include/set_up_binsrv_environment.inc + +--exec $BINSRV fetch $binsrv_config_file_path > /dev/null + +--let EXPORTED_BINSRV_CONFIG_FILE_PATH = $binsrv_config_file_path +--perl + use strict; + use warnings; + my $config_file = $ENV{'EXPORTED_BINSRV_CONFIG_FILE_PATH'}; + open(my $in, '<', $config_file) or die "Failed to open config file: $!"; + local $/; + my $config = <$in>; + close($in); + $config =~ s/"mode"\s*:\s*"position"/"mode":"gtid"/ + or die "Failed to replace replication mode"; + open(my $out, '>', $config_file) or die "Failed to write config file: $!"; + print {$out} $config; + close($out) or die "Failed to close config file: $!"; +EOF + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 +--let $assert_text = Replication mode mismatch should be rejected +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = replication mode provided to initialize storage differs from the one stored in metadata +--source include/assert_grep.inc + +--source ../include/tear_down_binsrv_environment.inc + +--echo +--echo *** Test 8: Missing per-binlog metadata file +--source ../include/set_up_binsrv_environment.inc + +--exec $BINSRV fetch $binsrv_config_file_path > /dev/null + +if ($storage_backend == file) +{ + --remove_file $binsrv_storage_path/$first_binlog.json +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 rm s3://$aws_s3_bucket/$binsrv_storage_path/$first_binlog.json > /dev/null +} + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 +--let $assert_text = Missing binlog metadata should be rejected +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = missing metadata for a binlog listed in the binlog index +--source include/assert_grep.inc + +--source ../include/tear_down_binsrv_environment.inc + +--echo +--echo *** Test 9: Per-binlog metadata size mismatch +--source ../include/set_up_binsrv_environment.inc + +--exec $BINSRV fetch $binsrv_config_file_path > /dev/null + +--let $binlog_metadata_file = $MYSQL_TMP_DIR/binlog_metadata_corrupt.json +if ($storage_backend == file) +{ + --copy_file $binsrv_storage_path/$first_binlog.json $binlog_metadata_file +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 cp s3://$aws_s3_bucket/$binsrv_storage_path/$first_binlog.json $binlog_metadata_file > /dev/null +} + +--let EXPORTED_BINLOG_METADATA_FILE = $binlog_metadata_file +--perl + use strict; + use warnings; + my $metadata_file = $ENV{'EXPORTED_BINLOG_METADATA_FILE'}; + open(my $in, '<', $metadata_file) or die "Failed to open metadata file: $!"; + local $/; + my $metadata = <$in>; + close($in); + $metadata =~ s/"size"\s*:\s*\d+/"size":1/ + or die "Failed to replace metadata size"; + open(my $out, '>', $metadata_file) or die "Failed to write metadata file: $!"; + print {$out} $metadata; + close($out) or die "Failed to close metadata file: $!"; +EOF + +if ($storage_backend == file) +{ + --copy_file $binlog_metadata_file $binsrv_storage_path/$first_binlog.json +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 cp $binlog_metadata_file s3://$aws_s3_bucket/$binsrv_storage_path/$first_binlog.json > /dev/null +} +--remove_file $binlog_metadata_file + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 +--let $assert_text = Metadata size mismatch should be rejected +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = size from the binlog metadata does not match the actual binlog size +--source include/assert_grep.inc + +--source ../include/tear_down_binsrv_environment.inc + +--echo +--echo *** Test 10: Metadata file for a non-existing binlog +--source ../include/set_up_binsrv_environment.inc + +--exec $BINSRV fetch $binsrv_config_file_path > /dev/null + +if ($storage_backend == file) +{ + --copy_file $binsrv_storage_path/$first_binlog.json $binsrv_storage_path/orphaned.000001.json +} +if ($storage_backend == s3) +{ + --exec $aws_cli s3 cp s3://$aws_s3_bucket/$binsrv_storage_path/$first_binlog.json s3://$aws_s3_bucket/$binsrv_storage_path/orphaned.000001.json > /dev/null +} + +--error 1 +--exec $BINSRV fetch $binsrv_config_file_path > $binsrv_log_path 2>&1 +--let $assert_text = Orphaned binlog metadata should be rejected +--let $assert_file = $binsrv_log_path +--let $assert_count = 1 +--let $assert_select = found metadata for a non-existing binlog +--source include/assert_grep.inc + +--source ../include/tear_down_binsrv_environment.inc + +DROP TABLE t1; + + + + +