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..5eb8e2123b8d5 --- /dev/null +++ b/mysql-test/suite/multi_source/mix_semisync_async.inc @@ -0,0 +1,72 @@ +# 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 + + +# 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` +--source include/stop_slave.inc + +# 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..36ccbf257413d --- /dev/null +++ b/mysql-test/suite/multi_source/mix_semisync_async.result @@ -0,0 +1,89 @@ +# 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', master_ssl_verify_server_cert=0; +CHANGE MASTER 'master2' TO +master_host='127.0.0.1', master_port=SERVER_MYPORT_2, +master_user='root', master_ssl_verify_server_cert=0; +# 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 +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 +include/stop_slave.inc +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 +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 +include/stop_slave.inc +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..2e91d23fb68e3 --- /dev/null +++ b/mysql-test/suite/multi_source/mix_semisync_async.test @@ -0,0 +1,65 @@ +# 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', master_ssl_verify_server_cert=0; +--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', master_ssl_verify_server_cert=0; + + +--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 d45eb009a122c..12ab9390ec3ed 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..31de85549f18d 100644 --- a/sql/semisync_slave.cc +++ b/sql/semisync_slave.cc @@ -40,29 +40,36 @@ int Repl_semi_sync_slave::init_object() return result; } -static bool local_semi_sync_enabled; - 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(); - var->type= SHOW_BOOL; - var->value= (char*) &local_semi_sync_enabled; + if (Master_info *mi= + get_master_info(&thd->variables.default_master_connection, + Sql_condition::WARN_LEVEL_NOTE)) + { + *static_cast(buff)= + repl_semisync_slave.get_slave_enabled(mi); + mi->release(); + var->type= SHOW_BOOL; + var->value= buff; + } + else + var->type= SHOW_UNDEF; return 0; } 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 +83,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 +118,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 +134,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 +151,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 +160,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 +222,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 +230,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 +241,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 +260,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 +283,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..ef7ad8383329a 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" @@ -33,7 +34,6 @@ class Master_info; class Repl_semi_sync_slave :public Repl_semi_sync_base { public: - Repl_semi_sync_slave() :m_slave_enabled(false) {} ~Repl_semi_sync_slave() = default; void set_trace_level(unsigned long trace_level) { @@ -45,12 +45,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) { + mi->semi_sync_enabled= enabled; } inline bool is_delay_master(){ @@ -71,8 +71,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 +80,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 @@ -97,7 +97,6 @@ class Repl_semi_sync_slave 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 +114,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 3af008a77eb36..e9f5bfebaaea2 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -5296,7 +5296,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), @@ -5353,7 +5353,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)) { @@ -5397,7 +5397,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") || @@ -7410,7 +7410,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,