############################# # Common setup for all tests ############################# # Note: Simulated slave delay is hardcoded to 800 milliseconds # Note: Simulated master shutdown delay is hardcoded to 500 milliseconds include/rpl_init.inc [topology=1->2, 1->3] connection server_1; # Slaves which simulate an error will produce a timeout on the primary call mtr.add_suppression("Timeout waiting"); call mtr.add_suppression("did not exit"); # Suppress slave errors related to the simulated error connection server_2; call mtr.add_suppression("reply failed"); call mtr.add_suppression("Replication event checksum verification"); call mtr.add_suppression("Relay log write failure"); call mtr.add_suppression("Failed to kill the active semi-sync connection"); connection server_3; call mtr.add_suppression("reply failed"); call mtr.add_suppression("Replication event checksum verification"); call mtr.add_suppression("Relay log write failure"); call mtr.add_suppression("Failed to kill the active semi-sync connection"); connection server_1; CREATE TABLE t1 (a int); connection server_2; connection server_3; connect server_1_con2, localhost, root,,; ############################# # Test cases ############################# # # Test Case 1) If both replicas simulate a delay that is within the # allowed timeout, the primary should delay killing the suspended thread # until an ACK is received (Rpl_semi_sync_master_yes_tx should be 1). # connection server_1; #-- #-- Semi-sync Setup connection server_1; #-- Enable semi-sync on slaves let slave_last= 3 connection server_2; set global rpl_semi_sync_slave_enabled = 1; include/stop_slave.inc include/start_slave.inc show status like 'Rpl_semi_sync_slave_status'; Variable_name Value Rpl_semi_sync_slave_status ON connection server_3; set global rpl_semi_sync_slave_enabled = 1; include/stop_slave.inc include/start_slave.inc show status like 'Rpl_semi_sync_slave_status'; Variable_name Value Rpl_semi_sync_slave_status ON #-- Enable semi-sync on master connection server_1; SET @@GLOBAL.rpl_semi_sync_master_enabled = 1; set @@global.rpl_semi_sync_master_timeout= 1600; #-- Wait for master to recognize semi-sync slaves connection server_1; #-- Master should have semi-sync enabled with 2 connections show status like 'Rpl_semi_sync_master_status'; Variable_name Value Rpl_semi_sync_master_status ON show status like 'Rpl_semi_sync_master_clients'; Variable_name Value Rpl_semi_sync_master_clients 2 #-- Prepare servers to simulate delay or error connection server_1; SET @@GLOBAL.debug_dbug= ""; connection server_2; SET @@GLOBAL.debug_dbug= "+d,simulate_delay_semisync_slave_reply"; connection server_3; SET @@GLOBAL.debug_dbug= "+d,simulate_delay_semisync_slave_reply"; #-- #-- Test begins connection server_1; #-- Begin semi-sync transaction INSERT INTO t1 VALUES (1); connection server_1_con2; #-- Wait until master recognizes a connection is awaiting semi-sync ACK show status like 'Rpl_semi_sync_master_wait_sessions'; Variable_name Value Rpl_semi_sync_master_wait_sessions 1 #-- Give enough time after timeout/ack received to query yes_tx/no_tx SET @@GLOBAL.debug_dbug= "+d,delay_shutdown_phase_2_after_semisync_wait"; #-- Begin master shutdown SHUTDOWN WAIT FOR ALL SLAVES; connection server_1; #-- Ensure either ACK was received (yes_tx=1) or timeout (no_tx=1) show status like 'Rpl_semi_sync_master_yes_tx'; Variable_name Value Rpl_semi_sync_master_yes_tx 1 show status like 'Rpl_semi_sync_master_no_tx'; Variable_name Value Rpl_semi_sync_master_no_tx 0 connection server_1_con2; # Check logs to ensure shutdown was delayed FOUND 1 /Delaying shutdown to await semi-sync ACK/ in mysqld.1.err # Validate slave data is in correct state connection server_2; select count(*)=1 from t1; count(*)=1 1 connection server_3; select count(*)=1 from t1; count(*)=1 1 # #-- Re-synchronize slaves with master and disable semi-sync #-- Stop slaves connection server_2; SET @@GLOBAL.debug_dbug= ""; SET @@GLOBAL.rpl_semi_sync_slave_enabled= 0; include/stop_slave.inc connection server_3; SET @@GLOBAL.debug_dbug= ""; SET @@GLOBAL.rpl_semi_sync_slave_enabled= 0; include/stop_slave.inc #-- Bring the master back up connection server_1_con2; connection default; connection server_1; SET @@GLOBAL.debug_dbug= ""; SET @@GLOBAL.rpl_semi_sync_master_enabled = 0; show status like 'Rpl_semi_sync_master_status'; Variable_name Value Rpl_semi_sync_master_status OFF TRUNCATE TABLE t1; #-- Bring slaves back up connection server_2; include/start_slave.inc show status like 'Rpl_semi_sync_slave_status'; Variable_name Value Rpl_semi_sync_slave_status OFF SELECT COUNT(*)=0 from t1; COUNT(*)=0 1 connection server_3; include/start_slave.inc show status like 'Rpl_semi_sync_slave_status'; Variable_name Value Rpl_semi_sync_slave_status OFF SELECT COUNT(*)=0 from t1; COUNT(*)=0 1 # # Test Case 2) If both replicas simulate an error before sending an ACK, # the primary should delay killing the suspended thread until the # timeout is reached (Rpl_semi_sync_master_no_tx should be 1). # connection server_1; #-- #-- Semi-sync Setup connection server_1; #-- Enable semi-sync on slaves let slave_last= 3 connection server_2; set global rpl_semi_sync_slave_enabled = 1; include/stop_slave.inc include/start_slave.inc show status like 'Rpl_semi_sync_slave_status'; Variable_name Value Rpl_semi_sync_slave_status ON connection server_3; set global rpl_semi_sync_slave_enabled = 1; include/stop_slave.inc include/start_slave.inc show status like 'Rpl_semi_sync_slave_status'; Variable_name Value Rpl_semi_sync_slave_status ON #-- Enable semi-sync on master connection server_1; SET @@GLOBAL.rpl_semi_sync_master_enabled = 1; set @@global.rpl_semi_sync_master_timeout= 500; #-- Wait for master to recognize semi-sync slaves connection server_1; #-- Master should have semi-sync enabled with 2 connections show status like 'Rpl_semi_sync_master_status'; Variable_name Value Rpl_semi_sync_master_status ON show status like 'Rpl_semi_sync_master_clients'; Variable_name Value Rpl_semi_sync_master_clients 2 #-- Prepare servers to simulate delay or error connection server_1; SET @@GLOBAL.debug_dbug= "+d,mysqld_delay_kill_threads_phase_1"; connection server_2; SET @@GLOBAL.debug_dbug= "+d,corrupt_queue_event"; connection server_3; SET @@GLOBAL.debug_dbug= "+d,corrupt_queue_event"; #-- #-- Test begins connection server_1; #-- Begin semi-sync transaction INSERT INTO t1 VALUES (1); connection server_1_con2; #-- Wait until master recognizes a connection is awaiting semi-sync ACK show status like 'Rpl_semi_sync_master_wait_sessions'; Variable_name Value Rpl_semi_sync_master_wait_sessions 1 #-- Give enough time after timeout/ack received to query yes_tx/no_tx SET @@GLOBAL.debug_dbug= "+d,delay_shutdown_phase_2_after_semisync_wait"; #-- Begin master shutdown SHUTDOWN WAIT FOR ALL SLAVES; connection server_1; #-- Ensure either ACK was received (yes_tx=1) or timeout (no_tx=1) show status like 'Rpl_semi_sync_master_yes_tx'; Variable_name Value Rpl_semi_sync_master_yes_tx 0 show status like 'Rpl_semi_sync_master_no_tx'; Variable_name Value Rpl_semi_sync_master_no_tx 1 connection server_1_con2; # Check logs to ensure shutdown was delayed FOUND 2 /Delaying shutdown to await semi-sync ACK/ in mysqld.1.err # Validate slave data is in correct state connection server_2; select count(*)=0 from t1; count(*)=0 1 connection server_3; select count(*)=0 from t1; count(*)=0 1 # #-- Re-synchronize slaves with master and disable semi-sync #-- Stop slaves connection server_2; SET @@GLOBAL.debug_dbug= ""; SET @@GLOBAL.rpl_semi_sync_slave_enabled= 0; include/stop_slave.inc connection server_3; SET @@GLOBAL.debug_dbug= ""; SET @@GLOBAL.rpl_semi_sync_slave_enabled= 0; include/stop_slave.inc #-- Bring the master back up connection server_1_con2; connection default; connection server_1; SET @@GLOBAL.debug_dbug= ""; SET @@GLOBAL.rpl_semi_sync_master_enabled = 0; show status like 'Rpl_semi_sync_master_status'; Variable_name Value Rpl_semi_sync_master_status OFF TRUNCATE TABLE t1; #-- Bring slaves back up connection server_2; include/start_slave.inc show status like 'Rpl_semi_sync_slave_status'; Variable_name Value Rpl_semi_sync_slave_status OFF SELECT COUNT(*)=0 from t1; COUNT(*)=0 1 connection server_3; include/start_slave.inc show status like 'Rpl_semi_sync_slave_status'; Variable_name Value Rpl_semi_sync_slave_status OFF SELECT COUNT(*)=0 from t1; COUNT(*)=0 1 # # Test Case 3) If one replica simulates a delay within the allowed # timeout and the other simulates an error before sending an ACK, the # primary should delay killing the suspended thread until it receives an # ACK from the delayed slave (Rpl_semi_sync_master_yes_tx should be 1). # connection server_1; #-- #-- Semi-sync Setup connection server_1; #-- Enable semi-sync on slaves let slave_last= 3 connection server_2; set global rpl_semi_sync_slave_enabled = 1; include/stop_slave.inc include/start_slave.inc show status like 'Rpl_semi_sync_slave_status'; Variable_name Value Rpl_semi_sync_slave_status ON connection server_3; set global rpl_semi_sync_slave_enabled = 1; include/stop_slave.inc include/start_slave.inc show status like 'Rpl_semi_sync_slave_status'; Variable_name Value Rpl_semi_sync_slave_status ON #-- Enable semi-sync on master connection server_1; SET @@GLOBAL.rpl_semi_sync_master_enabled = 1; set @@global.rpl_semi_sync_master_timeout= 1600; #-- Wait for master to recognize semi-sync slaves connection server_1; #-- Master should have semi-sync enabled with 2 connections show status like 'Rpl_semi_sync_master_status'; Variable_name Value Rpl_semi_sync_master_status ON show status like 'Rpl_semi_sync_master_clients'; Variable_name Value Rpl_semi_sync_master_clients 2 #-- Prepare servers to simulate delay or error connection server_1; SET @@GLOBAL.debug_dbug= "+d,mysqld_delay_kill_threads_phase_1"; connection server_2; SET @@GLOBAL.debug_dbug= "+d,corrupt_queue_event"; connection server_3; SET @@GLOBAL.debug_dbug= "+d,simulate_delay_semisync_slave_reply"; #-- #-- Test begins connection server_1; #-- Begin semi-sync transaction INSERT INTO t1 VALUES (1); connection server_1_con2; #-- Wait until master recognizes a connection is awaiting semi-sync ACK show status like 'Rpl_semi_sync_master_wait_sessions'; Variable_name Value Rpl_semi_sync_master_wait_sessions 1 #-- Give enough time after timeout/ack received to query yes_tx/no_tx SET @@GLOBAL.debug_dbug= "+d,delay_shutdown_phase_2_after_semisync_wait"; #-- Begin master shutdown SHUTDOWN WAIT FOR ALL SLAVES; connection server_1; #-- Ensure either ACK was received (yes_tx=1) or timeout (no_tx=1) show status like 'Rpl_semi_sync_master_yes_tx'; Variable_name Value Rpl_semi_sync_master_yes_tx 1 show status like 'Rpl_semi_sync_master_no_tx'; Variable_name Value Rpl_semi_sync_master_no_tx 0 connection server_1_con2; # Check logs to ensure shutdown was delayed FOUND 3 /Delaying shutdown to await semi-sync ACK/ in mysqld.1.err # Validate slave data is in correct state connection server_2; select count(*)=0 from t1; count(*)=0 1 connection server_3; select count(*)=1 from t1; count(*)=1 1 # #-- Re-synchronize slaves with master and disable semi-sync #-- Stop slaves connection server_2; SET @@GLOBAL.debug_dbug= ""; SET @@GLOBAL.rpl_semi_sync_slave_enabled= 0; include/stop_slave.inc connection server_3; SET @@GLOBAL.debug_dbug= ""; SET @@GLOBAL.rpl_semi_sync_slave_enabled= 0; include/stop_slave.inc #-- Bring the master back up connection server_1_con2; connection default; connection server_1; SET @@GLOBAL.debug_dbug= ""; SET @@GLOBAL.rpl_semi_sync_master_enabled = 0; show status like 'Rpl_semi_sync_master_status'; Variable_name Value Rpl_semi_sync_master_status OFF TRUNCATE TABLE t1; #-- Bring slaves back up connection server_2; include/start_slave.inc show status like 'Rpl_semi_sync_slave_status'; Variable_name Value Rpl_semi_sync_slave_status OFF SELECT COUNT(*)=0 from t1; COUNT(*)=0 1 connection server_3; include/start_slave.inc show status like 'Rpl_semi_sync_slave_status'; Variable_name Value Rpl_semi_sync_slave_status OFF SELECT COUNT(*)=0 from t1; COUNT(*)=0 1 # # Test Case 4) If a replica errors before sending an ACK, it will cause # the IO thread to stop and handle the error. During error handling, if # semi-sync is active, the replica will form a new connection with the # primary to kill the active connection. However, if the primary is # shutting down, it may kill the new connection, thereby leaving the # active semi-sync connection in-tact. The slave should notice this, and # not issue a `QUIT` command to the primary, which would otherwise be # sent to kill an active connection. This test case validates that the # slave does not send a `QUIT` in this case (Rpl_semi_sync_master_yes_tx # should be 1 because server_3 will send the ACK within a valid timeout). # connection server_1; #-- #-- Semi-sync Setup connection server_1; #-- Enable semi-sync on slaves let slave_last= 3 connection server_2; set global rpl_semi_sync_slave_enabled = 1; include/stop_slave.inc include/start_slave.inc show status like 'Rpl_semi_sync_slave_status'; Variable_name Value Rpl_semi_sync_slave_status ON connection server_3; set global rpl_semi_sync_slave_enabled = 1; include/stop_slave.inc include/start_slave.inc show status like 'Rpl_semi_sync_slave_status'; Variable_name Value Rpl_semi_sync_slave_status ON #-- Enable semi-sync on master connection server_1; SET @@GLOBAL.rpl_semi_sync_master_enabled = 1; set @@global.rpl_semi_sync_master_timeout= 1600; #-- Wait for master to recognize semi-sync slaves connection server_1; #-- Master should have semi-sync enabled with 2 connections show status like 'Rpl_semi_sync_master_status'; Variable_name Value Rpl_semi_sync_master_status ON show status like 'Rpl_semi_sync_master_clients'; Variable_name Value Rpl_semi_sync_master_clients 2 #-- Prepare servers to simulate delay or error connection server_1; SET @@GLOBAL.debug_dbug= "+d,mysqld_delay_kill_threads_phase_1"; connection server_2; SET @@GLOBAL.debug_dbug= "+d,corrupt_queue_event,slave_delay_killing_semisync_connection"; connection server_3; SET @@GLOBAL.debug_dbug= "+d,simulate_delay_semisync_slave_reply"; #-- #-- Test begins connection server_1; #-- Begin semi-sync transaction INSERT INTO t1 VALUES (1); connection server_1_con2; #-- Wait until master recognizes a connection is awaiting semi-sync ACK show status like 'Rpl_semi_sync_master_wait_sessions'; Variable_name Value Rpl_semi_sync_master_wait_sessions 1 #-- Give enough time after timeout/ack received to query yes_tx/no_tx SET @@GLOBAL.debug_dbug= "+d,delay_shutdown_phase_2_after_semisync_wait"; #-- Begin master shutdown SHUTDOWN WAIT FOR ALL SLAVES; connection server_1; #-- Ensure either ACK was received (yes_tx=1) or timeout (no_tx=1) show status like 'Rpl_semi_sync_master_yes_tx'; Variable_name Value Rpl_semi_sync_master_yes_tx 1 show status like 'Rpl_semi_sync_master_no_tx'; Variable_name Value Rpl_semi_sync_master_no_tx 0 connection server_1_con2; # Check logs to ensure shutdown was delayed FOUND 4 /Delaying shutdown to await semi-sync ACK/ in mysqld.1.err # Validate slave data is in correct state connection server_2; select count(*)=0 from t1; count(*)=0 1 connection server_3; select count(*)=1 from t1; count(*)=1 1 # #-- Re-synchronize slaves with master and disable semi-sync #-- Stop slaves connection server_2; SET @@GLOBAL.debug_dbug= ""; SET @@GLOBAL.rpl_semi_sync_slave_enabled= 0; include/stop_slave.inc connection server_3; SET @@GLOBAL.debug_dbug= ""; SET @@GLOBAL.rpl_semi_sync_slave_enabled= 0; include/stop_slave.inc #-- Bring the master back up connection server_1_con2; connection default; connection server_1; SET @@GLOBAL.debug_dbug= ""; SET @@GLOBAL.rpl_semi_sync_master_enabled = 0; show status like 'Rpl_semi_sync_master_status'; Variable_name Value Rpl_semi_sync_master_status OFF TRUNCATE TABLE t1; #-- Bring slaves back up connection server_2; include/start_slave.inc show status like 'Rpl_semi_sync_slave_status'; Variable_name Value Rpl_semi_sync_slave_status OFF SELECT COUNT(*)=0 from t1; COUNT(*)=0 1 connection server_3; include/start_slave.inc show status like 'Rpl_semi_sync_slave_status'; Variable_name Value Rpl_semi_sync_slave_status OFF SELECT COUNT(*)=0 from t1; COUNT(*)=0 1 ############################# # Cleanup ############################# connection server_2; include/stop_slave.inc include/start_slave.inc connection server_3; include/stop_slave.inc include/start_slave.inc connection server_1; drop table t1; include/rpl_end.inc