diff --git a/mysql-test/suite/multi_source/mix_semisync_async.inc b/mysql-test/suite/multi_source/mix_semisync_async.inc new file mode 100644 index 0000000000000..c9fad636e11ca --- /dev/null +++ b/mysql-test/suite/multi_source/mix_semisync_async.inc @@ -0,0 +1,84 @@ +# Test replicating from an additional connection of semi-sync mode `$mode2` +# does not interfere with the active connection of mode `$mode1`. +# +# Pre-Conditions: +# * On connection `slave`. +# * CHANGE MASTER `master1` and `master2` set up to +# replicate from the connection with the same name. +# * None of the replication threads are running. +# * User variables `$mode1` and `$mode2` contain +# corresponding `@@rpl_semi_sync_slave_enabled` values. +# +# Post-Conditions: +# * On connection `slave`. +# * None of the replication threads are running. +# * @@default_master_connection = 'master1', +# @@rpl_semi_sync_slave_enabled = $mode2 + +--let $status_var= Rpl_semi_sync_master_clients +--let $status_var_value= 1 + + +# Start `master1` +SET @@SESSION.default_master_connection= 'master1'; +--eval SET @@GLOBAL.rpl_semi_sync_slave_enabled= $mode1 +--source include/start_slave.inc + +# Start `master2` +SET @@SESSION.default_master_connection= 'master2'; +--eval SET @@GLOBAL.rpl_semi_sync_slave_enabled= $mode2 +--source include/start_slave.inc + + +# TODO MDEV-40941: +# `Rpl_semi_sync_slave_status` does not refer to `@@default_master_connection`, +# but rather the most recent START SLAVE (`$mode2` in this case). +SHOW STATUS LIKE 'Rpl_semi_sync_slave_status'; +SET @@SESSION.default_master_connection= 'master1'; +SHOW STATUS LIKE 'Rpl_semi_sync_slave_status'; + + +# Verify `master2` +--connection master2 + if ($mode2) + { + --source include/wait_for_status_var.inc + } + CREATE TABLE t2 (a INT); + DROP TABLE t2; + --save_master_pos +--connection slave +--sync_with_master 0, master2 + +# Verify `master1` +--connection master1 + if ($mode1) + { + --source include/wait_for_status_var.inc + } + CREATE TABLE t1 (a INT); + --save_master_pos +--connection slave +--sync_with_master 0, master1 +SHOW STATUS LIKE 'Rpl_semi_sync_slave_send_ack'; + + +# Stop `master2` +SET @@SESSION.default_master_connection= 'master2'; +--source include/stop_slave.inc +# TODO MDEV-40941: +# `Rpl_semi_sync_slave_status` shows OFF after STOP SLAVE on any connection. +SHOW STATUS LIKE 'Rpl_semi_sync_slave_status'; + +# Verify `master1` +--connection master1 + DROP TABLE t1; + SHOW STATUS LIKE 'Rpl_semi_sync_master_clients'; + --save_master_pos +--connection slave +--sync_with_master 0, master1 +SHOW STATUS LIKE 'Rpl_semi_sync_slave_send_ack'; + +# Stop `master1` +SET @@SESSION.default_master_connection= 'master1'; +--source include/stop_slave.inc diff --git a/mysql-test/suite/multi_source/mix_semisync_async.result b/mysql-test/suite/multi_source/mix_semisync_async.result new file mode 100644 index 0000000000000..29b33ea0331f1 --- /dev/null +++ b/mysql-test/suite/multi_source/mix_semisync_async.result @@ -0,0 +1,109 @@ +# Setup +connect master1, 127.0.0.1, root, , , $SERVER_MYPORT_1; +SET @@GLOBAL.rpl_semi_sync_master_wait_no_slave= 0; +SET @@GLOBAL.rpl_semi_sync_master_enabled= 1; +SET @@SESSION.gtid_domain_id= 1; +RESET MASTER; +connect master2, 127.0.0.1, root, , , $SERVER_MYPORT_2; +SET @@GLOBAL.rpl_semi_sync_master_wait_no_slave= 0; +SET @@GLOBAL.rpl_semi_sync_master_enabled= 1; +SET @@SESSION.gtid_domain_id= 2; +RESET MASTER; +connect slave, 127.0.0.1, root, , , $SERVER_MYPORT_3; +SET @@GLOBAL.gtid_slave_pos= ''; +CHANGE MASTER 'master1' TO +master_host='127.0.0.1', master_port=SERVER_MYPORT_1, master_user='root'; +CHANGE MASTER 'master2' TO +master_host='127.0.0.1', master_port=SERVER_MYPORT_2, master_user='root'; +# Test semi-sync replication during async replication +SET @@SESSION.default_master_connection= 'master1'; +SET @@GLOBAL.rpl_semi_sync_slave_enabled= 0; +include/start_slave.inc +SET @@SESSION.default_master_connection= 'master2'; +SET @@GLOBAL.rpl_semi_sync_slave_enabled= 1; +include/start_slave.inc +SHOW STATUS LIKE 'Rpl_semi_sync_slave_status'; +Variable_name Value +Rpl_semi_sync_slave_status ON +SET @@SESSION.default_master_connection= 'master1'; +SHOW STATUS LIKE 'Rpl_semi_sync_slave_status'; +Variable_name Value +Rpl_semi_sync_slave_status ON +connection master2; +CREATE TABLE t2 (a INT); +DROP TABLE t2; +connection slave; +connection master1; +CREATE TABLE t1 (a INT); +connection slave; +SHOW STATUS LIKE 'Rpl_semi_sync_slave_send_ack'; +Variable_name Value +Rpl_semi_sync_slave_send_ack 2 +SET @@SESSION.default_master_connection= 'master2'; +include/stop_slave.inc +SHOW STATUS LIKE 'Rpl_semi_sync_slave_status'; +Variable_name Value +Rpl_semi_sync_slave_status OFF +connection master1; +DROP TABLE t1; +SHOW STATUS LIKE 'Rpl_semi_sync_master_clients'; +Variable_name Value +Rpl_semi_sync_master_clients 0 +connection slave; +SHOW STATUS LIKE 'Rpl_semi_sync_slave_send_ack'; +Variable_name Value +Rpl_semi_sync_slave_send_ack 2 +SET @@SESSION.default_master_connection= 'master1'; +include/stop_slave.inc +# Test the other order +SET @@SESSION.default_master_connection= 'master1'; +SET @@GLOBAL.rpl_semi_sync_slave_enabled= 1; +include/start_slave.inc +SET @@SESSION.default_master_connection= 'master2'; +SET @@GLOBAL.rpl_semi_sync_slave_enabled= 0; +include/start_slave.inc +SHOW STATUS LIKE 'Rpl_semi_sync_slave_status'; +Variable_name Value +Rpl_semi_sync_slave_status OFF +SET @@SESSION.default_master_connection= 'master1'; +SHOW STATUS LIKE 'Rpl_semi_sync_slave_status'; +Variable_name Value +Rpl_semi_sync_slave_status OFF +connection master2; +CREATE TABLE t2 (a INT); +DROP TABLE t2; +connection slave; +connection master1; +CREATE TABLE t1 (a INT); +connection slave; +SHOW STATUS LIKE 'Rpl_semi_sync_slave_send_ack'; +Variable_name Value +Rpl_semi_sync_slave_send_ack 1 +SET @@SESSION.default_master_connection= 'master2'; +include/stop_slave.inc +SHOW STATUS LIKE 'Rpl_semi_sync_slave_status'; +Variable_name Value +Rpl_semi_sync_slave_status OFF +connection master1; +DROP TABLE t1; +SHOW STATUS LIKE 'Rpl_semi_sync_master_clients'; +Variable_name Value +Rpl_semi_sync_master_clients 1 +connection slave; +SHOW STATUS LIKE 'Rpl_semi_sync_slave_send_ack'; +Variable_name Value +Rpl_semi_sync_slave_send_ack 2 +SET @@SESSION.default_master_connection= 'master1'; +include/stop_slave.inc +# Cleanup +RESET SLAVE 'master1' ALL; +RESET SLAVE 'master2' ALL; +disconnect slave; +connection master1; +SET @@GLOBAL.rpl_semi_sync_master_enabled= 0; +SET @@GLOBAL.rpl_semi_sync_master_wait_no_slave= 1; +disconnect master1; +connection master2; +SET @@GLOBAL.rpl_semi_sync_master_enabled= 0; +SET @@GLOBAL.rpl_semi_sync_master_wait_no_slave= 1; +disconnect master2; diff --git a/mysql-test/suite/multi_source/mix_semisync_async.test b/mysql-test/suite/multi_source/mix_semisync_async.test new file mode 100644 index 0000000000000..8e3b035e95f26 --- /dev/null +++ b/mysql-test/suite/multi_source/mix_semisync_async.test @@ -0,0 +1,63 @@ +# MDEV-32947 Async conflicts with semi-sync in multi-source +# +# With a async master and a semi-sync master, +# test replication does not mix async and semi-sync regardless of order + +--source include/have_binlog_format_mixed.inc # format-agnostic +--let $orig_wait_no_slave= `SELECT @@GLOBAL.rpl_semi_sync_master_wait_no_slave` + +--echo # Setup +# Enable semi-sync on both masters; +# the slave will disable semi-sync on one of its connections. + +--connect (master1, 127.0.0.1, root, , , $SERVER_MYPORT_1) +SET @@GLOBAL.rpl_semi_sync_master_wait_no_slave= 0; +SET @@GLOBAL.rpl_semi_sync_master_enabled= 1; +SET @@SESSION.gtid_domain_id= 1; +RESET MASTER; + +--connect (master2, 127.0.0.1, root, , , $SERVER_MYPORT_2) +SET @@GLOBAL.rpl_semi_sync_master_wait_no_slave= 0; +SET @@GLOBAL.rpl_semi_sync_master_enabled= 1; +SET @@SESSION.gtid_domain_id= 2; +RESET MASTER; + +--connect (slave, 127.0.0.1, root, , , $SERVER_MYPORT_3) +--disable_warnings + SET @@GLOBAL.gtid_slave_pos= ''; +--enable_warnings + +--replace_result $SERVER_MYPORT_1 SERVER_MYPORT_1 +eval CHANGE MASTER 'master1' TO + master_host='127.0.0.1', master_port=$SERVER_MYPORT_1, master_user='root'; +--replace_result $SERVER_MYPORT_2 SERVER_MYPORT_2 +eval CHANGE MASTER 'master2' TO + master_host='127.0.0.1', master_port=$SERVER_MYPORT_2, master_user='root'; + + +--echo # Test semi-sync replication during async replication +--let $mode1= 0 +--let $mode2= 1 +--source mix_semisync_async.inc + +--echo # Test the other order +--let $mode1= 1 +--let $mode2= 0 +--source mix_semisync_async.inc + + +--echo # Cleanup + +RESET SLAVE 'master1' ALL; +RESET SLAVE 'master2' ALL; +--disconnect slave + +--connection master1 + SET @@GLOBAL.rpl_semi_sync_master_enabled= 0; + --eval SET @@GLOBAL.rpl_semi_sync_master_wait_no_slave= $orig_wait_no_slave +--disconnect master1 + +--connection master2 + SET @@GLOBAL.rpl_semi_sync_master_enabled= 0; + --eval SET @@GLOBAL.rpl_semi_sync_master_wait_no_slave= $orig_wait_no_slave +--disconnect master2 diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h index b471e2165c5ee..60de29036b8a0 100644 --- a/sql/rpl_mi.h +++ b/sql/rpl_mi.h @@ -376,6 +376,7 @@ class Master_info : public Slave_reporting_capability it must be ignored similarly to the replicate-same-server-id rule. */ bool do_accept_own_server_id= false; + bool semi_sync_enabled= false; /* Set to 1 when semi_sync is enabled. Set to 0 if there is any transmit problems to the slave, in which case any furter semi-sync reply is diff --git a/sql/semisync_slave.cc b/sql/semisync_slave.cc index 32e53dab7580d..45e03fa6f4c4d 100644 --- a/sql/semisync_slave.cc +++ b/sql/semisync_slave.cc @@ -46,7 +46,8 @@ int rpl_semi_sync_enabled(THD *thd, SHOW_VAR *var, void *buff, system_status_var *status_var, enum_var_type scope) { - local_semi_sync_enabled= repl_semisync_slave.get_slave_enabled(); + //TODO MDEV-40941 + local_semi_sync_enabled= repl_semisync_slave.m_slave_enabled; var->type= SHOW_BOOL; var->value= (char*) &local_semi_sync_enabled; return 0; @@ -55,14 +56,14 @@ int rpl_semi_sync_enabled(THD *thd, SHOW_VAR *var, void *buff, int Repl_semi_sync_slave::slave_read_sync_header(const uchar *header, unsigned long total_len, - int *semi_flags, + Master_info *mi, const uchar **payload, unsigned long *payload_len) { int read_res = 0; DBUG_ENTER("Repl_semi_sync_slave::slave_read_sync_header"); - if (get_slave_enabled()) + if (get_slave_enabled(mi)) { if (!DBUG_IF("semislave_corrupt_log") && header[0] == k_packet_magic_num) @@ -76,9 +77,9 @@ int Repl_semi_sync_slave::slave_read_sync_header(const uchar *header, semi_sync_need_reply)); if (semi_sync_need_reply) - *semi_flags |= SEMI_SYNC_NEED_ACK; + mi->semi_ack |= SEMI_SYNC_NEED_ACK; if (is_delay_master()) - *semi_flags |= SEMI_SYNC_SLAVE_DELAY_SYNC; + mi->semi_ack |= SEMI_SYNC_SLAVE_DELAY_SYNC; } else { @@ -111,7 +112,7 @@ void Repl_semi_sync_slave::slave_start(Master_info *mi) */ bool semi_sync= global_rpl_semi_sync_slave_enabled; - set_slave_enabled(semi_sync); + set_slave_enabled(mi, semi_sync); mi->semi_sync_reply_enabled= 0; sql_print_information("Slave I/O thread: Start %s replication to\ @@ -127,7 +128,7 @@ void Repl_semi_sync_slave::slave_start(Master_info *mi) void Repl_semi_sync_slave::slave_stop(Master_info *mi) { - if (get_slave_enabled()) + if (get_slave_enabled(mi)) { #ifdef ENABLED_DEBUG_SYNC /* @@ -144,7 +145,7 @@ void Repl_semi_sync_slave::slave_stop(Master_info *mi) kill_connection(mi); } - set_slave_enabled(0); + set_slave_enabled(mi, 0); } void Repl_semi_sync_slave::slave_reconnect(Master_info *mi) @@ -153,7 +154,7 @@ void Repl_semi_sync_slave::slave_reconnect(Master_info *mi) Start semi-sync either if it globally enabled or if was enabled before the reconnect. */ - if (global_rpl_semi_sync_slave_enabled || get_slave_enabled()) + if (global_rpl_semi_sync_slave_enabled || get_slave_enabled(mi)) slave_start(mi); } @@ -215,7 +216,7 @@ int Repl_semi_sync_slave::request_transmit(Master_info *mi) MYSQL_ROW row; const char *query; - if (!get_slave_enabled()) + if (!get_slave_enabled(mi)) return 0; query= "SHOW VARIABLES LIKE 'rpl_semi_sync_master_enabled'"; @@ -223,7 +224,7 @@ int Repl_semi_sync_slave::request_transmit(Master_info *mi) !(res= mysql_store_result(mysql))) { sql_print_error("Execution failed on master: %s, error :%s", query, mysql_error(mysql)); - set_slave_enabled(0); + set_slave_enabled(mi, 0); return 1; } @@ -234,7 +235,7 @@ int Repl_semi_sync_slave::request_transmit(Master_info *mi) if (!row) sql_print_warning("Master server does not support semi-sync, " "fallback to asynchronous replication"); - set_slave_enabled(0); + set_slave_enabled(mi, 0); mysql_free_result(res); return 0; } @@ -253,7 +254,7 @@ int Repl_semi_sync_slave::request_transmit(Master_info *mi) if (mysql_real_query(mysql, query, (ulong)strlen(query))) { sql_print_error("%s on master failed", query); - set_slave_enabled(0); + set_slave_enabled(mi, 0); return 1; } mi->semi_sync_reply_enabled= 1; @@ -276,7 +277,7 @@ int Repl_semi_sync_slave::slave_reply(Master_info *mi) int reply_res = 0; size_t name_len = strlen(binlog_filename); DBUG_ENTER("Repl_semi_sync_slave::slave_reply"); - DBUG_ASSERT(get_slave_enabled() && mi->semi_sync_reply_enabled); + DBUG_ASSERT(get_slave_enabled(mi) && mi->semi_sync_reply_enabled); /* Prepare the buffer of the reply. */ reply_buffer[REPLY_MAGIC_NUM_OFFSET] = k_packet_magic_num; diff --git a/sql/semisync_slave.h b/sql/semisync_slave.h index 79949067de8c3..776891e1e0936 100644 --- a/sql/semisync_slave.h +++ b/sql/semisync_slave.h @@ -17,6 +17,7 @@ #ifndef SEMISYNC_SLAVE_H #define SEMISYNC_SLAVE_H +#ifdef HAVE_REPLICATION #include "semisync.h" #include "my_global.h" @@ -45,12 +46,12 @@ class Repl_semi_sync_slave */ int init_object(); - inline bool get_slave_enabled() { - return m_slave_enabled; + inline bool get_slave_enabled(Master_info *mi) { + return mi->semi_sync_enabled; } - void set_slave_enabled(bool enabled) { - m_slave_enabled = enabled; + void set_slave_enabled(Master_info *mi, bool enabled) { + m_slave_enabled= mi->semi_sync_enabled= enabled; } inline bool is_delay_master(){ @@ -71,8 +72,8 @@ class Repl_semi_sync_slave * Input: * header - (IN) packet header pointer * total_len - (IN) total packet length: metadata + payload - * semi_flags - (IN) store flags: SEMI_SYNC_SLAVE_DELAY_SYNC and - SEMI_SYNC_NEED_ACK + * mi - (IN) store flags: SEMI_SYNC_SLAVE_DELAY_SYNC and + SEMI_SYNC_NEED_ACK, to Master_info::semi_ack * payload - (IN) payload: the replication event * payload_len - (IN) payload length * @@ -80,7 +81,7 @@ class Repl_semi_sync_slave * 0: success; non-zero: error */ int slave_read_sync_header(const uchar *header, unsigned long total_len, - int *semi_flags, + Master_info *mi, const uchar **payload, unsigned long *payload_len); /* A slave replies to the master indicating its replication process. It @@ -94,10 +95,11 @@ class Repl_semi_sync_slave int request_transmit(Master_info *mi); void kill_connection(Master_info *mi); + /// @deprecated For `Rpl_semi_sync_slave_status` compatibility only + bool m_slave_enabled; private: /* True when init_object has been called */ bool m_init_done; - bool m_slave_enabled; /* semi-sync is enabled on the slave */ bool m_delay_master; unsigned int m_kill_conn_timeout; }; @@ -115,4 +117,5 @@ extern unsigned long long rpl_semi_sync_slave_send_ack; extern int rpl_semi_sync_enabled(THD *thd, SHOW_VAR *var, void *buff, system_status_var *status_var, enum_var_type scope); +#endif /* HAVE_REPLICATION */ #endif /* SEMISYNC_SLAVE_H */ diff --git a/sql/slave.cc b/sql/slave.cc index 54550a42eff89..9b844f0f371ea 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -5293,7 +5293,7 @@ Stopping slave I/O thread due to out-of-memory error from master"); if (repl_semisync_slave. slave_read_sync_header((const uchar*) mysql->net.read_pos + 1, event_len, - &(mi->semi_ack), &event_buf, &event_len)) + mi, &event_buf, &event_len)) { mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL, ER_THD(thd, ER_SLAVE_FATAL_ERROR), @@ -5350,7 +5350,7 @@ Stopping slave I/O thread due to out-of-memory error from master"); goto err; } - if (repl_semisync_slave.get_slave_enabled() && + if (repl_semisync_slave.get_slave_enabled(mi) && mi->semi_sync_reply_enabled && (mi->semi_ack & SEMI_SYNC_NEED_ACK)) { @@ -5394,7 +5394,7 @@ Stopping slave I/O thread due to out-of-memory error from master"); master info only when ack is needed. This may lead to at least one group transaction delay but affords better performance improvement. */ - (!repl_semisync_slave.get_slave_enabled() || + (!repl_semisync_slave.get_slave_enabled(mi) || (!(mi->semi_ack & SEMI_SYNC_SLAVE_DELAY_SYNC) || (mi->semi_ack & (SEMI_SYNC_NEED_ACK)))) && (DBUG_IF("failed_flush_master_info") || @@ -7072,7 +7072,7 @@ static int queue_event(Master_info* mi, const uchar *buf, ulong event_len) */ mi->do_accept_own_server_id= (s_id == global_system_variables.server_id && - repl_semisync_slave.get_slave_enabled() && opt_gtid_strict_mode && + repl_semisync_slave.get_slave_enabled(mi) && opt_gtid_strict_mode && mi->using_gtid != Master_info::USE_GTID_NO && !mysql_bin_log.check_strict_gtid_sequence(event_gtid.domain_id, event_gtid.server_id,