diff --git a/mysql-test/suite/rpl/r/rpl_gtid_crash.result b/mysql-test/suite/rpl/r/rpl_gtid_crash.result index 7257847e2c0..c52a94db47b 100644 --- a/mysql-test/suite/rpl/r/rpl_gtid_crash.result +++ b/mysql-test/suite/rpl/r/rpl_gtid_crash.result @@ -56,5 +56,36 @@ a 1 2 3 +*** Test crashing slave at various points and check that it recovers crash-safe. *** +include/stop_slave.inc +SET GLOBAL debug_dbug="+d,inject_crash_before_write_rpl_slave_state"; +START SLAVE; +INSERT INTO t1 VALUES (4); +include/stop_slave.inc +SET GLOBAL debug_dbug="+d,crash_commit_before"; +START SLAVE; +INSERT INTO t1 VALUES (5); +include/stop_slave.inc +SET GLOBAL debug_dbug="+d,crash_commit_after"; +START SLAVE; +INSERT INTO t1 VALUES (6); +include/stop_slave.inc +SET GLOBAL debug_dbug="+d,inject_crash_before_flush_rli"; +START SLAVE; +INSERT INTO t1 VALUES (7); +include/stop_slave.inc +SET GLOBAL debug_dbug="+d,inject_crash_after_flush_rli"; +START SLAVE; +INSERT INTO t1 VALUES (8); +SELECT * FROM t1 ORDER BY a; +a +1 +2 +3 +4 +5 +6 +7 +8 DROP TABLE t1; include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_gtid_crash.test b/mysql-test/suite/rpl/t/rpl_gtid_crash.test index ae55e0f0e6b..977fadae9e7 100644 --- a/mysql-test/suite/rpl/t/rpl_gtid_crash.test +++ b/mysql-test/suite/rpl/t/rpl_gtid_crash.test @@ -124,6 +124,140 @@ SHOW BINLOG EVENTS IN 'master-bin.000004' LIMIT 1,1; --sync_with_master SELECT * FROM t1 ORDER BY a; + +--echo *** Test crashing slave at various points and check that it recovers crash-safe. *** + +# Crash the slave just before updating mysql.rpl_slave_state table. +--source include/stop_slave.inc +--write_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect +wait +EOF +# We do not have to save @@GLOBAL.debug_dbug, it is reset when slave crashes. +SET GLOBAL debug_dbug="+d,inject_crash_before_write_rpl_slave_state"; +START SLAVE; + +--connection server_1 +INSERT INTO t1 VALUES (4); + +--connection server_2 +--source include/wait_until_disconnected.inc + +--remove_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect +--write_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect +restart: --skip-slave-start=0 +EOF + +--enable_reconnect +--source include/wait_until_connected_again.inc + +--let $wait_condition= SELECT COUNT(*) = 4 FROM t1 +--source include/wait_condition.inc + +# Crash the slave just before committing. +--source include/stop_slave.inc +--write_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect +wait +EOF +SET GLOBAL debug_dbug="+d,crash_commit_before"; +START SLAVE; + +--connection server_1 +INSERT INTO t1 VALUES (5); + +--connection server_2 +--source include/wait_until_disconnected.inc + +--remove_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect +--write_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect +restart: --skip-slave-start=0 +EOF + +--enable_reconnect +--source include/wait_until_connected_again.inc + +--let $wait_condition= SELECT COUNT(*) = 5 FROM t1 +--source include/wait_condition.inc + +# Crash the slave just after committing. +--source include/stop_slave.inc +--write_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect +wait +EOF +SET GLOBAL debug_dbug="+d,crash_commit_after"; +START SLAVE; + +--connection server_1 +INSERT INTO t1 VALUES (6); + +--connection server_2 +--source include/wait_until_disconnected.inc + +--remove_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect +--write_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect +restart: --skip-slave-start=0 +EOF + +--enable_reconnect +--source include/wait_until_connected_again.inc + +--let $wait_condition= SELECT COUNT(*) = 6 FROM t1 +--source include/wait_condition.inc + +# Crash the slave just before updating relay-log.info +--source include/stop_slave.inc +--write_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect +wait +EOF +SET GLOBAL debug_dbug="+d,inject_crash_before_flush_rli"; +START SLAVE; + +--connection server_1 +INSERT INTO t1 VALUES (7); + +--connection server_2 +--source include/wait_until_disconnected.inc + +--remove_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect +--write_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect +restart: --skip-slave-start=0 +EOF + +--enable_reconnect +--source include/wait_until_connected_again.inc + +--let $wait_condition= SELECT COUNT(*) = 7 FROM t1 +--source include/wait_condition.inc + +# Crash the slave just after updating relay-log.info +--source include/stop_slave.inc +--write_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect +wait +EOF +SET GLOBAL debug_dbug="+d,inject_crash_after_flush_rli"; +START SLAVE; + +--connection server_1 +INSERT INTO t1 VALUES (8); + +--connection server_2 +--source include/wait_until_disconnected.inc + +--remove_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect +--write_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect +restart: --skip-slave-start=0 +EOF + +--enable_reconnect +--source include/wait_until_connected_again.inc + +--let $wait_condition= SELECT COUNT(*) = 8 FROM t1 +--source include/wait_condition.inc + + +# Check that everything was replicated correctly. +SELECT * FROM t1 ORDER BY a; + + --connection server_1 DROP TABLE t1; diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc index cdb948f3618..d6a6ed90bd3 100644 --- a/sql/rpl_gtid.cc +++ b/sql/rpl_gtid.cc @@ -329,6 +329,7 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id, table->field[1]->store(sub_id, true); table->field[2]->store((ulonglong)gtid->server_id, true); table->field[3]->store(gtid->seq_no, true); + DBUG_EXECUTE_IF("inject_crash_before_write_rpl_slave_state", DBUG_SUICIDE();); if ((err= table->file->ha_write_row(table->record[0]))) goto end; diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 41bff96b5e5..6d53e6c3187 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -1246,7 +1246,9 @@ void Relay_log_info::stmt_done(my_off_t event_master_log_pos, DBA aware of the problem in the error log. */ } + DBUG_EXECUTE_IF("inject_crash_before_flush_rli", DBUG_SUICIDE();); flush_relay_log_info(this); + DBUG_EXECUTE_IF("inject_crash_after_flush_rli", DBUG_SUICIDE();); /* Note that Rotate_log_event::do_apply_event() does not call this function, so there is no chance that a fake rotate event resets @@ -1453,6 +1455,10 @@ rpl_load_gtid_slave_state(THD *thd) entry= (struct local_element *)rec; if (entry->sub_id >= sub_id) continue; + entry->sub_id= sub_id; + DBUG_ASSERT(entry->gtid.domain_id == domain_id); + entry->gtid.server_id= server_id; + entry->gtid.seq_no= seq_no; } else { @@ -1462,17 +1468,16 @@ rpl_load_gtid_slave_state(THD *thd) err= 1; goto end; } + entry->sub_id= sub_id; + entry->gtid.domain_id= domain_id; + entry->gtid.server_id= server_id; + entry->gtid.seq_no= seq_no; if ((err= my_hash_insert(&hash, (uchar *)entry))) { my_free(entry); goto end; } } - - entry->sub_id= sub_id; - entry->gtid.domain_id= domain_id; - entry->gtid.server_id= server_id; - entry->gtid.seq_no= seq_no; } rpl_global_gtid_slave_state.lock();