mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 03:52:35 +01:00
7af50e4df4
rpl_semi_sync_slave_enabled_consistent.test and the first part of the commit message comes from Brandon Nesterenko. A test to show how to induce the "Read semi-sync reply magic number error" message on a primary. In short, if semi-sync is turned on during the hand-shake process between a primary and replica, but later a user negates the rpl_semi_sync_slave_enabled variable while the replica's IO thread is running; if the io thread exits, the replica can skip a necessary call to kill_connection() in repl_semisync_slave.slave_stop() due to its reliance on a global variable. Then, the replica will send a COM_QUIT packet to the primary on an active semi-sync connection, causing the magic number error. The test in this patch exits the IO thread by forcing an error; though note a call to STOP SLAVE could also do this, but it ends up needing more synchronization. That is, the STOP SLAVE command also tries to kill the VIO of the replica, which makes a race with the IO thread to try and send the COM_QUIT before this happens (which would need more debug_sync to get around). See THD::awake_no_mutex for details as to the killing of the replica’s vio. Notes: - The MariaDB documentation does not make it clear that when one enables semi-sync replication it does not matter if one enables it first in the master or slave. Any order works. Changes done: - The rpl_semi_sync_slave_enabled variable is now a default value for when semisync is started. The variable does not anymore affect semisync if it is already running. This fixes the original reported bug. Internally we now use repl_semisync_slave.get_slave_enabled() instead of rpl_semi_sync_slave_enabled. To check if semisync is active on should check the @@rpl_semi_sync_slave_status variable (as before). - The semisync protocol conflicts in the way that the original MySQL/MariaDB client-server protocol was designed (client-server send and reply packets are strictly ordered and includes a packet number to allow one to check if a packet is lost). When using semi-sync the master and slave can send packets at 'any time', so packet numbering does not work. The 'solution' has been that each communication starts with packet number 1, but in some cases there is still a chance that the packet number check can fail. Fixed by adding a flag (pkt_nr_can_be_reset) in the NET struct that one can use to signal that packet number checking should not be done. This is flag is set when semi-sync is used. - Added Master_info::semi_sync_reply_enabled to allow one to configure some slaves with semisync and other other slaves without semisync. Removed global variable semi_sync_need_reply that would not work with multi-master. - Repl_semi_sync_master::report_reply_packet() can now recognize the COM_QUIT packet from semisync slave and not give a "Read semi-sync reply magic number error" error for this case. The slave will be removed from the Ack listener. - On Windows, don't stop semisync Ack listener just because one slave connection is using socket_id > FD_SETSIZE. - Removed busy loop in Ack_receiver::run() by using "Self-pipe trick" to signal new slave and stop Ack_receiver. - Changed some Repl_semi_sync_slave functions that always returns 0 from int to void. - Added Repl_semi_sync_slave::slave_reconnect(). - Removed dummy_function Repl_semi_sync_slave::reset_slave(). - Removed some duplicate semisync notes from the error log. - Add test of "if (get_slave_enabled() && semi_sync_need_reply)" before calling Repl_semi_sync_slave::slave_reply(). (Speeds up the code as we can skip all initializations). - If epl_semisync_slave.slave_reply() fails, we disable semisync for that connection. - We do not call semisync.switch_off() if there are no active slaves. Instead we check in Repl_semi_sync_master::commit_trx() if there are no active threads. This simplices the code. - Changed assert() to DBUG_ASSERT() to ensure that the DBUG log is flushed in case of asserts. - Removed the internal rpl_semi_sync_slave_status as it is not needed anymore. The @@rpl_semi_sync_slave_status status variable is now mapped to rpl_semi_sync_enabled. - Removed rpl_semi_sync_slave_enabled as it is not needed anymore. Repl_semi_sync_slave::get_slave_enabled() contains the active status. - Added checking that we do not add a slave twice with Ack_receiver::add_slave(). This could happen with old code. - Removed Repl_semi_sync_master::check_and_switch() as it is not needed anymore. - Ensure that when we call Ack_receiver::remove_slave() that the slave is removed from the listener before function returns. - Call listener.listen_on_sockets() outside of mutex for better performance and less contested mutex. - Ensure that listening is ignoring newly added slaves when checking for responses. - Fixed the master ack_receiver listener is not killed if there are no connected slaves (and thus stop semisync handling of future connections). This could happen if all slaves sockets where would be marked as unreliable. - Added unlink() to base_ilist_iterator and remove() to I_List_iterator. This enables us to remove 'dead' slaves in Ack_recever::run(). - kill_zombie_dump_threads() now does killing of dump threads properly. - It can now kill several threads (should be impossible but could happen if IO slaves reconnects very fast). - We now wait until the dump thread is done before starting the dump. - Added an error if kill_zombie_dump_threads() fails. - Set thd->variables.server_id before calling kill_zombie_dump_threads(). This simplies the code. - Added a lot of comments both in code and tests. - Removed DBUG_EVALUATE_IF "failed_slave_start" as it is not used. Test changes: - rpl.rpl_session_var2 added which runs rpl.rpl_session_var test with semisync enabled. - Some timings changed slight with startup of slave which caused rpl_binlog_dump_slave_gtid_state_info.text to fail as it checked the error log file before the slave had started properly. Fixed by adding wait_for_pattern_in_file.inc that allows waiting for the pattern to appear in the log file. - Tests have been updated so that we first set rpl_semi_sync_master_enabled on the master and then set rpl_semi_sync_slave_enabled on the slaves (this is according to how the MariaDB documentation document how to setup semi-sync). - Error text "Master server does not have semi-sync enabled" has been replaced with "Master server does not support semi-sync" for the case when the master supports semi-sync but semi-sync is not enabled. Other things: - Some trivial cleanups in Repl_semi_sync_master::update_sync_header(). - We should in 11.3 changed the default value for rpl-semi-sync-master-wait-no-slave from TRUE to FALSE as the TRUE does not make much sense as default. The main difference with using FALSE is that we do not wait for semisync Ack if there are no slave threads. In the case of TRUE we wait once, which did not bring any notable benefits except slower startup of master configured for using semisync. Co-author: Brandon Nesterenko <brandon.nesterenko@mariadb.com> This solves the problem reported in MDEV-32960 where a new slave may not be registered in time and the master disables semi sync because of that.
80 lines
3 KiB
C
80 lines
3 KiB
C
/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
|
|
|
|
#ifndef SQL_REPL_INCLUDED
|
|
#define SQL_REPL_INCLUDED
|
|
|
|
#include "rpl_filter.h"
|
|
|
|
#ifdef HAVE_REPLICATION
|
|
#include "slave.h"
|
|
|
|
struct slave_connection_state;
|
|
|
|
extern my_bool opt_show_slave_auth_info;
|
|
extern char *master_host, *master_info_file;
|
|
|
|
extern int max_binlog_dump_events;
|
|
extern my_bool opt_sporadic_binlog_dump_fail;
|
|
|
|
int start_slave(THD* thd, Master_info* mi, bool net_report);
|
|
int stop_slave(THD* thd, Master_info* mi, bool net_report);
|
|
bool change_master(THD* thd, Master_info* mi, bool *master_info_added);
|
|
bool mysql_show_binlog_events(THD* thd);
|
|
int reset_slave(THD *thd, Master_info* mi);
|
|
int reset_master(THD* thd, rpl_gtid *init_state, uint32 init_state_len,
|
|
ulong next_log_number);
|
|
bool purge_master_logs(THD* thd, const char* to_log);
|
|
bool purge_master_logs_before_date(THD* thd, time_t purge_time);
|
|
bool log_in_use(const char* log_name);
|
|
void adjust_linfo_offsets(my_off_t purge_offset);
|
|
void show_binlogs_get_fields(THD *thd, List<Item> *field_list);
|
|
bool show_binlogs(THD* thd);
|
|
extern int init_master_info(Master_info* mi);
|
|
bool kill_zombie_dump_threads(THD *thd, uint32 slave_server_id);
|
|
int check_binlog_magic(IO_CACHE* log, const char** errmsg);
|
|
int compare_log_name(const char *log_1, const char *log_2);
|
|
|
|
struct LOAD_FILE_IO_CACHE : public IO_CACHE
|
|
{
|
|
THD* thd;
|
|
my_off_t last_pos_in_file;
|
|
bool wrote_create_file, log_delayed;
|
|
int (*real_read_function)(struct st_io_cache *,uchar *,size_t);
|
|
};
|
|
|
|
int log_loaded_block(IO_CACHE* file, uchar *Buffer, size_t Count);
|
|
int init_replication_sys_vars();
|
|
void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, ushort flags);
|
|
|
|
#ifdef HAVE_PSI_INTERFACE
|
|
extern PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state;
|
|
#endif
|
|
void rpl_init_gtid_slave_state();
|
|
void rpl_deinit_gtid_slave_state();
|
|
void rpl_init_gtid_waiting();
|
|
void rpl_deinit_gtid_waiting();
|
|
int gtid_state_from_binlog_pos(const char *name, uint32 pos, String *out_str);
|
|
int rpl_append_gtid_state(String *dest, bool use_binlog);
|
|
int rpl_load_gtid_state(slave_connection_state *state, bool use_binlog);
|
|
bool rpl_gtid_pos_check(THD *thd, char *str, size_t len);
|
|
bool rpl_gtid_pos_update(THD *thd, char *str, size_t len);
|
|
#else
|
|
|
|
struct LOAD_FILE_IO_CACHE : public IO_CACHE { };
|
|
|
|
#endif /* HAVE_REPLICATION */
|
|
|
|
#endif /* SQL_REPL_INCLUDED */
|