1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
|
# ==== 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. replica_skip_errors includes the error.
#
# M3. replica_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 replica coordinator and worker threads are stopped, possibly leaving data in inconsistent state");
call mtr.add_suppression("Replica: Could not apply JSON diff");
call mtr.add_suppression("Replica: Corrupted JSON diff");
call mtr.add_suppression("Replica: Corrupted replication event was detected");
call mtr.add_suppression("Replica: Got error 1610 - ");
call mtr.add_suppression("Replica: Got error 3648 - ");
call mtr.add_suppression("Replica: Got error 3649 - ");
call mtr.add_suppression("Replica: 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_REPLICA_CORRUPT_EVENT)
--let $rpl_server_parameters= --replica_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-replica-start
--source include/rpl_restart_server.inc
--source include/rpl_connection_slave.inc
SET GLOBAL REPLICA_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_REPLICA_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
--connection slave
SET GLOBAL REPLICA_EXEC_MODE = STRICT;
--let $rpl_only_running_threads= 1
--source include/rpl_end.inc
|