# ==== Requirements ==== # # The following situations should result in errors: # # E1. A JSON path exists on the master but not on slave, and is # updated/deleted on teh master. # # E2. A JSON path exists on the slave but not on the master, and is # inserted on the master. # # E3. The slave has an extra column, which is generated and has a uniqueness # constaint that gets violated by the update. # # E4. Both master and slave have a generated column, and the slave has an # extra uniquness constraint that gets violated by the update. # # E5. Out of memory happens while applying a JSON diff. # # E6. The row event containing partial JSON is corrupted. # - This includes a number of different ways to corrupt the event. # See implementation of E6 below. # # The consequence of the error depends on the mode; there are three modes: # # M1. Normal mode, error causing slave to stop. # # M2. slave_skip_errors includes the error. # # M3. slave_exec_mode=IDEMPOTENT # # In addition, the consequence of corrupted events should be that # mysqlbinlog -v prints an appropriate message when failing to decode # the row event. # # The code paths to handle idempotent errors are different for each # slave_rows_search_algorithms, hence we must run the test once for each # algorithm. # # ==== Implementation ==== # # Setup each scenario using # extra/rpl_tests/rpl_row_jsondiff_error_scenario.inc # # Setup scenarios in a loop with three iterations, each iteration testing # one of M1-M3. # # Use include/begin_replace_combination.inc to run with all different # slave_rows_search_algorithms. # # ==== Related Worklog ==== # # WL#2955 RBR replication of partial JSON updates # --source include/have_debug.inc --source include/have_grep.inc --source include/master-slave.inc --echo ######## Configure ######## --let $dollar_func= CHAR(36) --let $dollar= `SELECT $dollar_func` --echo # Configure slave_rows_search_algorithms on master --let $replace_combination_from= BOTH.BINLOG_FORMAT=ROW,MIXED,STATEMENT --let $replace_combination_to= GLOBAL.SLAVE_ROWS_SEARCH_ALGORITHMS=INDEX_SCAN,HASH_SCAN,TABLE_SCAN --source include/rpl_connection_master.inc --source include/begin_replace_combination.inc --echo # Configure partial JSON on master --source include/rpl_connection_master.inc SET @old_binlog_row_image= @@SESSION.BINLOG_ROW_IMAGE; SET @old_binlog_row_value_options= @@SESSION.BINLOG_ROW_VALUE_OPTIONS; SET @@SESSION.BINLOG_ROW_IMAGE = 'MINIMAL'; SET @@SESSION.BINLOG_ROW_VALUE_OPTIONS = 'PARTIAL_JSON'; --echo # Add error suppressions on slave --source include/rpl_connection_slave.inc call mtr.add_suppression("Could not execute Update_rows_partial event on table test.t"); call mtr.add_suppression("Can't find record in 't'"); # When mts is enabled call mtr.add_suppression("The slave coordinator and worker threads are stopped, possibly leaving data in inconsistent state"); call mtr.add_suppression("Slave: Could not apply JSON diff"); call mtr.add_suppression("Slave: Corrupted JSON diff"); call mtr.add_suppression("Slave: Corrupted replication event was detected"); call mtr.add_suppression("Slave: Got error 1610 - "); call mtr.add_suppression("Slave: Got error 3648 - "); call mtr.add_suppression("Slave: Got error 3649 - "); call mtr.add_suppression("Slave: Got error 5 - "); --echo ######## Test ######## --let $clean_error= 1 --let $mode= 0 while ($mode < 3) { --source include/rpl_connection_slave.inc if ($mode == 0) { --let $error_mode= FAIL --source include/stop_slave.inc } if ($mode == 1) { --let $error_mode= SKIP --let $rpl_server_number= 2 # Error ER_KEY_NOT_FOUND --let $er_dup_entry= convert_error(ER_DUP_ENTRY) --let $er_key_not_found= convert_error(ER_KEY_NOT_FOUND) --let $er_could_not_apply_json_diff= convert_error(ER_COULD_NOT_APPLY_JSON_DIFF) --let $er_corrupted_json_diff= convert_error(ER_CORRUPTED_JSON_DIFF) --let $er_slave_corrupt_event= convert_error(ER_SLAVE_CORRUPT_EVENT) --let $rpl_server_parameters= --slave_skip_errors=$er_dup_entry,$er_key_not_found,$er_could_not_apply_json_diff,$er_corrupted_json_diff,$er_slave_corrupt_event,5 --source include/rpl_restart_server.inc --source include/rpl_connection_slave.inc } if ($mode == 2) { --let $error_mode= IDEMPOTENT --let $rpl_server_number= 2 --let $rpl_server_parameters= --skip-slave-start --source include/rpl_restart_server.inc --source include/rpl_connection_slave.inc SET GLOBAL SLAVE_EXEC_MODE = IDEMPOTENT; } --echo ******** ERROR MODE: $error_mode ******** --echo ---- Configure slave ---- # Configure slave options here and not before the loop, since we # restart the server above. --echo # Configure slave_rows_search_algorithms and binlog_format --source include/rpl_connection_slave.inc --source include/begin_replace_combination.inc --echo # Configure partial JSON SET @old_binlog_row_image= @@GLOBAL.BINLOG_ROW_IMAGE; SET @old_binlog_row_value_options= @@GLOBAL.BINLOG_ROW_VALUE_OPTIONS; SET @@GLOBAL.BINLOG_ROW_IMAGE = 'MINIMAL'; SET @@GLOBAL.BINLOG_ROW_VALUE_OPTIONS = 'PARTIAL_JSON'; --source include/start_slave.inc # For show_rpl_debug_info --let $extra_debug_table= test.t --let $table= test.t --let $column_def= i INT PRIMARY KEY, j JSON --let $stmt_pre= UPDATE t SET j = --let $stmt_post= WHERE i = 1 --let $insert_columns= (i, j) --let $desc= E1. Path exists on master but not on slave, JSON_REMOVE --let $rows= (1, '[1, {"m" : 1}]') --let $rows_slave= (1, '[1, {"s" : 1}]') --let $stmt= JSON_REMOVE(j, '$dollar[1].m') --let $error= convert_error(ER_COULD_NOT_APPLY_JSON_DIFF) --source extra/rpl_tests/rpl_row_jsondiff_error_scenario.inc --let $desc= E1. Path exists on master but not on slave, JSON_SET --let $rows= (1, '[1, {"m" : 1}]') --let $rows_slave= (1, '[1, {"s" : 1}]') --let $stmt= JSON_SET(j, '$dollar[1].m', 2) --let $error= convert_error(ER_COULD_NOT_APPLY_JSON_DIFF) --source extra/rpl_tests/rpl_row_jsondiff_error_scenario.inc --let $desc= E2. Path exists on slave but not on master --let $rows= (1, '[1, {"m" : 1}]') --let $rows_slave= (1, '[1, {"s" : 1}]') --let $stmt= JSON_SET(j, '$dollar[1].s', 2) --let $error= convert_error(ER_COULD_NOT_APPLY_JSON_DIFF) --source extra/rpl_tests/rpl_row_jsondiff_error_scenario.inc # Disabled due to BUG#26630497 if (0) { --let $desc= E3. Violation of uniqueness constraint in slave-only generated column --let $column_def= i INT PRIMARY KEY, j JSON --let $column_def_slave= $column_def, g INT GENERATED ALWAYS AS (j->"$dollar.id") VIRTUAL UNIQUE KEY --let $rows_slave= --let $rows= (1, '{"id": 1, "value": "x"}'), (2, '{"id": 2, "value": "y"}') --let $stmt= JSON_SET(j, '$dollar.id', 2) --let $error= convert_error(ER_DUP_ENTRY) --source extra/rpl_tests/rpl_row_jsondiff_error_scenario.inc } --let $desc= E4. Violation of slave-only uniqueness constraint in generated column --let $column_def= i INT PRIMARY KEY, j JSON, g INT GENERATED ALWAYS AS (j->"$dollar.id") VIRTUAL --let $column_def_slave= $column_def UNIQUE KEY --let $rows_slave= --let $rows= (1, '{"id": 1, "value": "x"}'), (2, '{"id": 2, "value": "y"}') --let $stmt= JSON_SET(j, '$dollar.id', 2) --let $error= convert_error(ER_DUP_ENTRY) --source extra/rpl_tests/rpl_row_jsondiff_error_scenario.inc --let $desc= E5. Out of memory applying diff --let $column_def= i INT PRIMARY KEY, j JSON --let $column_def_slave= --let $rows= (1, '{"id": 1, "value": "x"}') --let $stmt= JSON_SET(j, '$dollar.id', REPEAT('a', 1024)) --let $slave_debug_symbol= simulate_oom_in_apply_json_diffs --let $error= 5 --source include/rpl_connection_slave.inc SET GLOBAL BINLOG_ROW_VALUE_OPTIONS = ''; --source include/stop_slave_sql.inc --source include/start_slave_sql.inc --source extra/rpl_tests/rpl_row_jsondiff_error_scenario.inc --source include/rpl_connection_slave.inc SET GLOBAL BINLOG_ROW_VALUE_OPTIONS = 'PARTIAL_JSON'; --source include/stop_slave_sql.inc --source include/start_slave_sql.inc --let $slave_debug_symbol= --let $desc= E6. Corruption in event --let $stmt= JSON_SET(j, '$dollar.id', '[1]') # Use mysqlbinlog on corrupted events, in order to get coverage for # error cases in mysqlbinlog --let $use_mysqlbinlog= 1 # Make remove_debug_point not complain if the symbol was already removed. --let $debug_if_exists= 1 # 1. Error cases where the format of a json diff is invalid in some # way. The cases only differ on the suffix of the debug symbol, # so we iterate over them in a loop to reduce copy-paste. --let $error= convert_error(ER_CORRUPTED_JSON_DIFF) --let $debug_symbol_list= bad_op,truncate_before_path_length,bad_path_length,truncate_before_path,bad_path_char,truncate_before_doc_length,bad_doc_length,truncate_before_doc,bad_doc_char --let $debug_symbol_number= 1 while ($debug_symbol_number <= 9) { # Extract the Nth symbol from comma-separated list --let $master_debug_symbol= `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX('$debug_symbol_list', ',', $debug_symbol_number), ',', -1)` --let $master_debug_symbol= binlog_corrupt_json_diff_$master_debug_symbol --source extra/rpl_tests/rpl_row_jsondiff_error_scenario.inc --inc $debug_symbol_number } # 2. Other simulated error cases error. # This uses a different error and a different debug symbol prefix, # so execute outside the loop. --let $error= convert_error(ER_SLAVE_CORRUPT_EVENT) --let $master_debug_symbol= binlog_omit_last_column_from_table_map_event --source extra/rpl_tests/rpl_row_jsondiff_error_scenario.inc --let $debug_if_exists= 0 --let $use_mysqlbinlog= 0 --let $master_debug_symbol= --echo ---- Clean up partial JSON on slave ---- --source include/rpl_connection_slave.inc SET @@GLOBAL.BINLOG_ROW_VALUE_OPTIONS= @old_binlog_row_value_options; SET @@GLOBAL.BINLOG_ROW_IMAGE= @old_binlog_row_image; --inc $mode } --source include/rpl_connection_master.inc SET @@SESSION.BINLOG_ROW_VALUE_OPTIONS= @old_binlog_row_value_options; SET @@SESSION.BINLOG_ROW_IMAGE= @old_binlog_row_image; --source include/end_replace_combination.inc --source include/rpl_connection_slave.inc # To avoid an error 'ER_RUNNING_APPLIER_PREVENTS_SWITCH_GLOBAL_BINLOG_FORMAT', # which will be caused by the following include/end_replace_combination.inc --source include/stop_slave_sql.inc --source include/end_replace_combination.inc --let $rpl_only_running_threads= 1 --source include/rpl_end.inc