Description:
While designing some test cases I found an issue that was preventing
"START SLAVE UNTIL" with master binlog file/position or slave relay
log file/position clauses to stop if the position in the until clause
was of the last transaction present on the relay log.
The UNTIL verification for master binlog file/position or slave
relay log file/position clauses is only checked inside next_event()
function, after calling read_log_event(). If read_log_event() return
no event because it reached the end of the relay log, it will not
check the UNTIL clause, and will wait for the relay log update by
calling relay_log.wait_for_update_relay_log().
There is a UNTIL verification in the exec_relay_log_event() function,
before calling next_event() function, but only for until clauses
containing SQL_AFTER_GTIDS option.
How to repeat:
Use the following MTR test cases:
=== FIRST TEST CASE ===
$ cat rpl_until_pos_in_last_group_of_master_binlog.test
#
# Check if SQL thread stops correctly
# after executing the last event of the master binlog
# based on master binlog file and position
#
# This test with GTIDs will hit BUG#18306199, so disabling GTIDs by now
--source include/not_gtid_enabled.inc
--source include/master-slave.inc
# Stop the SQL thread after syncing
--source include/sync_slave_sql_with_master.inc
--source include/stop_slave_sql.inc
# Save master position to use on UNTIL clause of START SLAVE
--source include/rpl_connection_master.inc
--let $master_file= query_get_value(SHOW MASTER STATUS, File, 1)
--let $master_pos= query_get_value(SHOW MASTER STATUS, Position, 1)
--inc $master_pos
# Create a table in the master
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
# Sync the IO thread with the master
--source include/sync_slave_io_with_master.inc
# Start SQL thread until it executed the CREATE TABLE
eval START SLAVE SQL_THREAD UNTIL MASTER_LOG_FILE = "$master_file", MASTER_LOG_POS = $master_pos;
# Wait until SQL thread reaches last master binlog file
--let $slave_param= Relay_Master_Log_File
--let $slave_param_value= $master_file
--let $slave_param_comparison= =
--source include/wait_for_slave_param.inc
# Wait until SQL thread reaches desired master binlog position
--let $slave_param= Exec_Master_Log_Pos
--let $slave_param_value= $master_pos
--let $slave_param_comparison= >=
--source include/wait_for_slave_param.inc
# If the desired position was reached, SQL thread should stop
--let $slave_param_comparison= =
--source include/wait_for_slave_sql_to_stop.inc
# Cleanup
--source include/start_slave_sql.inc
--source include/rpl_connection_master.inc
DROP TABLE t1;
--source include/rpl_end.inc
=== SECOND TEST CASE ===
$ cat rpl_until_pos_in_last_group_of_slave_relaylog.test
#
# Check if SQL thread stops correctly
# after executing the last event of the slave relaylog
# based on slave relaylog file and position
#
# This test with GTIDs will hit BUG#18306199, so disabling GTIDs by now
--source include/not_gtid_enabled.inc
--source include/master-slave.inc
# Stop the SQL thread after syncing
--source include/sync_slave_sql_with_master.inc
--source include/stop_slave_sql.inc
# Save slave position to use on UNTIL clause of START SLAVE
--source include/rpl_connection_slave.inc
--let $slave_file= query_get_value(SHOW SLAVE STATUS, Relay_Log_File, 1)
--let $slave_pos= query_get_value(SHOW SLAVE STATUS, Relay_Log_Pos, 1)
--inc $slave_pos
# Create a table in the master
--source include/rpl_connection_master.inc
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
# Sync the IO thread with the master
--source include/sync_slave_io_with_master.inc
--echo # Start SQL thread until it executed the CREATE TABLE
eval START SLAVE SQL_THREAD UNTIL RELAY_LOG_FILE = "$slave_file", RELAY_LOG_POS = $slave_pos;
# Wait until SQL thread reaches last slave relaylog file
--let $slave_param= Relay_Log_File
--let $slave_param_value= $slave_file
--let $slave_param_comparison= =
--source include/wait_for_slave_param.inc
# Wait until SQL thread reaches desired slave relaylog position
--let $slave_param= Relay_Log_Pos
--let $slave_param_value= $slave_pos
--let $slave_param_comparison= >=
--source include/wait_for_slave_param.inc
# If the desired position was reached, SQL thread should stop
--let $slave_param_comparison= =
--source include/wait_for_slave_sql_to_stop.inc
# Cleanup
--source include/start_slave_sql.inc
--source include/rpl_connection_master.inc
DROP TABLE t1;
--source include/rpl_end.inc
Suggested fix:
=== modified file 'mysql-test/include/wait_for_slave_sql_to_stop.inc'
--- mysql-test/include/wait_for_slave_sql_to_stop.inc revid:sujatha.sivakumar@oracle.com-20140616044742-3ibgiv08ygux3a9j
+++ mysql-test/include/wait_for_slave_sql_to_stop.inc 2014-06-18 11:08:07 +0000
@@ -31,6 +31,7 @@
--let $slave_param= Slave_SQL_Running
--let $slave_param_value= No
+--let $slave_param_comparison= =
--let $slave_error_param= Last_SQL_Errno
--source include/wait_for_slave_param.inc
--let $slave_error_param=
=== modified file 'sql/rpl_slave.cc'
--- sql/rpl_slave.cc revid:sujatha.sivakumar@oracle.com-20140616044742-3ibgiv08ygux3a9j
+++ sql/rpl_slave.cc 2014-06-18 11:05:09 +0000
@@ -3796,14 +3796,20 @@
mysql_mutex_lock(&rli->data_lock);
/*
- UNTIL_SQL_AFTER_GTIDS requires special handling since we have to check
- whether the until_condition is satisfied *before* the SQL threads goes on
- a wait inside next_event() for the relay log to grow. This is reuired since
- if we have already applied the last event in the waiting set but since he
- check happens only at the start of the next event we may end up waiting
- forever the next event is not available or is delayed.
+ UNTIL_SQL_AFTER_GTIDS, UNTIL_MASTER_POS and UNTIL_RELAY_POS requires
+ special handling since we have to check whether the until_condition is
+ satisfied *before* the SQL threads goes on a wait inside next_event()
+ for the relay log to grow.
+ This is required in the following case: We have already applied the last
+ event in the waiting set, but the relay log ends after this event. Then it
+ is not enough to check the condition in next_event; we also have to check
+ it here, before going to sleep. Otherwise, if no updates were coming from
+ the master, we would sleep forever despite having reached the required
+ position.
*/
- if (rli->until_condition == Relay_log_info::UNTIL_SQL_AFTER_GTIDS &&
+ if ((rli->until_condition == Relay_log_info::UNTIL_SQL_AFTER_GTIDS ||
+ rli->until_condition == Relay_log_info::UNTIL_MASTER_POS ||
+ rli->until_condition == Relay_log_info::UNTIL_RELAY_POS) &&
rli->is_until_satisfied(thd, NULL))
{
rli->abort_slave= 1;
Description: While designing some test cases I found an issue that was preventing "START SLAVE UNTIL" with master binlog file/position or slave relay log file/position clauses to stop if the position in the until clause was of the last transaction present on the relay log. The UNTIL verification for master binlog file/position or slave relay log file/position clauses is only checked inside next_event() function, after calling read_log_event(). If read_log_event() return no event because it reached the end of the relay log, it will not check the UNTIL clause, and will wait for the relay log update by calling relay_log.wait_for_update_relay_log(). There is a UNTIL verification in the exec_relay_log_event() function, before calling next_event() function, but only for until clauses containing SQL_AFTER_GTIDS option. How to repeat: Use the following MTR test cases: === FIRST TEST CASE === $ cat rpl_until_pos_in_last_group_of_master_binlog.test # # Check if SQL thread stops correctly # after executing the last event of the master binlog # based on master binlog file and position # # This test with GTIDs will hit BUG#18306199, so disabling GTIDs by now --source include/not_gtid_enabled.inc --source include/master-slave.inc # Stop the SQL thread after syncing --source include/sync_slave_sql_with_master.inc --source include/stop_slave_sql.inc # Save master position to use on UNTIL clause of START SLAVE --source include/rpl_connection_master.inc --let $master_file= query_get_value(SHOW MASTER STATUS, File, 1) --let $master_pos= query_get_value(SHOW MASTER STATUS, Position, 1) --inc $master_pos # Create a table in the master CREATE TABLE t1 (a INT) ENGINE=InnoDB; # Sync the IO thread with the master --source include/sync_slave_io_with_master.inc # Start SQL thread until it executed the CREATE TABLE eval START SLAVE SQL_THREAD UNTIL MASTER_LOG_FILE = "$master_file", MASTER_LOG_POS = $master_pos; # Wait until SQL thread reaches last master binlog file --let $slave_param= Relay_Master_Log_File --let $slave_param_value= $master_file --let $slave_param_comparison= = --source include/wait_for_slave_param.inc # Wait until SQL thread reaches desired master binlog position --let $slave_param= Exec_Master_Log_Pos --let $slave_param_value= $master_pos --let $slave_param_comparison= >= --source include/wait_for_slave_param.inc # If the desired position was reached, SQL thread should stop --let $slave_param_comparison= = --source include/wait_for_slave_sql_to_stop.inc # Cleanup --source include/start_slave_sql.inc --source include/rpl_connection_master.inc DROP TABLE t1; --source include/rpl_end.inc === SECOND TEST CASE === $ cat rpl_until_pos_in_last_group_of_slave_relaylog.test # # Check if SQL thread stops correctly # after executing the last event of the slave relaylog # based on slave relaylog file and position # # This test with GTIDs will hit BUG#18306199, so disabling GTIDs by now --source include/not_gtid_enabled.inc --source include/master-slave.inc # Stop the SQL thread after syncing --source include/sync_slave_sql_with_master.inc --source include/stop_slave_sql.inc # Save slave position to use on UNTIL clause of START SLAVE --source include/rpl_connection_slave.inc --let $slave_file= query_get_value(SHOW SLAVE STATUS, Relay_Log_File, 1) --let $slave_pos= query_get_value(SHOW SLAVE STATUS, Relay_Log_Pos, 1) --inc $slave_pos # Create a table in the master --source include/rpl_connection_master.inc CREATE TABLE t1 (a INT) ENGINE=InnoDB; # Sync the IO thread with the master --source include/sync_slave_io_with_master.inc --echo # Start SQL thread until it executed the CREATE TABLE eval START SLAVE SQL_THREAD UNTIL RELAY_LOG_FILE = "$slave_file", RELAY_LOG_POS = $slave_pos; # Wait until SQL thread reaches last slave relaylog file --let $slave_param= Relay_Log_File --let $slave_param_value= $slave_file --let $slave_param_comparison= = --source include/wait_for_slave_param.inc # Wait until SQL thread reaches desired slave relaylog position --let $slave_param= Relay_Log_Pos --let $slave_param_value= $slave_pos --let $slave_param_comparison= >= --source include/wait_for_slave_param.inc # If the desired position was reached, SQL thread should stop --let $slave_param_comparison= = --source include/wait_for_slave_sql_to_stop.inc # Cleanup --source include/start_slave_sql.inc --source include/rpl_connection_master.inc DROP TABLE t1; --source include/rpl_end.inc Suggested fix: === modified file 'mysql-test/include/wait_for_slave_sql_to_stop.inc' --- mysql-test/include/wait_for_slave_sql_to_stop.inc revid:sujatha.sivakumar@oracle.com-20140616044742-3ibgiv08ygux3a9j +++ mysql-test/include/wait_for_slave_sql_to_stop.inc 2014-06-18 11:08:07 +0000 @@ -31,6 +31,7 @@ --let $slave_param= Slave_SQL_Running --let $slave_param_value= No +--let $slave_param_comparison= = --let $slave_error_param= Last_SQL_Errno --source include/wait_for_slave_param.inc --let $slave_error_param= === modified file 'sql/rpl_slave.cc' --- sql/rpl_slave.cc revid:sujatha.sivakumar@oracle.com-20140616044742-3ibgiv08ygux3a9j +++ sql/rpl_slave.cc 2014-06-18 11:05:09 +0000 @@ -3796,14 +3796,20 @@ mysql_mutex_lock(&rli->data_lock); /* - UNTIL_SQL_AFTER_GTIDS requires special handling since we have to check - whether the until_condition is satisfied *before* the SQL threads goes on - a wait inside next_event() for the relay log to grow. This is reuired since - if we have already applied the last event in the waiting set but since he - check happens only at the start of the next event we may end up waiting - forever the next event is not available or is delayed. + UNTIL_SQL_AFTER_GTIDS, UNTIL_MASTER_POS and UNTIL_RELAY_POS requires + special handling since we have to check whether the until_condition is + satisfied *before* the SQL threads goes on a wait inside next_event() + for the relay log to grow. + This is required in the following case: We have already applied the last + event in the waiting set, but the relay log ends after this event. Then it + is not enough to check the condition in next_event; we also have to check + it here, before going to sleep. Otherwise, if no updates were coming from + the master, we would sleep forever despite having reached the required + position. */ - if (rli->until_condition == Relay_log_info::UNTIL_SQL_AFTER_GTIDS && + if ((rli->until_condition == Relay_log_info::UNTIL_SQL_AFTER_GTIDS || + rli->until_condition == Relay_log_info::UNTIL_MASTER_POS || + rli->until_condition == Relay_log_info::UNTIL_RELAY_POS) && rli->is_until_satisfied(thd, NULL)) { rli->abort_slave= 1;