From f3e00431de0cb4bb545921bdc62e11f60a09ded6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Tue, 20 Feb 2018 15:35:52 +0200 Subject: [PATCH 01/18] Fix MXS-1418 regression If a server is removed from a service, readconnroute will not verify that the server it is connected to is still the same root master. This fixes the regression of MXS-1418. --- server/modules/routing/readconnroute/readconnroute.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/server/modules/routing/readconnroute/readconnroute.c b/server/modules/routing/readconnroute/readconnroute.c index 788da87d2..82f61ed42 100644 --- a/server/modules/routing/readconnroute/readconnroute.c +++ b/server/modules/routing/readconnroute/readconnroute.c @@ -551,12 +551,20 @@ static inline bool connection_is_valid(ROUTER_INSTANCE* inst, ROUTER_CLIENT_SES* if (SERVER_IS_RUNNING(router_cli_ses->backend->server) && (router_cli_ses->backend->server->status & inst->bitmask & inst->bitvalue)) { - if (inst->bitvalue & SERVER_MASTER) + if ((inst->bitvalue & SERVER_MASTER) && router_cli_ses->backend->active) { + // If we're using an active master server, verify that it is still a master rval = router_cli_ses->backend == get_root_master(inst->service->dbref); } else { + /** + * Either we don't use master type servers or the server reference + * is deactivated. We let deactivated connection close gracefully + * so we simply assume it is OK. This allows a server to be taken + * out of use in a manner that won't cause errors to the connected + * clients. + */ rval = true; } } From 1ecd791887994209eb29e56e1271f8c407cd0cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Wed, 21 Feb 2018 09:35:42 +0200 Subject: [PATCH 02/18] MXS-1678: Store master_id even when IO thread is stopped When the IO thread of a relay master is stopped, the knowledge that it is not a real master but a relay master is lost. To prevent this loss of information, the master server's server_id value should always be stored if it is available. --- server/modules/monitor/mysqlmon/mysql_mon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/modules/monitor/mysqlmon/mysql_mon.c b/server/modules/monitor/mysqlmon/mysql_mon.c index 14226c14b..c8bb5bd22 100644 --- a/server/modules/monitor/mysqlmon/mysql_mon.c +++ b/server/modules/monitor/mysqlmon/mysql_mon.c @@ -457,7 +457,7 @@ static inline void monitor_mysql_db(MXS_MONITOR_SERVERS* database, MYSQL_SERVER_ * root master server. * Please note, there could be no slaves at all if Slave_SQL_Running == 'No' */ - if (serv_info->slave_io && server_version != MYSQL_SERVER_VERSION_51) + if (server_version != MYSQL_SERVER_VERSION_51) { /* Get Master_Server_Id */ master_id = atol(row[i_master_id]); From 8e31b30d19e14854f30e5a618f74c161172677d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Wed, 21 Feb 2018 10:43:12 +0200 Subject: [PATCH 03/18] MXS-1678: Add test case Added test case that checks that relay master status is not lost when IO thread is stopped. --- maxscale-system-test/CMakeLists.txt | 4 + maxscale-system-test/mxs1678_relay_master.cpp | 86 +++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 maxscale-system-test/mxs1678_relay_master.cpp diff --git a/maxscale-system-test/CMakeLists.txt b/maxscale-system-test/CMakeLists.txt index aaf91c21a..73d22d729 100644 --- a/maxscale-system-test/CMakeLists.txt +++ b/maxscale-system-test/CMakeLists.txt @@ -530,6 +530,10 @@ add_test_executable(mxs1543.cpp mxs1543 avro LABELS REPL_BACKEND) # https://jira.mariadb.org/browse/MXS-1585 add_test_executable(mxs1585.cpp mxs1585 mxs1585 LABELS REPL_BACKEND) +# MXS-1678: Stopping IO thread on relay master causes it to be promoted as master +# https://jira.mariadb.org/browse/MXS-1678 +add_test_executable(mxs1678_relay_master.cpp mxs1678_relay_master replication LABELS REPL_BACKEND) + # 'namedserverfilter' test add_test_executable(namedserverfilter.cpp namedserverfilter namedserverfilter LABELS namedserverfilter LIGHT REPL_BACKEND) diff --git a/maxscale-system-test/mxs1678_relay_master.cpp b/maxscale-system-test/mxs1678_relay_master.cpp new file mode 100644 index 000000000..bee2a0a97 --- /dev/null +++ b/maxscale-system-test/mxs1678_relay_master.cpp @@ -0,0 +1,86 @@ +/** + * MXS-1678: Stopping IO thread on relay master causes it to be promoted as master + * + * https://jira.mariadb.org/browse/MXS-1678 + */ +#include "testconnections.h" +#include +#include + +typedef std::set StringSet; + +// Note: This is backported from 2.2 and can be replaced with MaxScales::get_server_status once merged +StringSet state(TestConnections& test, const char* name) +{ + StringSet rval; + char* res = test.ssh_maxscale_output(true, "maxadmin list servers|grep \'%s\'", name); + char* pipe = strrchr(res, '|'); + + if (res && pipe) + { + pipe++; + char* tok = strtok(pipe, ","); + + while (tok) + { + char* p = tok; + char *end = strchr(tok, '\n'); + if (!end) + { + end = strchr(tok, '\0'); + } + + // Trim leading whitespace + while (p < end && isspace(*p)) + { + p++; + } + + // Trim trailing whitespace + while (end > tok && isspace(*end)) + { + *end-- = '\0'; + } + + rval.insert(p); + tok = strtok(NULL, ",\n"); + } + } + + free(res); + + return rval; +} + + +int main(int argc, char** argv) +{ + TestConnections test(argc, argv); + test.repl->connect(); + execute_query(test.repl->nodes[3], "STOP SLAVE"); + execute_query(test.repl->nodes[3], "CHANGE MASTER TO MASTER_HOST='%s', MASTER_PORT=%d", + test.repl->IP_private[2], test.repl->port[2]); + execute_query(test.repl->nodes[3], "START SLAVE"); + + StringSet master = {"Master", "Running"}; + StringSet slave = {"Slave", "Running"}; + StringSet relay_master = {"Relay Master", "Slave", "Running"}; + + test.tprintf("Checking before stopping IO thread"); + test.add_result(state(test, "server1") != master, "server1 is not a master"); + test.add_result(state(test, "server2") != slave, "server2 is not a slave"); + test.add_result(state(test, "server3") != relay_master, "server3 is not a relay master"); + test.add_result(state(test, "server4") != slave, "server4 is not a slave"); + + execute_query(test.repl->nodes[2], "STOP SLAVE IO_THREAD"); + sleep(10); + + test.tprintf("Checking after stopping IO thread"); + test.add_result(state(test, "server1") != master, "server1 is not a master"); + test.add_result(state(test, "server2") != slave, "server2 is not a slave"); + test.add_result(state(test, "server3") != relay_master, "server3 is not a relay master"); + test.add_result(state(test, "server4") != slave, "server4 is not a slave"); + + test.repl->fix_replication(); + return test.global_result; +} From ff55106610881d55db88eca9e2ef6a056cbc8d51 Mon Sep 17 00:00:00 2001 From: Esa Korhonen Date: Tue, 20 Feb 2018 12:55:33 +0200 Subject: [PATCH 04/18] MariaDB Monitor: Move additional classes to separate file Also use stl containers in monitor definition. --- .../modules/monitor/mariadbmon/CMakeLists.txt | 2 +- .../modules/monitor/mariadbmon/mariadbmon.cc | 409 +++++------------- .../modules/monitor/mariadbmon/mariadbmon.hh | 34 +- .../modules/monitor/mariadbmon/utilities.cc | 112 +++++ .../modules/monitor/mariadbmon/utilities.hh | 127 ++++++ 5 files changed, 367 insertions(+), 317 deletions(-) create mode 100644 server/modules/monitor/mariadbmon/utilities.cc create mode 100644 server/modules/monitor/mariadbmon/utilities.hh diff --git a/server/modules/monitor/mariadbmon/CMakeLists.txt b/server/modules/monitor/mariadbmon/CMakeLists.txt index ba5aaebb0..9a208764e 100644 --- a/server/modules/monitor/mariadbmon/CMakeLists.txt +++ b/server/modules/monitor/mariadbmon/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(mariadbmon SHARED mariadbmon.cc) +add_library(mariadbmon SHARED mariadbmon.cc utilities.cc) target_link_libraries(mariadbmon maxscale-common) add_dependencies(mariadbmon pcre2) set_target_properties(mariadbmon PROPERTIES VERSION "1.4.0") diff --git a/server/modules/monitor/mariadbmon/mariadbmon.cc b/server/modules/monitor/mariadbmon/mariadbmon.cc index ee10f4347..1db72d999 100644 --- a/server/modules/monitor/mariadbmon/mariadbmon.cc +++ b/server/modules/monitor/mariadbmon/mariadbmon.cc @@ -35,6 +35,7 @@ #include // TODO: For monitorAddParameters #include "../../../core/internal/monitor.h" +#include "utilities.hh" /** Column positions for SHOW SLAVE STATUS */ #define MYSQL55_STATUS_MASTER_LOG_POS 5 @@ -69,16 +70,9 @@ } while (false) using std::string; -typedef std::vector ServerVector; typedef std::vector StringVector; class MySqlServerInfo; - -enum mysql_server_version -{ - MYSQL_SERVER_VERSION_100, - MYSQL_SERVER_VERSION_55, - MYSQL_SERVER_VERSION_51 -}; +class MariaDBMonitor; enum slave_down_setting_t { @@ -101,36 +95,37 @@ static json_t* diagnostics_json(const MXS_MONITOR *); static MXS_MONITORED_SERVER *getServerByNodeId(MXS_MONITORED_SERVER *, long); static MXS_MONITORED_SERVER *getSlaveOfNodeId(MXS_MONITORED_SERVER *, long, slave_down_setting_t); static MXS_MONITORED_SERVER *get_replication_tree(MXS_MONITOR *, int); -static void set_master_heartbeat(MYSQL_MONITOR *, MXS_MONITORED_SERVER *); +static void set_master_heartbeat(MariaDBMonitor *, MXS_MONITORED_SERVER *); static void set_slave_heartbeat(MXS_MONITOR *, MXS_MONITORED_SERVER *); static int add_slave_to_master(long *, int, long); static bool isMySQLEvent(mxs_monitor_event_t event); void check_maxscale_schema_replication(MXS_MONITOR *monitor); -static MySqlServerInfo* get_server_info(const MYSQL_MONITOR* handle, const MXS_MONITORED_SERVER* db); -static bool mon_process_failover(MYSQL_MONITOR*, uint32_t, bool*); -static bool do_failover(MYSQL_MONITOR* mon, json_t** output); -static bool do_switchover(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* current_master, +static MySqlServerInfo* get_server_info(MariaDBMonitor* handle, const MXS_MONITORED_SERVER* db); +static const MySqlServerInfo* get_server_info(const MariaDBMonitor* handle, const MXS_MONITORED_SERVER* db); +static bool mon_process_failover(MariaDBMonitor*, uint32_t, bool*); +static bool do_failover(MariaDBMonitor* mon, json_t** output); +static bool do_switchover(MariaDBMonitor* mon, MXS_MONITORED_SERVER* current_master, MXS_MONITORED_SERVER* new_master, json_t** err_out); -static bool update_gtids(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER *database, MySqlServerInfo* info); +static bool update_gtids(MariaDBMonitor* mon, MXS_MONITORED_SERVER *database, MySqlServerInfo* info); static bool update_replication_settings(MXS_MONITORED_SERVER *database, MySqlServerInfo* info); static bool query_one_row(MXS_MONITORED_SERVER *database, const char* query, unsigned int expected_cols, StringVector* output); static void read_server_variables(MXS_MONITORED_SERVER* database, MySqlServerInfo* serv_info); -static bool server_is_rejoin_suspect(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* server, +static bool server_is_rejoin_suspect(MariaDBMonitor* mon, MXS_MONITORED_SERVER* server, MySqlServerInfo* master_info); -static bool get_joinable_servers(MYSQL_MONITOR* mon, ServerVector* output); -static uint32_t do_rejoin(MYSQL_MONITOR* mon, const ServerVector& servers); +static bool get_joinable_servers(MariaDBMonitor* mon, ServerVector* output); +static uint32_t do_rejoin(MariaDBMonitor* mon, const ServerVector& servers); static bool join_cluster(MXS_MONITORED_SERVER* server, const char* change_cmd); -static void disable_setting(MYSQL_MONITOR* mon, const char* setting); -static bool cluster_can_be_joined(MYSQL_MONITOR* mon); -static bool can_replicate_from(MYSQL_MONITOR* mon, +static void disable_setting(MariaDBMonitor* mon, const char* setting); +static bool cluster_can_be_joined(MariaDBMonitor* mon); +static bool can_replicate_from(MariaDBMonitor* mon, MXS_MONITORED_SERVER* slave, MySqlServerInfo* slave_info, MXS_MONITORED_SERVER* master, MySqlServerInfo* master_info); -static bool wait_cluster_stabilization(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* new_master, +static bool wait_cluster_stabilization(MariaDBMonitor* mon, MXS_MONITORED_SERVER* new_master, const ServerVector& slaves, int seconds_remaining); static string get_connection_errors(const ServerVector& servers); static int64_t scan_server_id(const char* id_string); -static string generate_change_master_cmd(MYSQL_MONITOR* mon, const string& master_host, int master_port); +static string generate_change_master_cmd(MariaDBMonitor* mon, const string& master_host, int master_port); static bool report_version_err = true; static const char* hb_table_name = "maxscale_schema.replication_heartbeat"; @@ -160,185 +155,18 @@ static const char CN_REPLICATION_PASSWORD[] = "replication_password"; #define DEFAULT_MASTER_FAILURE_TIMEOUT "10" /** Server id default value */ -static const int64_t SERVER_ID_UNKNOWN = -1; +const int64_t SERVER_ID_UNKNOWN = -1; /** Default port */ -static const int PORT_UNKNOWN = 0; +const int PORT_UNKNOWN = 0; -class Gtid -{ -public: - uint32_t domain; - int64_t server_id; // Is actually 32bit unsigned. 0 is only used by server versions <= 10.1 - uint64_t sequence; - Gtid() - : domain(0) - , server_id(SERVER_ID_UNKNOWN) - , sequence(0) - {} +MariaDBMonitor::MariaDBMonitor() +{} - /** - * Parse a Gtid-triplet from a string. In case of a multi-triplet value, only the triplet with - * the given domain is returned. - * - * @param str Gtid string - * @param search_domain The Gtid domain whose triplet should be returned. Negative domain stands for - * autoselect, which is only allowed when the string contains one triplet. - */ - Gtid(const char* str, int64_t search_domain = -1) - : domain(0) - , server_id(SERVER_ID_UNKNOWN) - , sequence(0) - { - // Autoselect only allowed with one triplet - ss_dassert(search_domain >= 0 || strchr(str, ',') == NULL); - parse_triplet(str); - if (search_domain >= 0 && domain != search_domain) - { - // Search for the correct triplet. - bool found = false; - for (const char* next_triplet = strchr(str, ','); - next_triplet != NULL && !found; - next_triplet = strchr(next_triplet, ',')) - { - parse_triplet(++next_triplet); - if (domain == search_domain) - { - found = true; - } - } - ss_dassert(found); - } - } - bool operator == (const Gtid& rhs) const - { - return domain == rhs.domain && - server_id != SERVER_ID_UNKNOWN && server_id == rhs.server_id && - sequence == rhs.sequence; - } - string to_string() const - { - std::stringstream ss; - if (server_id != SERVER_ID_UNKNOWN) - { - ss << domain << "-" << server_id << "-" << sequence; - } - return ss.str(); - } -private: - void parse_triplet(const char* str) - { - ss_debug(int rv = ) sscanf(str, "%" PRIu32 "-%" PRId64 "-%" PRIu64, &domain, &server_id, &sequence); - ss_dassert(rv == 3); - } -}; +MariaDBMonitor::~MariaDBMonitor() +{} -// Contains data returned by one row of SHOW ALL SLAVES STATUS -class SlaveStatusInfo -{ -public: - int64_t master_server_id; /**< The master's server_id value. Valid ids are 32bit unsigned. -1 is - * unread/error. */ - string master_host; /**< Master server host name. */ - int master_port; /**< Master server port. */ - bool slave_io_running; /**< Whether the slave I/O thread is running and connected. */ - bool slave_sql_running; /**< Whether or not the SQL thread is running. */ - string master_log_file; /**< Name of the master binary log file that the I/O thread is currently - * reading from. */ - uint64_t read_master_log_pos; /**< Position up to which the I/O thread has read in the current master - * binary log file. */ - Gtid gtid_io_pos; /**< Gtid I/O position of the slave thread. Only shows the triplet with - * the current master domain. */ - string last_error; /**< Last IO or SQL error encountered. */ - - SlaveStatusInfo() - : master_server_id(SERVER_ID_UNKNOWN) - , master_port(0) - , slave_io_running(false) - , slave_sql_running(false) - , read_master_log_pos(0) - {} -}; - -// This class groups some miscellaneous replication related settings together. -class ReplicationSettings -{ -public: - bool gtid_strict_mode; /**< Enable additional checks for replication */ - bool log_bin; /**< Is binary logging enabled */ - bool log_slave_updates; /**< Does the slave log replicated events to binlog */ - ReplicationSettings() - : gtid_strict_mode(false) - , log_bin(false) - , log_slave_updates(false) - {} -}; - -/** - * Monitor specific information about a server - * - * Note: These are initialized in @c init_server_info - */ -class MySqlServerInfo -{ -public: - int64_t server_id; /**< Value of @@server_id. Valid values are 32bit unsigned. */ - int group; /**< Multi-master group where this server belongs, - * 0 for servers not in groups */ - bool read_only; /**< Value of @@read_only */ - bool slave_configured; /**< Whether SHOW SLAVE STATUS returned rows */ - bool binlog_relay; /** Server is a Binlog Relay */ - int n_slaves_configured; /**< Number of configured slave connections*/ - int n_slaves_running; /**< Number of running slave connections */ - int slave_heartbeats; /**< Number of received heartbeats */ - double heartbeat_period; /**< The time interval between heartbeats */ - time_t latest_event; /**< Time when latest event was received from the master */ - int64_t gtid_domain_id; /**< The value of gtid_domain_id, the domain which is used for - * new non-replicated events. */ - Gtid gtid_current_pos; /**< Gtid of latest event. Only shows the triplet - * with the current master domain. */ - Gtid gtid_binlog_pos; /**< Gtid of latest event written to binlog. Only shows - * the triplet with the current master domain. */ - SlaveStatusInfo slave_status; /**< Data returned from SHOW SLAVE STATUS */ - ReplicationSettings rpl_settings; /**< Miscellaneous replication related settings */ - mysql_server_version version; /**< Server version, 10.X, 5.5 or 5.1 */ - - MySqlServerInfo() - : server_id(SERVER_ID_UNKNOWN) - , group(0) - , read_only(false) - , slave_configured(false) - , binlog_relay(false) - , n_slaves_configured(0) - , n_slaves_running(0) - , slave_heartbeats(0) - , heartbeat_period(0) - , latest_event(0) - , gtid_domain_id(-1) - , version(MYSQL_SERVER_VERSION_51) - {} - - /** - * Calculate how many events are left in the relay log. If gtid_current_pos is ahead of Gtid_IO_Pos, - * or a server_id is unknown, an error value is returned. - * - * @return Number of events in relay log according to latest queried info. A negative value signifies - * an error in the gtid-values. - */ - int64_t relay_log_events() - { - if (slave_status.gtid_io_pos.server_id != SERVER_ID_UNKNOWN && - gtid_current_pos.server_id != SERVER_ID_UNKNOWN && - slave_status.gtid_io_pos.domain == gtid_current_pos.domain && - slave_status.gtid_io_pos.sequence >= gtid_current_pos.sequence) - { - return slave_status.gtid_io_pos.sequence - gtid_current_pos.sequence; - } - return -1; - } -}; - -bool uses_gtid(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* mon_server, json_t** error_out) +bool uses_gtid(MariaDBMonitor* mon, MXS_MONITORED_SERVER* mon_server, json_t** error_out) { bool rval = false; const MySqlServerInfo* info = get_server_info(mon, mon_server); @@ -354,6 +182,7 @@ bool uses_gtid(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* mon_server, json_t** er } return rval; } + /** * Check that the given server is a master and it's the only master. * @@ -364,7 +193,7 @@ bool uses_gtid(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* mon_server, json_t** er * @return False, if there is some error with the specified current master, * True otherwise. */ -bool mysql_switchover_check_current(const MYSQL_MONITOR* mon, +bool mysql_switchover_check_current(const MariaDBMonitor* mon, const MXS_MONITORED_SERVER* suggested_curr_master, json_t** error_out) { @@ -436,7 +265,7 @@ bool mysql_switchover_check_new(const MXS_MONITORED_SERVER* monitored_server, js * @param error_out JSON error out * @return True if failover may proceed */ -bool failover_check(MYSQL_MONITOR* mon, json_t** error_out) +bool failover_check(MariaDBMonitor* mon, json_t** error_out) { // Check that there is no running master and that there is at least one running server in the cluster. // Also, all slaves must be using gtid-replication. @@ -508,7 +337,7 @@ bool mysql_switchover(MXS_MONITOR* mon, MXS_MONITORED_SERVER* new_master, MXS_MO } bool rval = false; - MYSQL_MONITOR* handle = static_cast(mon->handle); + MariaDBMonitor* handle = static_cast(mon->handle); bool current_ok = mysql_switchover_check_current(handle, current_master, error_out); bool new_ok = mysql_switchover_check_new(new_master, error_out); @@ -599,7 +428,7 @@ bool mysql_handle_switchover(const MODULECMD_ARG* args, json_t** error_out) else { // Autoselect current master - MYSQL_MONITOR* handle = static_cast(mon->handle); + MariaDBMonitor* handle = static_cast(mon->handle); if (handle->master) { mon_curr_master = handle->master; @@ -650,7 +479,7 @@ bool mysql_failover(MXS_MONITOR* mon, json_t** output) } bool rv = true; - MYSQL_MONITOR *handle = static_cast(mon->handle); + MariaDBMonitor *handle = static_cast(mon->handle); rv = failover_check(handle, output); if (rv) { @@ -719,7 +548,7 @@ bool mysql_rejoin(MXS_MONITOR* mon, SERVER* rejoin_server, json_t** output) } bool rval = false; - MYSQL_MONITOR *handle = static_cast(mon->handle); + MariaDBMonitor *handle = static_cast(mon->handle); if (cluster_can_be_joined(handle)) { MXS_MONITORED_SERVER* mon_server = mon_get_monitored_server(mon, rejoin_server); @@ -941,41 +770,34 @@ void info_free_func(void *val) * @brief Helper function that initializes the server info hashtable * * @param handle MariaDB monitor handle - * @param database List of monitored databases * @return True on success, false if initialization failed. At the moment * initialization can only fail if memory allocation fails. */ -bool init_server_info(MYSQL_MONITOR *handle, MXS_MONITORED_SERVER *database) +void init_server_info(MariaDBMonitor *handle) { - bool rval = true; + ServerInfoMap& infos = handle->server_info; + infos.clear(); - MySqlServerInfo info; - - while (database) + for (MXS_MONITORED_SERVER* server = handle->monitor->monitored_servers; server; server = server->next) { - /** Delete any existing structures and replace them with empty ones */ - hashtable_delete(handle->server_info, database->server->unique_name); - - if (!hashtable_add(handle->server_info, database->server->unique_name, &info)) - { - rval = false; - break; - } - - database = database->next; + ServerInfoMap::value_type new_val(server, MySqlServerInfo()); + infos.insert(new_val); } - - return rval; } -static MySqlServerInfo* get_server_info(const MYSQL_MONITOR* handle, const MXS_MONITORED_SERVER* db) +static MySqlServerInfo* get_server_info(MariaDBMonitor* handle, const MXS_MONITORED_SERVER* db) { - void* value = hashtable_fetch(handle->server_info, db->server->unique_name); - ss_dassert(value); - return static_cast(value); + ServerInfoMap::iterator iter = handle->server_info.find(db); + ss_dassert(iter != handle->server_info.end()); + return &iter->second; } -static bool set_replication_credentials(MYSQL_MONITOR *handle, const MXS_CONFIG_PARAMETER* params) +static const MySqlServerInfo* get_server_info(const MariaDBMonitor* handle, const MXS_MONITORED_SERVER* db) +{ + return get_server_info(const_cast(handle), db); +} + +static bool set_replication_credentials(MariaDBMonitor *handle, const MXS_CONFIG_PARAMETER* params) { bool rval = false; const char* repl_user = config_get_string(params, CN_REPLICATION_USER); @@ -1007,9 +829,10 @@ static bool set_replication_credentials(MYSQL_MONITOR *handle, const MXS_CONFIG_ * @param server Server to test * @return True if server is in the excluded-list of the monitor. */ -static bool server_is_excluded(const MYSQL_MONITOR *handle, const MXS_MONITORED_SERVER* server) +static bool server_is_excluded(const MariaDBMonitor *handle, const MXS_MONITORED_SERVER* server) { - for (int i = 0; i < handle->n_excluded; i++) + size_t n_excluded = handle->excluded_servers.size(); + for (size_t i = 0; i < n_excluded; i++) { if (handle->excluded_servers[i] == server) { @@ -1032,33 +855,18 @@ static void * startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params) { bool error = false; - MYSQL_MONITOR *handle = (MYSQL_MONITOR*) monitor->handle; + MariaDBMonitor *handle = (MariaDBMonitor*) monitor->handle; if (handle) { handle->shutdown = 0; handle->script.clear(); handle->replication_user.clear(); handle->replication_password.clear(); - MXS_FREE(handle->excluded_servers); - handle->excluded_servers = NULL; - handle->n_excluded = 0; + handle->excluded_servers.clear(); } else { - handle = new MYSQL_MONITOR; - HASHTABLE *server_info = hashtable_alloc(MAX_NUM_SLAVES, - hashtable_item_strhash, hashtable_item_strcmp); - - if (server_info == NULL) - { - delete handle; - hashtable_free(server_info); - return NULL; - } - - hashtable_memory_fns(server_info, hashtable_item_strdup, info_copy_func, - hashtable_item_free, info_free_func); - handle->server_info = server_info; + handle = new MariaDBMonitor; handle->shutdown = 0; handle->id = config_get_global_options()->id; handle->warn_set_standalone_master = true; @@ -1087,13 +895,14 @@ startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params) handle->auto_rejoin = config_get_bool(params, CN_AUTO_REJOIN); handle->verify_master_failure = config_get_bool(params, CN_VERIFY_MASTER_FAILURE); handle->master_failure_timeout = config_get_integer(params, CN_MASTER_FAILURE_TIMEOUT); - handle->excluded_servers = NULL; - handle->n_excluded = mon_config_get_servers(params, CN_NO_PROMOTE_SERVERS, monitor, - &handle->excluded_servers); - if (handle->n_excluded < 0) + + MXS_MONITORED_SERVER** excluded_array = NULL; + int n_excluded = mon_config_get_servers(params, CN_NO_PROMOTE_SERVERS, monitor, &excluded_array); + for (int i = 0; i < n_excluded; i++) { - error = true; + handle->excluded_servers.push_back(excluded_array[i]); } + MXS_FREE(excluded_array); if (!set_replication_credentials(handle, params)) { @@ -1107,15 +916,10 @@ startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params) error = true; } - if (!init_server_info(handle, monitor->monitored_servers)) - { - error = true; - } + init_server_info(handle); if (error) { - hashtable_free(handle->server_info); - MXS_FREE(handle->excluded_servers); delete handle; handle = NULL; } @@ -1126,7 +930,6 @@ startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params) if (thread_start(&handle->thread, monitorMain, handle, 0) == NULL) { MXS_ERROR("Failed to start monitor thread for monitor '%s'.", monitor->name); - hashtable_free(handle->server_info); delete handle; handle = NULL; } @@ -1143,7 +946,7 @@ startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params) static void stopMonitor(MXS_MONITOR *mon) { - MYSQL_MONITOR *handle = (MYSQL_MONITOR *) mon->handle; + MariaDBMonitor *handle = (MariaDBMonitor *) mon->handle; handle->shutdown = 1; thread_wait(handle->thread); @@ -1164,7 +967,7 @@ static bool stop_monitor(MXS_MONITOR* mon) bool actually_stopped = false; - MYSQL_MONITOR *handle = static_cast(mon->handle); + MariaDBMonitor *handle = static_cast(mon->handle); if (handle->status == MXS_MONITOR_RUNNING) { @@ -1175,9 +978,10 @@ static bool stop_monitor(MXS_MONITOR* mon) return actually_stopped; } -static string monitored_servers_to_string(MXS_MONITORED_SERVER** array, size_t array_size) +static string monitored_servers_to_string(const ServerVector& array) { string rval; + size_t array_size = array.size(); if (array_size > 0) { const char* separator = ""; @@ -1198,7 +1002,7 @@ static string monitored_servers_to_string(MXS_MONITORED_SERVER** array, size_t a */ static void diagnostics(DCB *dcb, const MXS_MONITOR *mon) { - const MYSQL_MONITOR *handle = (const MYSQL_MONITOR *)mon->handle; + const MariaDBMonitor *handle = (const MariaDBMonitor *)mon->handle; dcb_printf(dcb, "Automatic failover: %s\n", handle->auto_failover ? "Enabled" : "Disabled"); dcb_printf(dcb, "Failcount: %d\n", handle->failcount); @@ -1209,17 +1013,16 @@ static void diagnostics(DCB *dcb, const MXS_MONITOR *mon) dcb_printf(dcb, "Detect replication lag: %s\n", (handle->replicationHeartbeat) ? "Enabled" : "Disabled"); dcb_printf(dcb, "Detect stale master: %s\n", (handle->detectStaleMaster == 1) ? "Enabled" : "Disabled"); - if (handle->n_excluded > 0) + if (handle->excluded_servers.size() > 0) { dcb_printf(dcb, "Non-promotable servers (failover): "); - dcb_printf(dcb, "%s\n", - monitored_servers_to_string(handle->excluded_servers, handle->n_excluded).c_str()); + dcb_printf(dcb, "%s\n", monitored_servers_to_string(handle->excluded_servers).c_str()); } dcb_printf(dcb, "\nServer information:\n-------------------\n\n"); for (MXS_MONITORED_SERVER *db = mon->monitored_servers; db; db = db->next) { - MySqlServerInfo *serv_info = get_server_info(handle, db); + const MySqlServerInfo* serv_info = get_server_info(handle, db); dcb_printf(dcb, "Server: %s\n", db->server->unique_name); dcb_printf(dcb, "Server ID: %" PRId64 "\n", serv_info->server_id); dcb_printf(dcb, "Read only: %s\n", serv_info->read_only ? "YES" : "NO"); @@ -1265,7 +1068,7 @@ static json_t* diagnostics_json(const MXS_MONITOR *mon) { json_t* rval = json_object(); - const MYSQL_MONITOR *handle = (const MYSQL_MONITOR *)mon->handle; + const MariaDBMonitor *handle = (const MariaDBMonitor *)mon->handle; json_object_set_new(rval, "monitor_id", json_integer(handle->id)); json_object_set_new(rval, "detect_stale_master", json_boolean(handle->detectStaleMaster)); json_object_set_new(rval, "detect_stale_slave", json_boolean(handle->detectStaleSlave)); @@ -1284,9 +1087,9 @@ static json_t* diagnostics_json(const MXS_MONITOR *mon) { json_object_set_new(rval, "script", json_string(handle->script.c_str())); } - if (handle->n_excluded > 0) + if (handle->excluded_servers.size() > 0) { - string list = monitored_servers_to_string(handle->excluded_servers, handle->n_excluded); + string list = monitored_servers_to_string(handle->excluded_servers); json_object_set_new(rval, CN_NO_PROMOTE_SERVERS, json_string(list.c_str())); } if (mon->monitored_servers) @@ -1296,7 +1099,7 @@ static json_t* diagnostics_json(const MXS_MONITOR *mon) for (MXS_MONITORED_SERVER *db = mon->monitored_servers; db; db = db->next) { json_t* srv = json_object(); - MySqlServerInfo *serv_info = get_server_info(handle, db); + const MySqlServerInfo* serv_info = get_server_info(handle, db); json_object_set_new(srv, "name", json_string(db->server->unique_name)); json_object_set_new(srv, "server_id", json_integer(serv_info->server_id)); json_object_set_new(srv, "master_id", json_integer(serv_info->slave_status.master_server_id)); @@ -1348,7 +1151,7 @@ static enum mysql_server_version get_server_version(MXS_MONITORED_SERVER* db) return MYSQL_SERVER_VERSION_51; } -static bool do_show_slave_status(MYSQL_MONITOR* mon, +static bool do_show_slave_status(MariaDBMonitor* mon, MySqlServerInfo* serv_info, MXS_MONITORED_SERVER* database) { @@ -1510,7 +1313,7 @@ static bool do_show_slave_status(MYSQL_MONITOR* mon, * @param handle Cluster monitor * @return True, if a slave has an event more recent than master_failure_timeout. */ -static bool slave_receiving_events(MYSQL_MONITOR* handle) +static bool slave_receiving_events(MariaDBMonitor* handle) { ss_dassert(handle->master); bool received_event = false; @@ -1534,7 +1337,7 @@ static bool slave_receiving_events(MYSQL_MONITOR* handle) return received_event; } -static inline void monitor_mysql_db(MYSQL_MONITOR* mon, +static inline void monitor_mysql_db(MariaDBMonitor* mon, MXS_MONITORED_SERVER* database, MySqlServerInfo *serv_info) { @@ -1569,7 +1372,7 @@ static MXS_MONITORED_SERVER *build_mysql51_replication_tree(MXS_MONITOR *mon) MXS_MONITORED_SERVER* database = mon->monitored_servers; MXS_MONITORED_SERVER *ptr, *rval = NULL; int i; - MYSQL_MONITOR *handle = static_cast(mon->handle); + MariaDBMonitor *handle = static_cast(mon->handle); while (database) { @@ -1675,7 +1478,7 @@ static MXS_MONITORED_SERVER *build_mysql51_replication_tree(MXS_MONITOR *mon) static void monitorDatabase(MXS_MONITOR *mon, MXS_MONITORED_SERVER *database) { - MYSQL_MONITOR* handle = static_cast(mon->handle); + MariaDBMonitor* handle = static_cast(mon->handle); /* Don't probe servers in maintenance mode */ if (SERVER_IN_MAINT(database->server)) @@ -1903,7 +1706,7 @@ static void visit_node(struct graph_node *node, struct graph_node **stack, * member. Nodes in a group get a positive group ID where the nodes not in a * group get a group ID of 0. */ -void find_graph_cycles(MYSQL_MONITOR *handle, MXS_MONITORED_SERVER *database, int nservers) +void find_graph_cycles(MariaDBMonitor *handle, MXS_MONITORED_SERVER *database, int nservers) { struct graph_node graph[nservers]; struct graph_node *stack[nservers]; @@ -2009,7 +1812,7 @@ void find_graph_cycles(MYSQL_MONITOR *handle, MXS_MONITORED_SERVER *database, in * * @return True if standalone master should be used */ -bool standalone_master_required(MYSQL_MONITOR *handle, MXS_MONITORED_SERVER *db) +bool standalone_master_required(MariaDBMonitor *handle, MXS_MONITORED_SERVER *db) { int candidates = 0; @@ -2046,7 +1849,7 @@ bool standalone_master_required(MYSQL_MONITOR *handle, MXS_MONITORED_SERVER *db) * @param handle Monitor instance * @param db Monitor servers */ -bool set_standalone_master(MYSQL_MONITOR *handle, MXS_MONITORED_SERVER *db) +bool set_standalone_master(MariaDBMonitor *handle, MXS_MONITORED_SERVER *db) { bool rval = false; @@ -2080,7 +1883,7 @@ bool set_standalone_master(MYSQL_MONITOR *handle, MXS_MONITORED_SERVER *db) return rval; } -bool failover_not_possible(MYSQL_MONITOR* handle) +bool failover_not_possible(MariaDBMonitor* handle) { bool rval = false; @@ -2107,7 +1910,7 @@ bool failover_not_possible(MYSQL_MONITOR* handle) static void monitorMain(void *arg) { - MYSQL_MONITOR *handle = (MYSQL_MONITOR *) arg; + MariaDBMonitor *handle = (MariaDBMonitor *) arg; MXS_MONITOR* mon = handle->monitor; MXS_MONITORED_SERVER *ptr; bool replication_heartbeat; @@ -2677,7 +2480,7 @@ static int get_row_count(MXS_MONITORED_SERVER *database, const char* query) * @param handle The monitor handle * @param database The number database server */ -static void set_master_heartbeat(MYSQL_MONITOR *handle, MXS_MONITORED_SERVER *database) +static void set_master_heartbeat(MariaDBMonitor *handle, MXS_MONITORED_SERVER *database) { unsigned long id = handle->id; time_t heartbeat; @@ -2798,7 +2601,7 @@ static void set_master_heartbeat(MYSQL_MONITOR *handle, MXS_MONITORED_SERVER *da */ static void set_slave_heartbeat(MXS_MONITOR* mon, MXS_MONITORED_SERVER *database) { - MYSQL_MONITOR *handle = (MYSQL_MONITOR*) mon->handle; + MariaDBMonitor *handle = (MariaDBMonitor*) mon->handle; unsigned long id = handle->id; time_t heartbeat; char select_heartbeat_query[256] = ""; @@ -2908,7 +2711,7 @@ static void set_slave_heartbeat(MXS_MONITOR* mon, MXS_MONITORED_SERVER *database static MXS_MONITORED_SERVER *get_replication_tree(MXS_MONITOR *mon, int num_servers) { - MYSQL_MONITOR* handle = (MYSQL_MONITOR*) mon->handle; + MariaDBMonitor* handle = (MariaDBMonitor*) mon->handle; MXS_MONITORED_SERVER *ptr; MXS_MONITORED_SERVER *backend; SERVER *current; @@ -3308,7 +3111,7 @@ void check_maxscale_schema_replication(MXS_MONITOR *monitor) * @todo Currently this only works with flat replication topologies and * needs to be moved inside mysqlmon as it is MariaDB specific code. */ -bool mon_process_failover(MYSQL_MONITOR* monitor, uint32_t failover_timeout, bool* cluster_modified_out) +bool mon_process_failover(MariaDBMonitor* monitor, uint32_t failover_timeout, bool* cluster_modified_out) { ss_dassert(*cluster_modified_out == false); bool rval = true; @@ -3389,7 +3192,7 @@ bool mon_process_failover(MYSQL_MONITOR* monitor, uint32_t failover_timeout, boo * @param server Slave to update * @return Slave server info. NULL on error, or if server is not a slave. */ -static MySqlServerInfo* update_slave_info(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* server) +static MySqlServerInfo* update_slave_info(MariaDBMonitor* mon, MXS_MONITORED_SERVER* server) { MySqlServerInfo* info = get_server_info(mon, server); if (info->slave_status.slave_sql_running && @@ -3453,7 +3256,7 @@ static bool check_replication_settings(const MXS_MONITORED_SERVER* server, MySql * @param err_out Json object for error printing. Can be NULL. * @return True, if given slave is a valid promotion candidate. */ -bool switchover_check_preferred_master(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* preferred, json_t** err_out) +bool switchover_check_preferred_master(MariaDBMonitor* mon, MXS_MONITORED_SERVER* preferred, json_t** err_out) { ss_dassert(preferred); bool rval = true; @@ -3512,7 +3315,7 @@ bool is_candidate_better(const MySqlServerInfo* current_best_info, const MySqlSe * @param err_out json object for error printing. Can be NULL. * @return The found master, or NULL if not found */ -MXS_MONITORED_SERVER* select_new_master(MYSQL_MONITOR* mon, ServerVector* slaves_out, json_t** err_out) +MXS_MONITORED_SERVER* select_new_master(MariaDBMonitor* mon, ServerVector* slaves_out, json_t** err_out) { ss_dassert(slaves_out && slaves_out->size() == 0); /* Select a new master candidate. Selects the one with the latest event in relay log. @@ -3605,7 +3408,7 @@ MXS_MONITORED_SERVER* select_new_master(MYSQL_MONITOR* mon, ServerVector* slaves * @param err_out Json error output * @return True if relay log was processed within time limit, or false if time ran out or an error occurred. */ -bool failover_wait_relay_log(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* new_master, int seconds_remaining, +bool failover_wait_relay_log(MariaDBMonitor* mon, MXS_MONITORED_SERVER* new_master, int seconds_remaining, json_t** err_out) { MySqlServerInfo* master_info = get_server_info(mon, new_master); @@ -3658,7 +3461,7 @@ bool failover_wait_relay_log(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* new_maste return rval; } -bool start_external_replication(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* new_master, json_t** err_out) +bool start_external_replication(MariaDBMonitor* mon, MXS_MONITORED_SERVER* new_master, json_t** err_out) { bool rval = false; string change_cmd = generate_change_master_cmd(mon, mon->external_master_host, mon->external_master_port); @@ -3685,7 +3488,7 @@ bool start_external_replication(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* new_ma * @param err_out json object for error printing. Can be NULL. * @return True if successful */ -bool promote_new_master(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* new_master, json_t** err_out) +bool promote_new_master(MariaDBMonitor* mon, MXS_MONITORED_SERVER* new_master, json_t** err_out) { bool success = false; MXS_NOTICE("Promoting server '%s' to master.", new_master->server->unique_name); @@ -3726,7 +3529,7 @@ bool promote_new_master(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* new_master, js * @param master_port Master port * @return Generated query */ -string generate_change_master_cmd(MYSQL_MONITOR* mon, const string& master_host, int master_port) +string generate_change_master_cmd(MariaDBMonitor* mon, const string& master_host, int master_port) { std::stringstream change_cmd; change_cmd << "CHANGE MASTER TO MASTER_HOST = '" << master_host << "', "; @@ -3780,7 +3583,7 @@ bool redirect_one_slave(MXS_MONITORED_SERVER* slave, const char* change_cmd) * @param redirected_slaves A vector where to insert successfully redirected slaves. Can be NULL. * @return The number of slaves successfully redirected. */ -int redirect_slaves(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* new_master, const ServerVector& slaves, +int redirect_slaves(MariaDBMonitor* mon, MXS_MONITORED_SERVER* new_master, const ServerVector& slaves, ServerVector* redirected_slaves = NULL) { MXS_NOTICE("Redirecting slaves to new master."); @@ -3838,7 +3641,7 @@ void print_redirect_errors(MXS_MONITORED_SERVER* first_server, const ServerVecto * @param err_out Json output * @return True if successful */ -static bool do_failover(MYSQL_MONITOR* mon, json_t** err_out) +static bool do_failover(MariaDBMonitor* mon, json_t** err_out) { // Topology has already been tested to be simple. if (mon->master_gtid_domain < 0) @@ -3993,7 +3796,7 @@ static bool update_replication_settings(MXS_MONITORED_SERVER *database, MySqlSer * @param info Server info structure for saving result * @return True if successful */ -static bool update_gtids(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER *database, MySqlServerInfo* info) +static bool update_gtids(MariaDBMonitor* mon, MXS_MONITORED_SERVER *database, MySqlServerInfo* info) { StringVector row; const char query[] = "SELECT @@gtid_current_pos, @@gtid_binlog_pos;"; @@ -4023,7 +3826,7 @@ static bool update_gtids(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER *database, MyS * @param err_out json object for error printing. Can be NULL. * @return True if successful. */ -static bool switchover_demote_master(MYSQL_MONITOR* mon, +static bool switchover_demote_master(MariaDBMonitor* mon, MXS_MONITORED_SERVER* current_master, MySqlServerInfo* info, json_t** err_out) @@ -4226,7 +4029,7 @@ static bool switchover_wait_slaves_catchup(const ServerVector& slaves, const Gti * @return True if commands were accepted. This does not guarantee that replication proceeds * successfully. */ -static bool switchover_start_slave(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* old_master, SERVER* new_master) +static bool switchover_start_slave(MariaDBMonitor* mon, MXS_MONITORED_SERVER* old_master, SERVER* new_master) { bool rval = false; std::string change_cmd = generate_change_master_cmd(mon, new_master->name, new_master->port); @@ -4277,7 +4080,7 @@ static string get_connection_errors(const ServerVector& servers) * @param seconds_remaining How long can we wait * @return True, if at least one slave got the new event within the time limit */ -static bool wait_cluster_stabilization(MYSQL_MONITOR* mon, +static bool wait_cluster_stabilization(MariaDBMonitor* mon, MXS_MONITORED_SERVER* new_master, const ServerVector& slaves, int seconds_remaining) @@ -4371,7 +4174,7 @@ static bool wait_cluster_stabilization(MYSQL_MONITOR* mon, * @return True if successful. If false, the cluster can be in various situations depending on which step * failed. In practice, manual intervention is usually required on failure. */ -static bool do_switchover(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* current_master, +static bool do_switchover(MariaDBMonitor* mon, MXS_MONITORED_SERVER* current_master, MXS_MONITORED_SERVER* new_master, json_t** err_out) { MXS_MONITORED_SERVER* demotion_target = current_master ? current_master : mon->master; @@ -4568,7 +4371,7 @@ static void read_server_variables(MXS_MONITORED_SERVER* database, MySqlServerInf * @param master_info Master info * @return True if slave can replicate from master */ -static bool can_replicate_from(MYSQL_MONITOR* mon, +static bool can_replicate_from(MariaDBMonitor* mon, MXS_MONITORED_SERVER* slave, MySqlServerInfo* slave_info, MXS_MONITORED_SERVER* master, MySqlServerInfo* master_info) { @@ -4599,7 +4402,7 @@ static bool can_replicate_from(MYSQL_MONITOR* mon, * @param master_info Master server info * @return True, if server is a rejoin suspect. */ -static bool server_is_rejoin_suspect(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* server, +static bool server_is_rejoin_suspect(MariaDBMonitor* mon, MXS_MONITORED_SERVER* server, MySqlServerInfo* master_info) { bool is_suspect = false; @@ -4643,7 +4446,7 @@ static bool server_is_rejoin_suspect(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* s * @return False, if there were possible rejoinable servers but communications error to master server * prevented final checks. */ -static bool get_joinable_servers(MYSQL_MONITOR* mon, ServerVector* output) +static bool get_joinable_servers(MariaDBMonitor* mon, ServerVector* output) { ss_dassert(output); MXS_MONITORED_SERVER* master = mon->master; @@ -4694,7 +4497,7 @@ static bool get_joinable_servers(MYSQL_MONITOR* mon, ServerVector* output) * @param joinable_servers Which servers to rejoin * @return The number of servers successfully rejoined */ -static uint32_t do_rejoin(MYSQL_MONITOR* mon, const ServerVector& joinable_servers) +static uint32_t do_rejoin(MariaDBMonitor* mon, const ServerVector& joinable_servers) { SERVER* master = mon->master->server; uint32_t servers_joined = 0; @@ -4764,7 +4567,7 @@ static bool join_cluster(MXS_MONITORED_SERVER* server, const char* change_cmd) * @param mon Cluster monitor * @param setting_name Setting to disable */ -static void disable_setting(MYSQL_MONITOR* mon, const char* setting) +static void disable_setting(MariaDBMonitor* mon, const char* setting) { MXS_CONFIG_PARAMETER p = {}; p.name = const_cast(setting); @@ -4778,7 +4581,7 @@ static void disable_setting(MYSQL_MONITOR* mon, const char* setting) * @param mon Cluster monitor * @return True, if cluster can be joined */ -static bool cluster_can_be_joined(MYSQL_MONITOR* mon) +static bool cluster_can_be_joined(MariaDBMonitor* mon) { return (mon->master != NULL && SERVER_IS_MASTER(mon->master->server) && mon->master_gtid_domain >= 0); } diff --git a/server/modules/monitor/mariadbmon/mariadbmon.hh b/server/modules/monitor/mariadbmon/mariadbmon.hh index 4a4bca451..18cf3cc60 100644 --- a/server/modules/monitor/mariadbmon/mariadbmon.hh +++ b/server/modules/monitor/mariadbmon/mariadbmon.hh @@ -1,6 +1,5 @@ #pragma once -#ifndef _MARIADBMON_H -#define _MARIADBMON_H + /* * Copyright (c) 2016 MariaDB Corporation Ab * @@ -15,29 +14,41 @@ */ /** - * @file mysqlmon.hh - The MySQL monitor + * @file mariadbmon.hh - The MySQL monitor */ #include #include +#include +#include -#include -#include -#include #include #include -using std::string; +#include "utilities.hh" + +extern const int PORT_UNKNOWN; +extern const int64_t SERVER_ID_UNKNOWN; + +typedef std::tr1::unordered_map ServerInfoMap; +typedef std::vector ServerVector; + // MySQL Monitor module instance -class MYSQL_MONITOR +class MariaDBMonitor { +private: + MariaDBMonitor(const MariaDBMonitor&); + MariaDBMonitor& operator = (const MariaDBMonitor&); public: + MariaDBMonitor(); + ~MariaDBMonitor(); + MXS_MONITOR* monitor; /**< Generic monitor object */ THREAD thread; /**< Monitor thread */ int shutdown; /**< Flag to shutdown the monitor thread */ int status; /**< Monitor status */ MXS_MONITORED_SERVER *master; /**< Master server for MySQL Master/Slave replication */ - HASHTABLE *server_info; /**< Contains server specific information */ + ServerInfoMap server_info; /**< Contains server specific information */ bool warn_set_standalone_master; /**< Log a warning when setting standalone master */ unsigned long id; /**< Monitor ID */ @@ -67,13 +78,10 @@ public: bool auto_failover; /**< If automatic master failover is enabled */ bool auto_rejoin; /**< Attempt to start slave replication on standalone servers or servers * replicating from the wrong master automatically. */ - MXS_MONITORED_SERVER** excluded_servers; /**< Servers banned for master promotion during auto-failover. */ - int n_excluded; /**< Number of excluded servers */ + ServerVector excluded_servers; /**< Servers banned for master promotion during auto-failover. */ // Other settings string script; /**< Script to call when state changes occur on servers */ uint64_t events; /**< enabled events */ bool allow_cluster_recovery; /**< Allow failed servers to rejoin the cluster */ }; - -#endif diff --git a/server/modules/monitor/mariadbmon/utilities.cc b/server/modules/monitor/mariadbmon/utilities.cc new file mode 100644 index 000000000..7fc22dcd3 --- /dev/null +++ b/server/modules/monitor/mariadbmon/utilities.cc @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2018 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl11. + * + * Change Date: 2020-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#include "utilities.hh" + +#include +#include +#include +#include +#include + +#include "mariadbmon.hh" + +Gtid::Gtid() + : domain(0) + , server_id(SERVER_ID_UNKNOWN) + , sequence(0) +{} + +Gtid::Gtid(const char* str, int64_t search_domain) + : domain(0) + , server_id(SERVER_ID_UNKNOWN) + , sequence(0) +{ + // Autoselect only allowed with one triplet + ss_dassert(search_domain >= 0 || strchr(str, ',') == NULL); + parse_triplet(str); + if (search_domain >= 0 && domain != search_domain) + { + // Search for the correct triplet. + bool found = false; + for (const char* next_triplet = strchr(str, ','); + next_triplet != NULL && !found; + next_triplet = strchr(next_triplet, ',')) + { + parse_triplet(++next_triplet); + if (domain == search_domain) + { + found = true; + } + } + ss_dassert(found); + } +} + +bool Gtid::operator == (const Gtid& rhs) const +{ + return domain == rhs.domain && + server_id != SERVER_ID_UNKNOWN && server_id == rhs.server_id && + sequence == rhs.sequence; +} + +string Gtid::to_string() const +{ + std::stringstream ss; + if (server_id != SERVER_ID_UNKNOWN) + { + ss << domain << "-" << server_id << "-" << sequence; + } + return ss.str(); +} + +void Gtid::parse_triplet(const char* str) +{ + ss_debug(int rv = ) sscanf(str, "%" PRIu32 "-%" PRId64 "-%" PRIu64, &domain, &server_id, &sequence); + ss_dassert(rv == 3); +} + +SlaveStatusInfo::SlaveStatusInfo() + : master_server_id(SERVER_ID_UNKNOWN) + , master_port(0) + , slave_io_running(false) + , slave_sql_running(false) + , read_master_log_pos(0) +{} + +MySqlServerInfo::MySqlServerInfo() + : server_id(SERVER_ID_UNKNOWN) + , group(0) + , read_only(false) + , slave_configured(false) + , binlog_relay(false) + , n_slaves_configured(0) + , n_slaves_running(0) + , slave_heartbeats(0) + , heartbeat_period(0) + , latest_event(0) + , gtid_domain_id(-1) + , version(MYSQL_SERVER_VERSION_51) +{} + +int64_t MySqlServerInfo::relay_log_events() +{ + if (slave_status.gtid_io_pos.server_id != SERVER_ID_UNKNOWN && + gtid_current_pos.server_id != SERVER_ID_UNKNOWN && + slave_status.gtid_io_pos.domain == gtid_current_pos.domain && + slave_status.gtid_io_pos.sequence >= gtid_current_pos.sequence) + { + return slave_status.gtid_io_pos.sequence - gtid_current_pos.sequence; + } + return -1; +} diff --git a/server/modules/monitor/mariadbmon/utilities.hh b/server/modules/monitor/mariadbmon/utilities.hh new file mode 100644 index 000000000..0db5b5062 --- /dev/null +++ b/server/modules/monitor/mariadbmon/utilities.hh @@ -0,0 +1,127 @@ +#pragma once + +/* + * Copyright (c) 2018 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl11. + * + * Change Date: 2020-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#include +#include + +using std::string; + +enum mysql_server_version +{ + MYSQL_SERVER_VERSION_100, + MYSQL_SERVER_VERSION_55, + MYSQL_SERVER_VERSION_51 +}; + +class Gtid +{ +public: + uint32_t domain; + int64_t server_id; // Is actually 32bit unsigned. 0 is only used by server versions <= 10.1 + uint64_t sequence; + Gtid(); + + /** + * Parse a Gtid-triplet from a string. In case of a multi-triplet value, only the triplet with + * the given domain is returned. + * + * @param str Gtid string + * @param search_domain The Gtid domain whose triplet should be returned. Negative domain stands for + * autoselect, which is only allowed when the string contains one triplet. + */ + Gtid(const char* str, int64_t search_domain = -1); + + bool operator == (const Gtid& rhs) const; + + string to_string() const; +private: + void parse_triplet(const char* str); +}; + +// Contains data returned by one row of SHOW ALL SLAVES STATUS +class SlaveStatusInfo +{ +public: + int64_t master_server_id; /**< The master's server_id value. Valid ids are 32bit unsigned. -1 is + * unread/error. */ + string master_host; /**< Master server host name. */ + int master_port; /**< Master server port. */ + bool slave_io_running; /**< Whether the slave I/O thread is running and connected. */ + bool slave_sql_running; /**< Whether or not the SQL thread is running. */ + string master_log_file; /**< Name of the master binary log file that the I/O thread is currently + * reading from. */ + uint64_t read_master_log_pos; /**< Position up to which the I/O thread has read in the current master + * binary log file. */ + Gtid gtid_io_pos; /**< Gtid I/O position of the slave thread. Only shows the triplet with + * the current master domain. */ + string last_error; /**< Last IO or SQL error encountered. */ + + SlaveStatusInfo(); +}; + +// This class groups some miscellaneous replication related settings together. +class ReplicationSettings +{ +public: + bool gtid_strict_mode; /**< Enable additional checks for replication */ + bool log_bin; /**< Is binary logging enabled */ + bool log_slave_updates; /**< Does the slave log replicated events to binlog */ + ReplicationSettings() + : gtid_strict_mode(false) + , log_bin(false) + , log_slave_updates(false) + {} +}; + +/** + * Monitor specific information about a server + * + * Note: These are initialized in @c init_server_info + */ +class MySqlServerInfo +{ +public: + int64_t server_id; /**< Value of @@server_id. Valid values are 32bit unsigned. */ + int group; /**< Multi-master group where this server belongs, + * 0 for servers not in groups */ + bool read_only; /**< Value of @@read_only */ + bool slave_configured; /**< Whether SHOW SLAVE STATUS returned rows */ + bool binlog_relay; /** Server is a Binlog Relay */ + int n_slaves_configured; /**< Number of configured slave connections*/ + int n_slaves_running; /**< Number of running slave connections */ + int slave_heartbeats; /**< Number of received heartbeats */ + double heartbeat_period; /**< The time interval between heartbeats */ + time_t latest_event; /**< Time when latest event was received from the master */ + int64_t gtid_domain_id; /**< The value of gtid_domain_id, the domain which is used for + * new non-replicated events. */ + Gtid gtid_current_pos; /**< Gtid of latest event. Only shows the triplet + * with the current master domain. */ + Gtid gtid_binlog_pos; /**< Gtid of latest event written to binlog. Only shows + * the triplet with the current master domain. */ + SlaveStatusInfo slave_status; /**< Data returned from SHOW SLAVE STATUS */ + ReplicationSettings rpl_settings; /**< Miscellaneous replication related settings */ + mysql_server_version version; /**< Server version, 10.X, 5.5 or 5.1 */ + + MySqlServerInfo(); + + /** + * Calculate how many events are left in the relay log. If gtid_current_pos is ahead of Gtid_IO_Pos, + * or a server_id is unknown, an error value is returned. + * + * @return Number of events in relay log according to latest queried info. A negative value signifies + * an error in the gtid-values. + */ + int64_t relay_log_events(); +}; From 03eb30fbc62500a69e0cd4b6d4831e144a4a87b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Thu, 22 Feb 2018 10:06:29 +0200 Subject: [PATCH 05/18] Check SHOW DATABASES privilege on startup MySQLAuth requires the SHOW DATABASES privilege to see all the databases so it should be checked that the current user has the permission. A missing permission will cause errors that are hard to resolve. --- .../modules/authenticator/MySQLAuth/dbusers.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/server/modules/authenticator/MySQLAuth/dbusers.c b/server/modules/authenticator/MySQLAuth/dbusers.c index 0310ff928..8829e5170 100644 --- a/server/modules/authenticator/MySQLAuth/dbusers.c +++ b/server/modules/authenticator/MySQLAuth/dbusers.c @@ -627,6 +627,25 @@ static bool check_server_permissions(SERVICE *service, SERVER* server, } } + // Check whether the current user has the SHOW DATABASES privilege + if (mxs_mysql_query(mysql, "SELECT show_db_priv FROM mysql.user " + "WHERE CONCAT(user, '@', host) = CURRENT_USER()") == 0) + { + MYSQL_RES* res = mysql_use_result(mysql); + if (res) + { + MYSQL_ROW row = mysql_fetch_row(res); + + if (row && strcasecmp(row[0], "Y") != 0) + { + MXS_WARNING("[%s] User '%s' is missing the SHOW DATABASES privilege.", + service->name, user); + } + + mysql_free_result(res); + } + } + mysql_close(mysql); return rval; From 8cdd23dda2add6486abb685834def94c72a09b6c Mon Sep 17 00:00:00 2001 From: Esa Korhonen Date: Wed, 21 Feb 2018 15:10:28 +0200 Subject: [PATCH 06/18] MariaDBMon: Move replication manipulation functions to a separate file Refactoring continues. This update moves some of the replication manipulation functions to a separate file and turns them into class methods. --- .../modules/monitor/mariadbmon/CMakeLists.txt | 2 +- .../mariadbmon/cluster_manipulation.cc | 191 +++++++++++++ .../modules/monitor/mariadbmon/mariadbmon.cc | 255 ++---------------- .../modules/monitor/mariadbmon/mariadbmon.hh | 49 +++- 4 files changed, 263 insertions(+), 234 deletions(-) create mode 100644 server/modules/monitor/mariadbmon/cluster_manipulation.cc diff --git a/server/modules/monitor/mariadbmon/CMakeLists.txt b/server/modules/monitor/mariadbmon/CMakeLists.txt index 9a208764e..36eb2a02f 100644 --- a/server/modules/monitor/mariadbmon/CMakeLists.txt +++ b/server/modules/monitor/mariadbmon/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(mariadbmon SHARED mariadbmon.cc utilities.cc) +add_library(mariadbmon SHARED mariadbmon.cc utilities.cc cluster_manipulation.cc) target_link_libraries(mariadbmon maxscale-common) add_dependencies(mariadbmon pcre2) set_target_properties(mariadbmon PROPERTIES VERSION "1.4.0") diff --git a/server/modules/monitor/mariadbmon/cluster_manipulation.cc b/server/modules/monitor/mariadbmon/cluster_manipulation.cc new file mode 100644 index 000000000..cad686bfa --- /dev/null +++ b/server/modules/monitor/mariadbmon/cluster_manipulation.cc @@ -0,0 +1,191 @@ +#include "mariadbmon.hh" + +#include +#include + +/* + * Copyright (c) 2018 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl11. + * + * Change Date: 2020-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +/** + * Generate a CHANGE MASTER TO-query. + * + * @param master_host Master hostname/address + * @param master_port Master port + * @return Generated query + */ +string MariaDBMonitor::generate_change_master_cmd(const string& master_host, int master_port) +{ + std::stringstream change_cmd; + change_cmd << "CHANGE MASTER TO MASTER_HOST = '" << master_host << "', "; + change_cmd << "MASTER_PORT = " << master_port << ", "; + change_cmd << "MASTER_USE_GTID = current_pos, "; + change_cmd << "MASTER_USER = '" << m_replication_user << "', "; + const char MASTER_PW[] = "MASTER_PASSWORD = '"; + const char END[] = "';"; +#if defined(SS_DEBUG) + std::stringstream change_cmd_nopw; + change_cmd_nopw << change_cmd.str(); + change_cmd_nopw << MASTER_PW << "******" << END;; + MXS_DEBUG("Change master command is '%s'.", change_cmd_nopw.str().c_str()); +#endif + change_cmd << MASTER_PW << m_replication_password << END; + return change_cmd.str(); +} + +/** + * Redirects slaves to replicate from another master server. + * + * @param slaves An array of slaves + * @param new_master The replication master + * @param redirected_slaves A vector where to insert successfully redirected slaves. + * @return The number of slaves successfully redirected. + */ +int MariaDBMonitor::redirect_slaves(MXS_MONITORED_SERVER* new_master, const ServerVector& slaves, + ServerVector* redirected_slaves) +{ + ss_dassert(redirected_slaves != NULL); + MXS_NOTICE("Redirecting slaves to new master."); + string change_cmd = generate_change_master_cmd(new_master->server->name, new_master->server->port); + int successes = 0; + for (ServerVector::const_iterator iter = slaves.begin(); iter != slaves.end(); iter++) + { + if (redirect_one_slave(*iter, change_cmd.c_str())) + { + successes++; + redirected_slaves->push_back(*iter); + } + } + return successes; +} + +/** + * Set the new master to replicate from the cluster external master. + * + * @param new_master The server being promoted + * @param err_out Error output + * @return True if new master accepted commands + */ +bool MariaDBMonitor::start_external_replication(MXS_MONITORED_SERVER* new_master, json_t** err_out) +{ + bool rval = false; + string change_cmd = generate_change_master_cmd(external_master_host, external_master_port); + if (mxs_mysql_query(new_master->con, change_cmd.c_str()) == 0 && + mxs_mysql_query(new_master->con, "START SLAVE;") == 0) + { + MXS_NOTICE("New master starting replication from external master %s:%d.", + external_master_host.c_str(), external_master_port); + rval = true; + } + else + { + PRINT_MXS_JSON_ERROR(err_out, "Could not start replication from external master: '%s'.", + mysql_error(new_master->con)); + } + return rval; +} + +/** + * Starts a new slave connection on a server. Should be used on a demoted master server. + * + * @param old_master The server which will start replication + * @param new_master Replication target + * @return True if commands were accepted. This does not guarantee that replication proceeds + * successfully. + */ +bool MariaDBMonitor::switchover_start_slave(MXS_MONITORED_SERVER* old_master, SERVER* new_master) +{ + bool rval = false; + string change_cmd = generate_change_master_cmd(new_master->name, new_master->port); + if (mxs_mysql_query(old_master->con, change_cmd.c_str()) == 0 && + mxs_mysql_query(old_master->con, "START SLAVE;") == 0) + { + MXS_NOTICE("Old master '%s' starting replication from '%s'.", + old_master->server->unique_name, new_master->unique_name); + rval = true; + } + else + { + MXS_ERROR("Old master '%s' could not start replication: '%s'.", + old_master->server->unique_name, mysql_error(old_master->con)); + } + return rval; +} + +/** + * Redirect one slave server to another master + * + * @param slave Server to redirect + * @param change_cmd Change master command, usually generated by generate_change_master_cmd() + * @return True if slave accepted all commands + */ +bool MariaDBMonitor::redirect_one_slave(MXS_MONITORED_SERVER* slave, const char* change_cmd) +{ + bool rval = false; + if (mxs_mysql_query(slave->con, "STOP SLAVE;") == 0 && + mxs_mysql_query(slave->con, "RESET SLAVE;") == 0 && // To erase any old I/O or SQL errors + mxs_mysql_query(slave->con, change_cmd) == 0 && + mxs_mysql_query(slave->con, "START SLAVE;") == 0) + { + rval = true; + MXS_NOTICE("Slave '%s' redirected to new master.", slave->server->unique_name); + } + else + { + MXS_WARNING("Slave '%s' redirection failed: '%s'.", slave->server->unique_name, + mysql_error(slave->con)); + } + return rval; +} + +uint32_t MariaDBMonitor::do_rejoin(const ServerVector& joinable_servers) +{ + SERVER* master = this->master->server; + uint32_t servers_joined = 0; + if (!joinable_servers.empty()) + { + string change_cmd = generate_change_master_cmd(master->name, master->port); + for (ServerVector::const_iterator iter = joinable_servers.begin(); + iter != joinable_servers.end(); + iter++) + { + MXS_MONITORED_SERVER* joinable = *iter; + const char* name = joinable->server->unique_name; + const char* master_name = master->unique_name; + MySqlServerInfo* redir_info = get_server_info(this, joinable); + + bool op_success; + if (redir_info->n_slaves_configured == 0) + { + MXS_NOTICE("Directing standalone server '%s' to replicate from '%s'.", name, master_name); + op_success = join_cluster(joinable, change_cmd.c_str()); + } + else + { + MXS_NOTICE("Server '%s' is replicating from a server other than '%s', " + "redirecting it to '%s'.", name, master_name, master_name); + op_success = redirect_one_slave(joinable, change_cmd.c_str()); + } + + if (op_success) + { + servers_joined++; + } + } + } + return servers_joined; +} + +bool MariaDBMonitor::cluster_can_be_joined() +{ + return (master != NULL && SERVER_IS_MASTER(master->server) && master_gtid_domain >= 0); +} \ No newline at end of file diff --git a/server/modules/monitor/mariadbmon/mariadbmon.cc b/server/modules/monitor/mariadbmon/mariadbmon.cc index 1db72d999..668be379b 100644 --- a/server/modules/monitor/mariadbmon/mariadbmon.cc +++ b/server/modules/monitor/mariadbmon/mariadbmon.cc @@ -20,14 +20,11 @@ #include "mariadbmon.hh" #include #include -#include #include -#include #include #include #include #include -#include #include #include #include @@ -59,16 +56,6 @@ #define SLAVE_HOSTS_HOSTNAME 1 #define SLAVE_HOSTS_PORT 2 -/** Utility macro for printing both MXS_ERROR and json error */ -#define PRINT_MXS_JSON_ERROR(err_out, format, ...)\ - do {\ - MXS_ERROR(format, ##__VA_ARGS__);\ - if (err_out)\ - {\ - *err_out = mxs_json_error_append(*err_out, format, ##__VA_ARGS__);\ - }\ - } while (false) - using std::string; typedef std::vector StringVector; class MySqlServerInfo; @@ -100,8 +87,6 @@ static void set_slave_heartbeat(MXS_MONITOR *, MXS_MONITORED_SERVER *); static int add_slave_to_master(long *, int, long); static bool isMySQLEvent(mxs_monitor_event_t event); void check_maxscale_schema_replication(MXS_MONITOR *monitor); -static MySqlServerInfo* get_server_info(MariaDBMonitor* handle, const MXS_MONITORED_SERVER* db); -static const MySqlServerInfo* get_server_info(const MariaDBMonitor* handle, const MXS_MONITORED_SERVER* db); static bool mon_process_failover(MariaDBMonitor*, uint32_t, bool*); static bool do_failover(MariaDBMonitor* mon, json_t** output); static bool do_switchover(MariaDBMonitor* mon, MXS_MONITORED_SERVER* current_master, @@ -114,10 +99,8 @@ static void read_server_variables(MXS_MONITORED_SERVER* database, MySqlServerInf static bool server_is_rejoin_suspect(MariaDBMonitor* mon, MXS_MONITORED_SERVER* server, MySqlServerInfo* master_info); static bool get_joinable_servers(MariaDBMonitor* mon, ServerVector* output); -static uint32_t do_rejoin(MariaDBMonitor* mon, const ServerVector& servers); static bool join_cluster(MXS_MONITORED_SERVER* server, const char* change_cmd); static void disable_setting(MariaDBMonitor* mon, const char* setting); -static bool cluster_can_be_joined(MariaDBMonitor* mon); static bool can_replicate_from(MariaDBMonitor* mon, MXS_MONITORED_SERVER* slave, MySqlServerInfo* slave_info, MXS_MONITORED_SERVER* master, MySqlServerInfo* master_info); @@ -125,7 +108,6 @@ static bool wait_cluster_stabilization(MariaDBMonitor* mon, MXS_MONITORED_SERVER const ServerVector& slaves, int seconds_remaining); static string get_connection_errors(const ServerVector& servers); static int64_t scan_server_id(const char* id_string); -static string generate_change_master_cmd(MariaDBMonitor* mon, const string& master_host, int master_port); static bool report_version_err = true; static const char* hb_table_name = "maxscale_schema.replication_heartbeat"; @@ -549,7 +531,7 @@ bool mysql_rejoin(MXS_MONITOR* mon, SERVER* rejoin_server, json_t** output) bool rval = false; MariaDBMonitor *handle = static_cast(mon->handle); - if (cluster_can_be_joined(handle)) + if (handle->cluster_can_be_joined()) { MXS_MONITORED_SERVER* mon_server = mon_get_monitored_server(mon, rejoin_server); if (mon_server) @@ -564,7 +546,7 @@ bool mysql_rejoin(MXS_MONITOR* mon, SERVER* rejoin_server, json_t** output) { ServerVector joinable_server; joinable_server.push_back(mon_server); - if (do_rejoin(handle, joinable_server) == 1) + if (handle->do_rejoin(joinable_server) == 1) { rval = true; MXS_NOTICE("Rejoin performed."); @@ -785,36 +767,36 @@ void init_server_info(MariaDBMonitor *handle) } } -static MySqlServerInfo* get_server_info(MariaDBMonitor* handle, const MXS_MONITORED_SERVER* db) +MySqlServerInfo* get_server_info(MariaDBMonitor* handle, const MXS_MONITORED_SERVER* db) { ServerInfoMap::iterator iter = handle->server_info.find(db); ss_dassert(iter != handle->server_info.end()); return &iter->second; } -static const MySqlServerInfo* get_server_info(const MariaDBMonitor* handle, const MXS_MONITORED_SERVER* db) +const MySqlServerInfo* get_server_info(const MariaDBMonitor* handle, const MXS_MONITORED_SERVER* db) { return get_server_info(const_cast(handle), db); } -static bool set_replication_credentials(MariaDBMonitor *handle, const MXS_CONFIG_PARAMETER* params) +bool MariaDBMonitor::set_replication_credentials(const MXS_CONFIG_PARAMETER* params) { bool rval = false; - const char* repl_user = config_get_string(params, CN_REPLICATION_USER); - const char* repl_pw = config_get_string(params, CN_REPLICATION_PASSWORD); + string repl_user = config_get_string(params, CN_REPLICATION_USER); + string repl_pw = config_get_string(params, CN_REPLICATION_PASSWORD); - if (!*repl_user && !*repl_pw) + if (repl_user.empty() && repl_pw.empty()) { // No replication credentials defined, use monitor credentials - repl_user = handle->monitor->user; - repl_pw = handle->monitor->password; + repl_user = monitor->user; + repl_pw = monitor->password; } - if (*repl_user && *repl_pw) + if (!repl_user.empty() && !repl_pw.empty()) { - handle->replication_user = repl_user; - char* decrypted = decrypt_password(repl_pw); - handle->replication_password = decrypted; + m_replication_user = repl_user; + char* decrypted = decrypt_password(repl_pw.c_str()); + m_replication_password = decrypted; MXS_FREE(decrypted); rval = true; } @@ -860,8 +842,6 @@ startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params) { handle->shutdown = 0; handle->script.clear(); - handle->replication_user.clear(); - handle->replication_password.clear(); handle->excluded_servers.clear(); } else @@ -904,7 +884,7 @@ startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params) } MXS_FREE(excluded_array); - if (!set_replication_credentials(handle, params)) + if (!handle->set_replication_credentials(params)) { MXS_ERROR("Both '%s' and '%s' must be defined", CN_REPLICATION_USER, CN_REPLICATION_PASSWORD); error = true; @@ -2364,13 +2344,13 @@ monitorMain(void *arg) // Do not auto-join servers on this monitor loop if a failover (or any other cluster modification) // has been performed, as server states have not been updated yet. It will happen next iteration. if (!config_get_global_options()->passive && handle->auto_rejoin && - !failover_performed && cluster_can_be_joined(handle)) + !failover_performed && handle->cluster_can_be_joined()) { // Check if any servers should be autojoined to the cluster ServerVector joinable_servers; if (get_joinable_servers(handle, &joinable_servers)) { - uint32_t joins = do_rejoin(handle, joinable_servers); + uint32_t joins = handle->do_rejoin(joinable_servers); if (joins > 0) { MXS_NOTICE("%d server(s) redirected or rejoined the cluster.", joins); @@ -3461,25 +3441,6 @@ bool failover_wait_relay_log(MariaDBMonitor* mon, MXS_MONITORED_SERVER* new_mast return rval; } -bool start_external_replication(MariaDBMonitor* mon, MXS_MONITORED_SERVER* new_master, json_t** err_out) -{ - bool rval = false; - string change_cmd = generate_change_master_cmd(mon, mon->external_master_host, mon->external_master_port); - if (mxs_mysql_query(new_master->con, change_cmd.c_str()) == 0 && - mxs_mysql_query(new_master->con, "START SLAVE;") == 0) - { - MXS_NOTICE("New master starting replication from external master %s:%d.", - mon->external_master_host.c_str(), mon->external_master_port); - rval = true; - } - else - { - PRINT_MXS_JSON_ERROR(err_out, "Could not start replication from external master: '%s'.", - mysql_error(new_master->con)); - } - return rval; -} - /** * Prepares a server for the replication master role. * @@ -3514,96 +3475,13 @@ bool promote_new_master(MariaDBMonitor* mon, MXS_MONITORED_SERVER* new_master, j // If the previous master was a slave to an external master, start the equivalent slave connection on // the new master. Success of replication is not checked. else if (mon->external_master_port != PORT_UNKNOWN && - !start_external_replication(mon, new_master, err_out)) + !mon->start_external_replication(new_master, err_out)) { success = false; } return success; } -/** - * Generate a CHANGE MASTER TO-query. - * - * @param mon Cluster monitor, needed for username & password - * @param master_host Master hostname/address - * @param master_port Master port - * @return Generated query - */ -string generate_change_master_cmd(MariaDBMonitor* mon, const string& master_host, int master_port) -{ - std::stringstream change_cmd; - change_cmd << "CHANGE MASTER TO MASTER_HOST = '" << master_host << "', "; - change_cmd << "MASTER_PORT = " << master_port << ", "; - change_cmd << "MASTER_USE_GTID = current_pos, "; - change_cmd << "MASTER_USER = '" << mon->replication_user << "', "; - const char MASTER_PW[] = "MASTER_PASSWORD = '"; - const char END[] = "';"; -#if defined(SS_DEBUG) - std::stringstream change_cmd_nopw; - change_cmd_nopw << change_cmd.str(); - change_cmd_nopw << MASTER_PW << "******" << END;; - MXS_DEBUG("Change master command is '%s'.", change_cmd_nopw.str().c_str()); -#endif - change_cmd << MASTER_PW << mon->replication_password << END; - return change_cmd.str(); -} - -/** - * Redirect one slave server to another master - * - * @param slave Server to redirect - * @param change_cmd Change master command, usually generated by generate_change_master_cmd() - * @return True if slave accepted all commands - */ -bool redirect_one_slave(MXS_MONITORED_SERVER* slave, const char* change_cmd) -{ - bool rval = false; - if (mxs_mysql_query(slave->con, "STOP SLAVE;") == 0 && - mxs_mysql_query(slave->con, "RESET SLAVE;") == 0 && // To erase any old I/O or SQL errors - mxs_mysql_query(slave->con, change_cmd) == 0 && - mxs_mysql_query(slave->con, "START SLAVE;") == 0) - { - rval = true; - MXS_NOTICE("Slave '%s' redirected to new master.", slave->server->unique_name); - } - else - { - MXS_WARNING("Slave '%s' redirection failed: '%s'.", slave->server->unique_name, - mysql_error(slave->con)); - } - return rval; -} - -/** - * Redirects slaves to replicate from another master server. - * - * @param mon The monitor - * @param slaves An array of slaves - * @param new_master The replication master - * @param redirected_slaves A vector where to insert successfully redirected slaves. Can be NULL. - * @return The number of slaves successfully redirected. - */ -int redirect_slaves(MariaDBMonitor* mon, MXS_MONITORED_SERVER* new_master, const ServerVector& slaves, - ServerVector* redirected_slaves = NULL) -{ - MXS_NOTICE("Redirecting slaves to new master."); - std::string change_cmd = generate_change_master_cmd(mon, - new_master->server->name, new_master->server->port); - int successes = 0; - for (ServerVector::const_iterator iter = slaves.begin(); iter != slaves.end(); iter++) - { - if (redirect_one_slave(*iter, change_cmd.c_str())) - { - successes++; - if (redirected_slaves) - { - redirected_slaves->push_back(*iter); - } - } - } - return successes; -} - /** * Print a redirect error to logs. If err_out exists, generate a combined error message by querying all * the server parameters for connection errors and append these errors to err_out. @@ -3677,7 +3555,7 @@ static bool do_failover(MariaDBMonitor* mon, json_t** err_out) { // Step 4: Redirect slaves. ServerVector redirected_slaves; - int redirects = redirect_slaves(mon, new_master, redirectable_slaves, &redirected_slaves); + int redirects = mon->redirect_slaves(new_master, redirectable_slaves, &redirected_slaves); bool success = redirectable_slaves.empty() ? true : redirects > 0; if (success) { @@ -4020,34 +3898,6 @@ static bool switchover_wait_slaves_catchup(const ServerVector& slaves, const Gti return success; } -/** - * Starts a new slave connection on a server. Should be used on a demoted master server. - * - * @param mon Cluster monitor - * @param old_master The server which will start replication - * @param new_master Replication target - * @return True if commands were accepted. This does not guarantee that replication proceeds - * successfully. - */ -static bool switchover_start_slave(MariaDBMonitor* mon, MXS_MONITORED_SERVER* old_master, SERVER* new_master) -{ - bool rval = false; - std::string change_cmd = generate_change_master_cmd(mon, new_master->name, new_master->port); - if (mxs_mysql_query(old_master->con, change_cmd.c_str()) == 0 && - mxs_mysql_query(old_master->con, "START SLAVE;") == 0) - { - MXS_NOTICE("Old master '%s' starting replication from '%s'.", - old_master->server->unique_name, new_master->unique_name); - rval = true; - } - else - { - MXS_ERROR("Old master '%s' could not start replication: '%s'.", - old_master->server->unique_name, mysql_error(old_master->con)); - } - return rval; -} - /** * Get MariaDB connection error strings from all the given servers, form one string. * @@ -4256,13 +4106,13 @@ static bool do_switchover(MariaDBMonitor* mon, MXS_MONITORED_SERVER* current_mas catchup_and_promote_success = true; // Step 5: Redirect slaves and start replication on old master. ServerVector redirected_slaves; - bool start_ok = switchover_start_slave(mon, demotion_target, promotion_target->server); + bool start_ok = mon->switchover_start_slave(demotion_target, promotion_target->server); if (start_ok) { redirected_slaves.push_back(demotion_target); } - int redirects = redirect_slaves(mon, promotion_target, - redirectable_slaves, &redirected_slaves); + int redirects = mon->redirect_slaves(promotion_target, redirectable_slaves, + &redirected_slaves); bool success = redirectable_slaves.empty() ? start_ok : start_ok || redirects > 0; if (success) @@ -4314,7 +4164,7 @@ static bool do_switchover(MariaDBMonitor* mon, MXS_MONITORED_SERVER* current_mas // Try to reactivate external replication if any. if (mon->external_master_port != PORT_UNKNOWN) { - start_external_replication(mon, new_master, err_out); + mon->start_external_replication(new_master, err_out); } } } @@ -4489,52 +4339,6 @@ static bool get_joinable_servers(MariaDBMonitor* mon, ServerVector* output) return comm_ok; } -/** - * (Re)join given servers to the cluster. The servers in the array are assumed to be joinable. - * Usually the list is created by get_joinable_servers(). - * - * @param mon Cluster monitor - * @param joinable_servers Which servers to rejoin - * @return The number of servers successfully rejoined - */ -static uint32_t do_rejoin(MariaDBMonitor* mon, const ServerVector& joinable_servers) -{ - SERVER* master = mon->master->server; - uint32_t servers_joined = 0; - if (!joinable_servers.empty()) - { - string change_cmd = generate_change_master_cmd(mon, master->name, master->port); - for (ServerVector::const_iterator iter = joinable_servers.begin(); - iter != joinable_servers.end(); - iter++) - { - MXS_MONITORED_SERVER* joinable = *iter; - const char* name = joinable->server->unique_name; - const char* master_name = master->unique_name; - MySqlServerInfo* redir_info = get_server_info(mon, joinable); - - bool op_success; - if (redir_info->n_slaves_configured == 0) - { - MXS_NOTICE("Directing standalone server '%s' to replicate from '%s'.", name, master_name); - op_success = join_cluster(joinable, change_cmd.c_str()); - } - else - { - MXS_NOTICE("Server '%s' is replicating from a server other than '%s', " - "redirecting it to '%s'.", name, master_name, master_name); - op_success = redirect_one_slave(joinable, change_cmd.c_str()); - } - - if (op_success) - { - servers_joined++; - } - } - } - return servers_joined; -} - /** * Joins a standalone server to the cluster. * @@ -4542,7 +4346,7 @@ static uint32_t do_rejoin(MariaDBMonitor* mon, const ServerVector& joinable_serv * @param change_cmd Change master command * @return True if commands were accepted by server */ -static bool join_cluster(MXS_MONITORED_SERVER* server, const char* change_cmd) +bool MariaDBMonitor::join_cluster(MXS_MONITORED_SERVER* server, const char* change_cmd) { /* Server does not have slave connections. This operation can fail, or the resulting * replication may end up broken. */ @@ -4575,17 +4379,6 @@ static void disable_setting(MariaDBMonitor* mon, const char* setting) monitorAddParameters(mon->monitor, &p); } -/** - * Is the cluster a valid rejoin target - * - * @param mon Cluster monitor - * @return True, if cluster can be joined - */ -static bool cluster_can_be_joined(MariaDBMonitor* mon) -{ - return (mon->master != NULL && SERVER_IS_MASTER(mon->master->server) && mon->master_gtid_domain >= 0); -} - /** * Scan a server id from a string. * diff --git a/server/modules/monitor/mariadbmon/mariadbmon.hh b/server/modules/monitor/mariadbmon/mariadbmon.hh index 18cf3cc60..948fed363 100644 --- a/server/modules/monitor/mariadbmon/mariadbmon.hh +++ b/server/modules/monitor/mariadbmon/mariadbmon.hh @@ -22,17 +22,32 @@ #include #include +#include #include #include #include "utilities.hh" +/** Utility macro for printing both MXS_ERROR and json error */ +#define PRINT_MXS_JSON_ERROR(err_out, format, ...)\ + do {\ + MXS_ERROR(format, ##__VA_ARGS__);\ + if (err_out)\ + {\ + *err_out = mxs_json_error_append(*err_out, format, ##__VA_ARGS__);\ + }\ + } while (false) + extern const int PORT_UNKNOWN; extern const int64_t SERVER_ID_UNKNOWN; +class MariaDBMonitor; typedef std::tr1::unordered_map ServerInfoMap; typedef std::vector ServerVector; +MySqlServerInfo* get_server_info(MariaDBMonitor* handle, const MXS_MONITORED_SERVER* db); +const MySqlServerInfo* get_server_info(const MariaDBMonitor* handle, const MXS_MONITORED_SERVER* db); + // MySQL Monitor module instance class MariaDBMonitor { @@ -43,6 +58,22 @@ public: MariaDBMonitor(); ~MariaDBMonitor(); + /** + * (Re)join given servers to the cluster. The servers in the array are assumed to be joinable. + * Usually the list is created by get_joinable_servers(). + * + * @param joinable_servers Which servers to rejoin + * @return The number of servers successfully rejoined + */ + uint32_t do_rejoin(const ServerVector& joinable_servers); + + /** + * Check if the cluster is a valid rejoin target. + * + * @return True if master and gtid domain are known + */ + bool cluster_can_be_joined(); + MXS_MONITOR* monitor; /**< Generic monitor object */ THREAD thread; /**< Monitor thread */ int shutdown; /**< Flag to shutdown the monitor thread */ @@ -67,8 +98,6 @@ public: bool replicationHeartbeat; /**< Monitor flag for MySQL replication heartbeat */ // Failover, switchover and rejoin settings - string replication_user; /**< Replication user for CHANGE MASTER TO-commands */ - string replication_password; /**< Replication password for CHANGE MASTER TO-commands */ int failcount; /**< How many monitoring cycles master must be down before auto-failover * begins */ uint32_t failover_timeout; /**< Timeout in seconds for the master failover */ @@ -84,4 +113,20 @@ public: string script; /**< Script to call when state changes occur on servers */ uint64_t events; /**< enabled events */ bool allow_cluster_recovery; /**< Allow failed servers to rejoin the cluster */ + +private: + // Failover, switchover and rejoin settings + string m_replication_user; /**< Replication user for CHANGE MASTER TO-commands */ + string m_replication_password; /**< Replication password for CHANGE MASTER TO-commands */ + +public: + // Following methods should be private, change it once refactoring is done. + string generate_change_master_cmd(const string& master_host, int master_port); + int redirect_slaves(MXS_MONITORED_SERVER* new_master, const ServerVector& slaves, + ServerVector* redirected_slaves); + bool set_replication_credentials(const MXS_CONFIG_PARAMETER* params); + bool start_external_replication(MXS_MONITORED_SERVER* new_master, json_t** err_out); + bool switchover_start_slave(MXS_MONITORED_SERVER* old_master, SERVER* new_master); + bool redirect_one_slave(MXS_MONITORED_SERVER* slave, const char* change_cmd); + bool join_cluster(MXS_MONITORED_SERVER* server, const char* change_cmd); }; From e72173343421023cf95fbd2de2e5e8ab7181c348 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Sat, 24 Feb 2018 15:35:02 +0200 Subject: [PATCH 07/18] Revert "MariaDBMon: Move replication manipulation functions to a separate file" This reverts commit 8cdd23dda2add6486abb685834def94c72a09b6c. --- .../modules/monitor/mariadbmon/CMakeLists.txt | 2 +- .../mariadbmon/cluster_manipulation.cc | 191 ------------- .../modules/monitor/mariadbmon/mariadbmon.cc | 255 ++++++++++++++++-- .../modules/monitor/mariadbmon/mariadbmon.hh | 49 +--- 4 files changed, 234 insertions(+), 263 deletions(-) delete mode 100644 server/modules/monitor/mariadbmon/cluster_manipulation.cc diff --git a/server/modules/monitor/mariadbmon/CMakeLists.txt b/server/modules/monitor/mariadbmon/CMakeLists.txt index 36eb2a02f..9a208764e 100644 --- a/server/modules/monitor/mariadbmon/CMakeLists.txt +++ b/server/modules/monitor/mariadbmon/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(mariadbmon SHARED mariadbmon.cc utilities.cc cluster_manipulation.cc) +add_library(mariadbmon SHARED mariadbmon.cc utilities.cc) target_link_libraries(mariadbmon maxscale-common) add_dependencies(mariadbmon pcre2) set_target_properties(mariadbmon PROPERTIES VERSION "1.4.0") diff --git a/server/modules/monitor/mariadbmon/cluster_manipulation.cc b/server/modules/monitor/mariadbmon/cluster_manipulation.cc deleted file mode 100644 index cad686bfa..000000000 --- a/server/modules/monitor/mariadbmon/cluster_manipulation.cc +++ /dev/null @@ -1,191 +0,0 @@ -#include "mariadbmon.hh" - -#include -#include - -/* - * Copyright (c) 2018 MariaDB Corporation Ab - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file and at www.mariadb.com/bsl11. - * - * Change Date: 2020-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2 or later of the General - * Public License. - */ - -/** - * Generate a CHANGE MASTER TO-query. - * - * @param master_host Master hostname/address - * @param master_port Master port - * @return Generated query - */ -string MariaDBMonitor::generate_change_master_cmd(const string& master_host, int master_port) -{ - std::stringstream change_cmd; - change_cmd << "CHANGE MASTER TO MASTER_HOST = '" << master_host << "', "; - change_cmd << "MASTER_PORT = " << master_port << ", "; - change_cmd << "MASTER_USE_GTID = current_pos, "; - change_cmd << "MASTER_USER = '" << m_replication_user << "', "; - const char MASTER_PW[] = "MASTER_PASSWORD = '"; - const char END[] = "';"; -#if defined(SS_DEBUG) - std::stringstream change_cmd_nopw; - change_cmd_nopw << change_cmd.str(); - change_cmd_nopw << MASTER_PW << "******" << END;; - MXS_DEBUG("Change master command is '%s'.", change_cmd_nopw.str().c_str()); -#endif - change_cmd << MASTER_PW << m_replication_password << END; - return change_cmd.str(); -} - -/** - * Redirects slaves to replicate from another master server. - * - * @param slaves An array of slaves - * @param new_master The replication master - * @param redirected_slaves A vector where to insert successfully redirected slaves. - * @return The number of slaves successfully redirected. - */ -int MariaDBMonitor::redirect_slaves(MXS_MONITORED_SERVER* new_master, const ServerVector& slaves, - ServerVector* redirected_slaves) -{ - ss_dassert(redirected_slaves != NULL); - MXS_NOTICE("Redirecting slaves to new master."); - string change_cmd = generate_change_master_cmd(new_master->server->name, new_master->server->port); - int successes = 0; - for (ServerVector::const_iterator iter = slaves.begin(); iter != slaves.end(); iter++) - { - if (redirect_one_slave(*iter, change_cmd.c_str())) - { - successes++; - redirected_slaves->push_back(*iter); - } - } - return successes; -} - -/** - * Set the new master to replicate from the cluster external master. - * - * @param new_master The server being promoted - * @param err_out Error output - * @return True if new master accepted commands - */ -bool MariaDBMonitor::start_external_replication(MXS_MONITORED_SERVER* new_master, json_t** err_out) -{ - bool rval = false; - string change_cmd = generate_change_master_cmd(external_master_host, external_master_port); - if (mxs_mysql_query(new_master->con, change_cmd.c_str()) == 0 && - mxs_mysql_query(new_master->con, "START SLAVE;") == 0) - { - MXS_NOTICE("New master starting replication from external master %s:%d.", - external_master_host.c_str(), external_master_port); - rval = true; - } - else - { - PRINT_MXS_JSON_ERROR(err_out, "Could not start replication from external master: '%s'.", - mysql_error(new_master->con)); - } - return rval; -} - -/** - * Starts a new slave connection on a server. Should be used on a demoted master server. - * - * @param old_master The server which will start replication - * @param new_master Replication target - * @return True if commands were accepted. This does not guarantee that replication proceeds - * successfully. - */ -bool MariaDBMonitor::switchover_start_slave(MXS_MONITORED_SERVER* old_master, SERVER* new_master) -{ - bool rval = false; - string change_cmd = generate_change_master_cmd(new_master->name, new_master->port); - if (mxs_mysql_query(old_master->con, change_cmd.c_str()) == 0 && - mxs_mysql_query(old_master->con, "START SLAVE;") == 0) - { - MXS_NOTICE("Old master '%s' starting replication from '%s'.", - old_master->server->unique_name, new_master->unique_name); - rval = true; - } - else - { - MXS_ERROR("Old master '%s' could not start replication: '%s'.", - old_master->server->unique_name, mysql_error(old_master->con)); - } - return rval; -} - -/** - * Redirect one slave server to another master - * - * @param slave Server to redirect - * @param change_cmd Change master command, usually generated by generate_change_master_cmd() - * @return True if slave accepted all commands - */ -bool MariaDBMonitor::redirect_one_slave(MXS_MONITORED_SERVER* slave, const char* change_cmd) -{ - bool rval = false; - if (mxs_mysql_query(slave->con, "STOP SLAVE;") == 0 && - mxs_mysql_query(slave->con, "RESET SLAVE;") == 0 && // To erase any old I/O or SQL errors - mxs_mysql_query(slave->con, change_cmd) == 0 && - mxs_mysql_query(slave->con, "START SLAVE;") == 0) - { - rval = true; - MXS_NOTICE("Slave '%s' redirected to new master.", slave->server->unique_name); - } - else - { - MXS_WARNING("Slave '%s' redirection failed: '%s'.", slave->server->unique_name, - mysql_error(slave->con)); - } - return rval; -} - -uint32_t MariaDBMonitor::do_rejoin(const ServerVector& joinable_servers) -{ - SERVER* master = this->master->server; - uint32_t servers_joined = 0; - if (!joinable_servers.empty()) - { - string change_cmd = generate_change_master_cmd(master->name, master->port); - for (ServerVector::const_iterator iter = joinable_servers.begin(); - iter != joinable_servers.end(); - iter++) - { - MXS_MONITORED_SERVER* joinable = *iter; - const char* name = joinable->server->unique_name; - const char* master_name = master->unique_name; - MySqlServerInfo* redir_info = get_server_info(this, joinable); - - bool op_success; - if (redir_info->n_slaves_configured == 0) - { - MXS_NOTICE("Directing standalone server '%s' to replicate from '%s'.", name, master_name); - op_success = join_cluster(joinable, change_cmd.c_str()); - } - else - { - MXS_NOTICE("Server '%s' is replicating from a server other than '%s', " - "redirecting it to '%s'.", name, master_name, master_name); - op_success = redirect_one_slave(joinable, change_cmd.c_str()); - } - - if (op_success) - { - servers_joined++; - } - } - } - return servers_joined; -} - -bool MariaDBMonitor::cluster_can_be_joined() -{ - return (master != NULL && SERVER_IS_MASTER(master->server) && master_gtid_domain >= 0); -} \ No newline at end of file diff --git a/server/modules/monitor/mariadbmon/mariadbmon.cc b/server/modules/monitor/mariadbmon/mariadbmon.cc index 668be379b..1db72d999 100644 --- a/server/modules/monitor/mariadbmon/mariadbmon.cc +++ b/server/modules/monitor/mariadbmon/mariadbmon.cc @@ -20,11 +20,14 @@ #include "mariadbmon.hh" #include #include +#include #include +#include #include #include #include #include +#include #include #include #include @@ -56,6 +59,16 @@ #define SLAVE_HOSTS_HOSTNAME 1 #define SLAVE_HOSTS_PORT 2 +/** Utility macro for printing both MXS_ERROR and json error */ +#define PRINT_MXS_JSON_ERROR(err_out, format, ...)\ + do {\ + MXS_ERROR(format, ##__VA_ARGS__);\ + if (err_out)\ + {\ + *err_out = mxs_json_error_append(*err_out, format, ##__VA_ARGS__);\ + }\ + } while (false) + using std::string; typedef std::vector StringVector; class MySqlServerInfo; @@ -87,6 +100,8 @@ static void set_slave_heartbeat(MXS_MONITOR *, MXS_MONITORED_SERVER *); static int add_slave_to_master(long *, int, long); static bool isMySQLEvent(mxs_monitor_event_t event); void check_maxscale_schema_replication(MXS_MONITOR *monitor); +static MySqlServerInfo* get_server_info(MariaDBMonitor* handle, const MXS_MONITORED_SERVER* db); +static const MySqlServerInfo* get_server_info(const MariaDBMonitor* handle, const MXS_MONITORED_SERVER* db); static bool mon_process_failover(MariaDBMonitor*, uint32_t, bool*); static bool do_failover(MariaDBMonitor* mon, json_t** output); static bool do_switchover(MariaDBMonitor* mon, MXS_MONITORED_SERVER* current_master, @@ -99,8 +114,10 @@ static void read_server_variables(MXS_MONITORED_SERVER* database, MySqlServerInf static bool server_is_rejoin_suspect(MariaDBMonitor* mon, MXS_MONITORED_SERVER* server, MySqlServerInfo* master_info); static bool get_joinable_servers(MariaDBMonitor* mon, ServerVector* output); +static uint32_t do_rejoin(MariaDBMonitor* mon, const ServerVector& servers); static bool join_cluster(MXS_MONITORED_SERVER* server, const char* change_cmd); static void disable_setting(MariaDBMonitor* mon, const char* setting); +static bool cluster_can_be_joined(MariaDBMonitor* mon); static bool can_replicate_from(MariaDBMonitor* mon, MXS_MONITORED_SERVER* slave, MySqlServerInfo* slave_info, MXS_MONITORED_SERVER* master, MySqlServerInfo* master_info); @@ -108,6 +125,7 @@ static bool wait_cluster_stabilization(MariaDBMonitor* mon, MXS_MONITORED_SERVER const ServerVector& slaves, int seconds_remaining); static string get_connection_errors(const ServerVector& servers); static int64_t scan_server_id(const char* id_string); +static string generate_change_master_cmd(MariaDBMonitor* mon, const string& master_host, int master_port); static bool report_version_err = true; static const char* hb_table_name = "maxscale_schema.replication_heartbeat"; @@ -531,7 +549,7 @@ bool mysql_rejoin(MXS_MONITOR* mon, SERVER* rejoin_server, json_t** output) bool rval = false; MariaDBMonitor *handle = static_cast(mon->handle); - if (handle->cluster_can_be_joined()) + if (cluster_can_be_joined(handle)) { MXS_MONITORED_SERVER* mon_server = mon_get_monitored_server(mon, rejoin_server); if (mon_server) @@ -546,7 +564,7 @@ bool mysql_rejoin(MXS_MONITOR* mon, SERVER* rejoin_server, json_t** output) { ServerVector joinable_server; joinable_server.push_back(mon_server); - if (handle->do_rejoin(joinable_server) == 1) + if (do_rejoin(handle, joinable_server) == 1) { rval = true; MXS_NOTICE("Rejoin performed."); @@ -767,36 +785,36 @@ void init_server_info(MariaDBMonitor *handle) } } -MySqlServerInfo* get_server_info(MariaDBMonitor* handle, const MXS_MONITORED_SERVER* db) +static MySqlServerInfo* get_server_info(MariaDBMonitor* handle, const MXS_MONITORED_SERVER* db) { ServerInfoMap::iterator iter = handle->server_info.find(db); ss_dassert(iter != handle->server_info.end()); return &iter->second; } -const MySqlServerInfo* get_server_info(const MariaDBMonitor* handle, const MXS_MONITORED_SERVER* db) +static const MySqlServerInfo* get_server_info(const MariaDBMonitor* handle, const MXS_MONITORED_SERVER* db) { return get_server_info(const_cast(handle), db); } -bool MariaDBMonitor::set_replication_credentials(const MXS_CONFIG_PARAMETER* params) +static bool set_replication_credentials(MariaDBMonitor *handle, const MXS_CONFIG_PARAMETER* params) { bool rval = false; - string repl_user = config_get_string(params, CN_REPLICATION_USER); - string repl_pw = config_get_string(params, CN_REPLICATION_PASSWORD); + const char* repl_user = config_get_string(params, CN_REPLICATION_USER); + const char* repl_pw = config_get_string(params, CN_REPLICATION_PASSWORD); - if (repl_user.empty() && repl_pw.empty()) + if (!*repl_user && !*repl_pw) { // No replication credentials defined, use monitor credentials - repl_user = monitor->user; - repl_pw = monitor->password; + repl_user = handle->monitor->user; + repl_pw = handle->monitor->password; } - if (!repl_user.empty() && !repl_pw.empty()) + if (*repl_user && *repl_pw) { - m_replication_user = repl_user; - char* decrypted = decrypt_password(repl_pw.c_str()); - m_replication_password = decrypted; + handle->replication_user = repl_user; + char* decrypted = decrypt_password(repl_pw); + handle->replication_password = decrypted; MXS_FREE(decrypted); rval = true; } @@ -842,6 +860,8 @@ startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params) { handle->shutdown = 0; handle->script.clear(); + handle->replication_user.clear(); + handle->replication_password.clear(); handle->excluded_servers.clear(); } else @@ -884,7 +904,7 @@ startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params) } MXS_FREE(excluded_array); - if (!handle->set_replication_credentials(params)) + if (!set_replication_credentials(handle, params)) { MXS_ERROR("Both '%s' and '%s' must be defined", CN_REPLICATION_USER, CN_REPLICATION_PASSWORD); error = true; @@ -2344,13 +2364,13 @@ monitorMain(void *arg) // Do not auto-join servers on this monitor loop if a failover (or any other cluster modification) // has been performed, as server states have not been updated yet. It will happen next iteration. if (!config_get_global_options()->passive && handle->auto_rejoin && - !failover_performed && handle->cluster_can_be_joined()) + !failover_performed && cluster_can_be_joined(handle)) { // Check if any servers should be autojoined to the cluster ServerVector joinable_servers; if (get_joinable_servers(handle, &joinable_servers)) { - uint32_t joins = handle->do_rejoin(joinable_servers); + uint32_t joins = do_rejoin(handle, joinable_servers); if (joins > 0) { MXS_NOTICE("%d server(s) redirected or rejoined the cluster.", joins); @@ -3441,6 +3461,25 @@ bool failover_wait_relay_log(MariaDBMonitor* mon, MXS_MONITORED_SERVER* new_mast return rval; } +bool start_external_replication(MariaDBMonitor* mon, MXS_MONITORED_SERVER* new_master, json_t** err_out) +{ + bool rval = false; + string change_cmd = generate_change_master_cmd(mon, mon->external_master_host, mon->external_master_port); + if (mxs_mysql_query(new_master->con, change_cmd.c_str()) == 0 && + mxs_mysql_query(new_master->con, "START SLAVE;") == 0) + { + MXS_NOTICE("New master starting replication from external master %s:%d.", + mon->external_master_host.c_str(), mon->external_master_port); + rval = true; + } + else + { + PRINT_MXS_JSON_ERROR(err_out, "Could not start replication from external master: '%s'.", + mysql_error(new_master->con)); + } + return rval; +} + /** * Prepares a server for the replication master role. * @@ -3475,13 +3514,96 @@ bool promote_new_master(MariaDBMonitor* mon, MXS_MONITORED_SERVER* new_master, j // If the previous master was a slave to an external master, start the equivalent slave connection on // the new master. Success of replication is not checked. else if (mon->external_master_port != PORT_UNKNOWN && - !mon->start_external_replication(new_master, err_out)) + !start_external_replication(mon, new_master, err_out)) { success = false; } return success; } +/** + * Generate a CHANGE MASTER TO-query. + * + * @param mon Cluster monitor, needed for username & password + * @param master_host Master hostname/address + * @param master_port Master port + * @return Generated query + */ +string generate_change_master_cmd(MariaDBMonitor* mon, const string& master_host, int master_port) +{ + std::stringstream change_cmd; + change_cmd << "CHANGE MASTER TO MASTER_HOST = '" << master_host << "', "; + change_cmd << "MASTER_PORT = " << master_port << ", "; + change_cmd << "MASTER_USE_GTID = current_pos, "; + change_cmd << "MASTER_USER = '" << mon->replication_user << "', "; + const char MASTER_PW[] = "MASTER_PASSWORD = '"; + const char END[] = "';"; +#if defined(SS_DEBUG) + std::stringstream change_cmd_nopw; + change_cmd_nopw << change_cmd.str(); + change_cmd_nopw << MASTER_PW << "******" << END;; + MXS_DEBUG("Change master command is '%s'.", change_cmd_nopw.str().c_str()); +#endif + change_cmd << MASTER_PW << mon->replication_password << END; + return change_cmd.str(); +} + +/** + * Redirect one slave server to another master + * + * @param slave Server to redirect + * @param change_cmd Change master command, usually generated by generate_change_master_cmd() + * @return True if slave accepted all commands + */ +bool redirect_one_slave(MXS_MONITORED_SERVER* slave, const char* change_cmd) +{ + bool rval = false; + if (mxs_mysql_query(slave->con, "STOP SLAVE;") == 0 && + mxs_mysql_query(slave->con, "RESET SLAVE;") == 0 && // To erase any old I/O or SQL errors + mxs_mysql_query(slave->con, change_cmd) == 0 && + mxs_mysql_query(slave->con, "START SLAVE;") == 0) + { + rval = true; + MXS_NOTICE("Slave '%s' redirected to new master.", slave->server->unique_name); + } + else + { + MXS_WARNING("Slave '%s' redirection failed: '%s'.", slave->server->unique_name, + mysql_error(slave->con)); + } + return rval; +} + +/** + * Redirects slaves to replicate from another master server. + * + * @param mon The monitor + * @param slaves An array of slaves + * @param new_master The replication master + * @param redirected_slaves A vector where to insert successfully redirected slaves. Can be NULL. + * @return The number of slaves successfully redirected. + */ +int redirect_slaves(MariaDBMonitor* mon, MXS_MONITORED_SERVER* new_master, const ServerVector& slaves, + ServerVector* redirected_slaves = NULL) +{ + MXS_NOTICE("Redirecting slaves to new master."); + std::string change_cmd = generate_change_master_cmd(mon, + new_master->server->name, new_master->server->port); + int successes = 0; + for (ServerVector::const_iterator iter = slaves.begin(); iter != slaves.end(); iter++) + { + if (redirect_one_slave(*iter, change_cmd.c_str())) + { + successes++; + if (redirected_slaves) + { + redirected_slaves->push_back(*iter); + } + } + } + return successes; +} + /** * Print a redirect error to logs. If err_out exists, generate a combined error message by querying all * the server parameters for connection errors and append these errors to err_out. @@ -3555,7 +3677,7 @@ static bool do_failover(MariaDBMonitor* mon, json_t** err_out) { // Step 4: Redirect slaves. ServerVector redirected_slaves; - int redirects = mon->redirect_slaves(new_master, redirectable_slaves, &redirected_slaves); + int redirects = redirect_slaves(mon, new_master, redirectable_slaves, &redirected_slaves); bool success = redirectable_slaves.empty() ? true : redirects > 0; if (success) { @@ -3898,6 +4020,34 @@ static bool switchover_wait_slaves_catchup(const ServerVector& slaves, const Gti return success; } +/** + * Starts a new slave connection on a server. Should be used on a demoted master server. + * + * @param mon Cluster monitor + * @param old_master The server which will start replication + * @param new_master Replication target + * @return True if commands were accepted. This does not guarantee that replication proceeds + * successfully. + */ +static bool switchover_start_slave(MariaDBMonitor* mon, MXS_MONITORED_SERVER* old_master, SERVER* new_master) +{ + bool rval = false; + std::string change_cmd = generate_change_master_cmd(mon, new_master->name, new_master->port); + if (mxs_mysql_query(old_master->con, change_cmd.c_str()) == 0 && + mxs_mysql_query(old_master->con, "START SLAVE;") == 0) + { + MXS_NOTICE("Old master '%s' starting replication from '%s'.", + old_master->server->unique_name, new_master->unique_name); + rval = true; + } + else + { + MXS_ERROR("Old master '%s' could not start replication: '%s'.", + old_master->server->unique_name, mysql_error(old_master->con)); + } + return rval; +} + /** * Get MariaDB connection error strings from all the given servers, form one string. * @@ -4106,13 +4256,13 @@ static bool do_switchover(MariaDBMonitor* mon, MXS_MONITORED_SERVER* current_mas catchup_and_promote_success = true; // Step 5: Redirect slaves and start replication on old master. ServerVector redirected_slaves; - bool start_ok = mon->switchover_start_slave(demotion_target, promotion_target->server); + bool start_ok = switchover_start_slave(mon, demotion_target, promotion_target->server); if (start_ok) { redirected_slaves.push_back(demotion_target); } - int redirects = mon->redirect_slaves(promotion_target, redirectable_slaves, - &redirected_slaves); + int redirects = redirect_slaves(mon, promotion_target, + redirectable_slaves, &redirected_slaves); bool success = redirectable_slaves.empty() ? start_ok : start_ok || redirects > 0; if (success) @@ -4164,7 +4314,7 @@ static bool do_switchover(MariaDBMonitor* mon, MXS_MONITORED_SERVER* current_mas // Try to reactivate external replication if any. if (mon->external_master_port != PORT_UNKNOWN) { - mon->start_external_replication(new_master, err_out); + start_external_replication(mon, new_master, err_out); } } } @@ -4339,6 +4489,52 @@ static bool get_joinable_servers(MariaDBMonitor* mon, ServerVector* output) return comm_ok; } +/** + * (Re)join given servers to the cluster. The servers in the array are assumed to be joinable. + * Usually the list is created by get_joinable_servers(). + * + * @param mon Cluster monitor + * @param joinable_servers Which servers to rejoin + * @return The number of servers successfully rejoined + */ +static uint32_t do_rejoin(MariaDBMonitor* mon, const ServerVector& joinable_servers) +{ + SERVER* master = mon->master->server; + uint32_t servers_joined = 0; + if (!joinable_servers.empty()) + { + string change_cmd = generate_change_master_cmd(mon, master->name, master->port); + for (ServerVector::const_iterator iter = joinable_servers.begin(); + iter != joinable_servers.end(); + iter++) + { + MXS_MONITORED_SERVER* joinable = *iter; + const char* name = joinable->server->unique_name; + const char* master_name = master->unique_name; + MySqlServerInfo* redir_info = get_server_info(mon, joinable); + + bool op_success; + if (redir_info->n_slaves_configured == 0) + { + MXS_NOTICE("Directing standalone server '%s' to replicate from '%s'.", name, master_name); + op_success = join_cluster(joinable, change_cmd.c_str()); + } + else + { + MXS_NOTICE("Server '%s' is replicating from a server other than '%s', " + "redirecting it to '%s'.", name, master_name, master_name); + op_success = redirect_one_slave(joinable, change_cmd.c_str()); + } + + if (op_success) + { + servers_joined++; + } + } + } + return servers_joined; +} + /** * Joins a standalone server to the cluster. * @@ -4346,7 +4542,7 @@ static bool get_joinable_servers(MariaDBMonitor* mon, ServerVector* output) * @param change_cmd Change master command * @return True if commands were accepted by server */ -bool MariaDBMonitor::join_cluster(MXS_MONITORED_SERVER* server, const char* change_cmd) +static bool join_cluster(MXS_MONITORED_SERVER* server, const char* change_cmd) { /* Server does not have slave connections. This operation can fail, or the resulting * replication may end up broken. */ @@ -4379,6 +4575,17 @@ static void disable_setting(MariaDBMonitor* mon, const char* setting) monitorAddParameters(mon->monitor, &p); } +/** + * Is the cluster a valid rejoin target + * + * @param mon Cluster monitor + * @return True, if cluster can be joined + */ +static bool cluster_can_be_joined(MariaDBMonitor* mon) +{ + return (mon->master != NULL && SERVER_IS_MASTER(mon->master->server) && mon->master_gtid_domain >= 0); +} + /** * Scan a server id from a string. * diff --git a/server/modules/monitor/mariadbmon/mariadbmon.hh b/server/modules/monitor/mariadbmon/mariadbmon.hh index 948fed363..18cf3cc60 100644 --- a/server/modules/monitor/mariadbmon/mariadbmon.hh +++ b/server/modules/monitor/mariadbmon/mariadbmon.hh @@ -22,32 +22,17 @@ #include #include -#include #include #include #include "utilities.hh" -/** Utility macro for printing both MXS_ERROR and json error */ -#define PRINT_MXS_JSON_ERROR(err_out, format, ...)\ - do {\ - MXS_ERROR(format, ##__VA_ARGS__);\ - if (err_out)\ - {\ - *err_out = mxs_json_error_append(*err_out, format, ##__VA_ARGS__);\ - }\ - } while (false) - extern const int PORT_UNKNOWN; extern const int64_t SERVER_ID_UNKNOWN; -class MariaDBMonitor; typedef std::tr1::unordered_map ServerInfoMap; typedef std::vector ServerVector; -MySqlServerInfo* get_server_info(MariaDBMonitor* handle, const MXS_MONITORED_SERVER* db); -const MySqlServerInfo* get_server_info(const MariaDBMonitor* handle, const MXS_MONITORED_SERVER* db); - // MySQL Monitor module instance class MariaDBMonitor { @@ -58,22 +43,6 @@ public: MariaDBMonitor(); ~MariaDBMonitor(); - /** - * (Re)join given servers to the cluster. The servers in the array are assumed to be joinable. - * Usually the list is created by get_joinable_servers(). - * - * @param joinable_servers Which servers to rejoin - * @return The number of servers successfully rejoined - */ - uint32_t do_rejoin(const ServerVector& joinable_servers); - - /** - * Check if the cluster is a valid rejoin target. - * - * @return True if master and gtid domain are known - */ - bool cluster_can_be_joined(); - MXS_MONITOR* monitor; /**< Generic monitor object */ THREAD thread; /**< Monitor thread */ int shutdown; /**< Flag to shutdown the monitor thread */ @@ -98,6 +67,8 @@ public: bool replicationHeartbeat; /**< Monitor flag for MySQL replication heartbeat */ // Failover, switchover and rejoin settings + string replication_user; /**< Replication user for CHANGE MASTER TO-commands */ + string replication_password; /**< Replication password for CHANGE MASTER TO-commands */ int failcount; /**< How many monitoring cycles master must be down before auto-failover * begins */ uint32_t failover_timeout; /**< Timeout in seconds for the master failover */ @@ -113,20 +84,4 @@ public: string script; /**< Script to call when state changes occur on servers */ uint64_t events; /**< enabled events */ bool allow_cluster_recovery; /**< Allow failed servers to rejoin the cluster */ - -private: - // Failover, switchover and rejoin settings - string m_replication_user; /**< Replication user for CHANGE MASTER TO-commands */ - string m_replication_password; /**< Replication password for CHANGE MASTER TO-commands */ - -public: - // Following methods should be private, change it once refactoring is done. - string generate_change_master_cmd(const string& master_host, int master_port); - int redirect_slaves(MXS_MONITORED_SERVER* new_master, const ServerVector& slaves, - ServerVector* redirected_slaves); - bool set_replication_credentials(const MXS_CONFIG_PARAMETER* params); - bool start_external_replication(MXS_MONITORED_SERVER* new_master, json_t** err_out); - bool switchover_start_slave(MXS_MONITORED_SERVER* old_master, SERVER* new_master); - bool redirect_one_slave(MXS_MONITORED_SERVER* slave, const char* change_cmd); - bool join_cluster(MXS_MONITORED_SERVER* server, const char* change_cmd); }; From 13661ab4a685640c0dbb547574ce493e1b8663d5 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Sat, 24 Feb 2018 15:35:36 +0200 Subject: [PATCH 08/18] Revert "MariaDB Monitor: Move additional classes to separate file" This reverts commit ff55106610881d55db88eca9e2ef6a056cbc8d51. --- .../modules/monitor/mariadbmon/CMakeLists.txt | 2 +- .../modules/monitor/mariadbmon/mariadbmon.cc | 409 +++++++++++++----- .../modules/monitor/mariadbmon/mariadbmon.hh | 34 +- .../modules/monitor/mariadbmon/utilities.cc | 112 ----- .../modules/monitor/mariadbmon/utilities.hh | 127 ------ 5 files changed, 317 insertions(+), 367 deletions(-) delete mode 100644 server/modules/monitor/mariadbmon/utilities.cc delete mode 100644 server/modules/monitor/mariadbmon/utilities.hh diff --git a/server/modules/monitor/mariadbmon/CMakeLists.txt b/server/modules/monitor/mariadbmon/CMakeLists.txt index 9a208764e..ba5aaebb0 100644 --- a/server/modules/monitor/mariadbmon/CMakeLists.txt +++ b/server/modules/monitor/mariadbmon/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(mariadbmon SHARED mariadbmon.cc utilities.cc) +add_library(mariadbmon SHARED mariadbmon.cc) target_link_libraries(mariadbmon maxscale-common) add_dependencies(mariadbmon pcre2) set_target_properties(mariadbmon PROPERTIES VERSION "1.4.0") diff --git a/server/modules/monitor/mariadbmon/mariadbmon.cc b/server/modules/monitor/mariadbmon/mariadbmon.cc index 1db72d999..ee10f4347 100644 --- a/server/modules/monitor/mariadbmon/mariadbmon.cc +++ b/server/modules/monitor/mariadbmon/mariadbmon.cc @@ -35,7 +35,6 @@ #include // TODO: For monitorAddParameters #include "../../../core/internal/monitor.h" -#include "utilities.hh" /** Column positions for SHOW SLAVE STATUS */ #define MYSQL55_STATUS_MASTER_LOG_POS 5 @@ -70,9 +69,16 @@ } while (false) using std::string; +typedef std::vector ServerVector; typedef std::vector StringVector; class MySqlServerInfo; -class MariaDBMonitor; + +enum mysql_server_version +{ + MYSQL_SERVER_VERSION_100, + MYSQL_SERVER_VERSION_55, + MYSQL_SERVER_VERSION_51 +}; enum slave_down_setting_t { @@ -95,37 +101,36 @@ static json_t* diagnostics_json(const MXS_MONITOR *); static MXS_MONITORED_SERVER *getServerByNodeId(MXS_MONITORED_SERVER *, long); static MXS_MONITORED_SERVER *getSlaveOfNodeId(MXS_MONITORED_SERVER *, long, slave_down_setting_t); static MXS_MONITORED_SERVER *get_replication_tree(MXS_MONITOR *, int); -static void set_master_heartbeat(MariaDBMonitor *, MXS_MONITORED_SERVER *); +static void set_master_heartbeat(MYSQL_MONITOR *, MXS_MONITORED_SERVER *); static void set_slave_heartbeat(MXS_MONITOR *, MXS_MONITORED_SERVER *); static int add_slave_to_master(long *, int, long); static bool isMySQLEvent(mxs_monitor_event_t event); void check_maxscale_schema_replication(MXS_MONITOR *monitor); -static MySqlServerInfo* get_server_info(MariaDBMonitor* handle, const MXS_MONITORED_SERVER* db); -static const MySqlServerInfo* get_server_info(const MariaDBMonitor* handle, const MXS_MONITORED_SERVER* db); -static bool mon_process_failover(MariaDBMonitor*, uint32_t, bool*); -static bool do_failover(MariaDBMonitor* mon, json_t** output); -static bool do_switchover(MariaDBMonitor* mon, MXS_MONITORED_SERVER* current_master, +static MySqlServerInfo* get_server_info(const MYSQL_MONITOR* handle, const MXS_MONITORED_SERVER* db); +static bool mon_process_failover(MYSQL_MONITOR*, uint32_t, bool*); +static bool do_failover(MYSQL_MONITOR* mon, json_t** output); +static bool do_switchover(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* current_master, MXS_MONITORED_SERVER* new_master, json_t** err_out); -static bool update_gtids(MariaDBMonitor* mon, MXS_MONITORED_SERVER *database, MySqlServerInfo* info); +static bool update_gtids(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER *database, MySqlServerInfo* info); static bool update_replication_settings(MXS_MONITORED_SERVER *database, MySqlServerInfo* info); static bool query_one_row(MXS_MONITORED_SERVER *database, const char* query, unsigned int expected_cols, StringVector* output); static void read_server_variables(MXS_MONITORED_SERVER* database, MySqlServerInfo* serv_info); -static bool server_is_rejoin_suspect(MariaDBMonitor* mon, MXS_MONITORED_SERVER* server, +static bool server_is_rejoin_suspect(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* server, MySqlServerInfo* master_info); -static bool get_joinable_servers(MariaDBMonitor* mon, ServerVector* output); -static uint32_t do_rejoin(MariaDBMonitor* mon, const ServerVector& servers); +static bool get_joinable_servers(MYSQL_MONITOR* mon, ServerVector* output); +static uint32_t do_rejoin(MYSQL_MONITOR* mon, const ServerVector& servers); static bool join_cluster(MXS_MONITORED_SERVER* server, const char* change_cmd); -static void disable_setting(MariaDBMonitor* mon, const char* setting); -static bool cluster_can_be_joined(MariaDBMonitor* mon); -static bool can_replicate_from(MariaDBMonitor* mon, +static void disable_setting(MYSQL_MONITOR* mon, const char* setting); +static bool cluster_can_be_joined(MYSQL_MONITOR* mon); +static bool can_replicate_from(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* slave, MySqlServerInfo* slave_info, MXS_MONITORED_SERVER* master, MySqlServerInfo* master_info); -static bool wait_cluster_stabilization(MariaDBMonitor* mon, MXS_MONITORED_SERVER* new_master, +static bool wait_cluster_stabilization(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* new_master, const ServerVector& slaves, int seconds_remaining); static string get_connection_errors(const ServerVector& servers); static int64_t scan_server_id(const char* id_string); -static string generate_change_master_cmd(MariaDBMonitor* mon, const string& master_host, int master_port); +static string generate_change_master_cmd(MYSQL_MONITOR* mon, const string& master_host, int master_port); static bool report_version_err = true; static const char* hb_table_name = "maxscale_schema.replication_heartbeat"; @@ -155,18 +160,185 @@ static const char CN_REPLICATION_PASSWORD[] = "replication_password"; #define DEFAULT_MASTER_FAILURE_TIMEOUT "10" /** Server id default value */ -const int64_t SERVER_ID_UNKNOWN = -1; +static const int64_t SERVER_ID_UNKNOWN = -1; /** Default port */ -const int PORT_UNKNOWN = 0; +static const int PORT_UNKNOWN = 0; -MariaDBMonitor::MariaDBMonitor() -{} +class Gtid +{ +public: + uint32_t domain; + int64_t server_id; // Is actually 32bit unsigned. 0 is only used by server versions <= 10.1 + uint64_t sequence; + Gtid() + : domain(0) + , server_id(SERVER_ID_UNKNOWN) + , sequence(0) + {} -MariaDBMonitor::~MariaDBMonitor() -{} + /** + * Parse a Gtid-triplet from a string. In case of a multi-triplet value, only the triplet with + * the given domain is returned. + * + * @param str Gtid string + * @param search_domain The Gtid domain whose triplet should be returned. Negative domain stands for + * autoselect, which is only allowed when the string contains one triplet. + */ + Gtid(const char* str, int64_t search_domain = -1) + : domain(0) + , server_id(SERVER_ID_UNKNOWN) + , sequence(0) + { + // Autoselect only allowed with one triplet + ss_dassert(search_domain >= 0 || strchr(str, ',') == NULL); + parse_triplet(str); + if (search_domain >= 0 && domain != search_domain) + { + // Search for the correct triplet. + bool found = false; + for (const char* next_triplet = strchr(str, ','); + next_triplet != NULL && !found; + next_triplet = strchr(next_triplet, ',')) + { + parse_triplet(++next_triplet); + if (domain == search_domain) + { + found = true; + } + } + ss_dassert(found); + } + } + bool operator == (const Gtid& rhs) const + { + return domain == rhs.domain && + server_id != SERVER_ID_UNKNOWN && server_id == rhs.server_id && + sequence == rhs.sequence; + } + string to_string() const + { + std::stringstream ss; + if (server_id != SERVER_ID_UNKNOWN) + { + ss << domain << "-" << server_id << "-" << sequence; + } + return ss.str(); + } +private: + void parse_triplet(const char* str) + { + ss_debug(int rv = ) sscanf(str, "%" PRIu32 "-%" PRId64 "-%" PRIu64, &domain, &server_id, &sequence); + ss_dassert(rv == 3); + } +}; -bool uses_gtid(MariaDBMonitor* mon, MXS_MONITORED_SERVER* mon_server, json_t** error_out) +// Contains data returned by one row of SHOW ALL SLAVES STATUS +class SlaveStatusInfo +{ +public: + int64_t master_server_id; /**< The master's server_id value. Valid ids are 32bit unsigned. -1 is + * unread/error. */ + string master_host; /**< Master server host name. */ + int master_port; /**< Master server port. */ + bool slave_io_running; /**< Whether the slave I/O thread is running and connected. */ + bool slave_sql_running; /**< Whether or not the SQL thread is running. */ + string master_log_file; /**< Name of the master binary log file that the I/O thread is currently + * reading from. */ + uint64_t read_master_log_pos; /**< Position up to which the I/O thread has read in the current master + * binary log file. */ + Gtid gtid_io_pos; /**< Gtid I/O position of the slave thread. Only shows the triplet with + * the current master domain. */ + string last_error; /**< Last IO or SQL error encountered. */ + + SlaveStatusInfo() + : master_server_id(SERVER_ID_UNKNOWN) + , master_port(0) + , slave_io_running(false) + , slave_sql_running(false) + , read_master_log_pos(0) + {} +}; + +// This class groups some miscellaneous replication related settings together. +class ReplicationSettings +{ +public: + bool gtid_strict_mode; /**< Enable additional checks for replication */ + bool log_bin; /**< Is binary logging enabled */ + bool log_slave_updates; /**< Does the slave log replicated events to binlog */ + ReplicationSettings() + : gtid_strict_mode(false) + , log_bin(false) + , log_slave_updates(false) + {} +}; + +/** + * Monitor specific information about a server + * + * Note: These are initialized in @c init_server_info + */ +class MySqlServerInfo +{ +public: + int64_t server_id; /**< Value of @@server_id. Valid values are 32bit unsigned. */ + int group; /**< Multi-master group where this server belongs, + * 0 for servers not in groups */ + bool read_only; /**< Value of @@read_only */ + bool slave_configured; /**< Whether SHOW SLAVE STATUS returned rows */ + bool binlog_relay; /** Server is a Binlog Relay */ + int n_slaves_configured; /**< Number of configured slave connections*/ + int n_slaves_running; /**< Number of running slave connections */ + int slave_heartbeats; /**< Number of received heartbeats */ + double heartbeat_period; /**< The time interval between heartbeats */ + time_t latest_event; /**< Time when latest event was received from the master */ + int64_t gtid_domain_id; /**< The value of gtid_domain_id, the domain which is used for + * new non-replicated events. */ + Gtid gtid_current_pos; /**< Gtid of latest event. Only shows the triplet + * with the current master domain. */ + Gtid gtid_binlog_pos; /**< Gtid of latest event written to binlog. Only shows + * the triplet with the current master domain. */ + SlaveStatusInfo slave_status; /**< Data returned from SHOW SLAVE STATUS */ + ReplicationSettings rpl_settings; /**< Miscellaneous replication related settings */ + mysql_server_version version; /**< Server version, 10.X, 5.5 or 5.1 */ + + MySqlServerInfo() + : server_id(SERVER_ID_UNKNOWN) + , group(0) + , read_only(false) + , slave_configured(false) + , binlog_relay(false) + , n_slaves_configured(0) + , n_slaves_running(0) + , slave_heartbeats(0) + , heartbeat_period(0) + , latest_event(0) + , gtid_domain_id(-1) + , version(MYSQL_SERVER_VERSION_51) + {} + + /** + * Calculate how many events are left in the relay log. If gtid_current_pos is ahead of Gtid_IO_Pos, + * or a server_id is unknown, an error value is returned. + * + * @return Number of events in relay log according to latest queried info. A negative value signifies + * an error in the gtid-values. + */ + int64_t relay_log_events() + { + if (slave_status.gtid_io_pos.server_id != SERVER_ID_UNKNOWN && + gtid_current_pos.server_id != SERVER_ID_UNKNOWN && + slave_status.gtid_io_pos.domain == gtid_current_pos.domain && + slave_status.gtid_io_pos.sequence >= gtid_current_pos.sequence) + { + return slave_status.gtid_io_pos.sequence - gtid_current_pos.sequence; + } + return -1; + } +}; + +bool uses_gtid(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* mon_server, json_t** error_out) { bool rval = false; const MySqlServerInfo* info = get_server_info(mon, mon_server); @@ -182,7 +354,6 @@ bool uses_gtid(MariaDBMonitor* mon, MXS_MONITORED_SERVER* mon_server, json_t** e } return rval; } - /** * Check that the given server is a master and it's the only master. * @@ -193,7 +364,7 @@ bool uses_gtid(MariaDBMonitor* mon, MXS_MONITORED_SERVER* mon_server, json_t** e * @return False, if there is some error with the specified current master, * True otherwise. */ -bool mysql_switchover_check_current(const MariaDBMonitor* mon, +bool mysql_switchover_check_current(const MYSQL_MONITOR* mon, const MXS_MONITORED_SERVER* suggested_curr_master, json_t** error_out) { @@ -265,7 +436,7 @@ bool mysql_switchover_check_new(const MXS_MONITORED_SERVER* monitored_server, js * @param error_out JSON error out * @return True if failover may proceed */ -bool failover_check(MariaDBMonitor* mon, json_t** error_out) +bool failover_check(MYSQL_MONITOR* mon, json_t** error_out) { // Check that there is no running master and that there is at least one running server in the cluster. // Also, all slaves must be using gtid-replication. @@ -337,7 +508,7 @@ bool mysql_switchover(MXS_MONITOR* mon, MXS_MONITORED_SERVER* new_master, MXS_MO } bool rval = false; - MariaDBMonitor* handle = static_cast(mon->handle); + MYSQL_MONITOR* handle = static_cast(mon->handle); bool current_ok = mysql_switchover_check_current(handle, current_master, error_out); bool new_ok = mysql_switchover_check_new(new_master, error_out); @@ -428,7 +599,7 @@ bool mysql_handle_switchover(const MODULECMD_ARG* args, json_t** error_out) else { // Autoselect current master - MariaDBMonitor* handle = static_cast(mon->handle); + MYSQL_MONITOR* handle = static_cast(mon->handle); if (handle->master) { mon_curr_master = handle->master; @@ -479,7 +650,7 @@ bool mysql_failover(MXS_MONITOR* mon, json_t** output) } bool rv = true; - MariaDBMonitor *handle = static_cast(mon->handle); + MYSQL_MONITOR *handle = static_cast(mon->handle); rv = failover_check(handle, output); if (rv) { @@ -548,7 +719,7 @@ bool mysql_rejoin(MXS_MONITOR* mon, SERVER* rejoin_server, json_t** output) } bool rval = false; - MariaDBMonitor *handle = static_cast(mon->handle); + MYSQL_MONITOR *handle = static_cast(mon->handle); if (cluster_can_be_joined(handle)) { MXS_MONITORED_SERVER* mon_server = mon_get_monitored_server(mon, rejoin_server); @@ -770,34 +941,41 @@ void info_free_func(void *val) * @brief Helper function that initializes the server info hashtable * * @param handle MariaDB monitor handle + * @param database List of monitored databases * @return True on success, false if initialization failed. At the moment * initialization can only fail if memory allocation fails. */ -void init_server_info(MariaDBMonitor *handle) +bool init_server_info(MYSQL_MONITOR *handle, MXS_MONITORED_SERVER *database) { - ServerInfoMap& infos = handle->server_info; - infos.clear(); + bool rval = true; - for (MXS_MONITORED_SERVER* server = handle->monitor->monitored_servers; server; server = server->next) + MySqlServerInfo info; + + while (database) { - ServerInfoMap::value_type new_val(server, MySqlServerInfo()); - infos.insert(new_val); + /** Delete any existing structures and replace them with empty ones */ + hashtable_delete(handle->server_info, database->server->unique_name); + + if (!hashtable_add(handle->server_info, database->server->unique_name, &info)) + { + rval = false; + break; + } + + database = database->next; } + + return rval; } -static MySqlServerInfo* get_server_info(MariaDBMonitor* handle, const MXS_MONITORED_SERVER* db) +static MySqlServerInfo* get_server_info(const MYSQL_MONITOR* handle, const MXS_MONITORED_SERVER* db) { - ServerInfoMap::iterator iter = handle->server_info.find(db); - ss_dassert(iter != handle->server_info.end()); - return &iter->second; + void* value = hashtable_fetch(handle->server_info, db->server->unique_name); + ss_dassert(value); + return static_cast(value); } -static const MySqlServerInfo* get_server_info(const MariaDBMonitor* handle, const MXS_MONITORED_SERVER* db) -{ - return get_server_info(const_cast(handle), db); -} - -static bool set_replication_credentials(MariaDBMonitor *handle, const MXS_CONFIG_PARAMETER* params) +static bool set_replication_credentials(MYSQL_MONITOR *handle, const MXS_CONFIG_PARAMETER* params) { bool rval = false; const char* repl_user = config_get_string(params, CN_REPLICATION_USER); @@ -829,10 +1007,9 @@ static bool set_replication_credentials(MariaDBMonitor *handle, const MXS_CONFIG * @param server Server to test * @return True if server is in the excluded-list of the monitor. */ -static bool server_is_excluded(const MariaDBMonitor *handle, const MXS_MONITORED_SERVER* server) +static bool server_is_excluded(const MYSQL_MONITOR *handle, const MXS_MONITORED_SERVER* server) { - size_t n_excluded = handle->excluded_servers.size(); - for (size_t i = 0; i < n_excluded; i++) + for (int i = 0; i < handle->n_excluded; i++) { if (handle->excluded_servers[i] == server) { @@ -855,18 +1032,33 @@ static void * startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params) { bool error = false; - MariaDBMonitor *handle = (MariaDBMonitor*) monitor->handle; + MYSQL_MONITOR *handle = (MYSQL_MONITOR*) monitor->handle; if (handle) { handle->shutdown = 0; handle->script.clear(); handle->replication_user.clear(); handle->replication_password.clear(); - handle->excluded_servers.clear(); + MXS_FREE(handle->excluded_servers); + handle->excluded_servers = NULL; + handle->n_excluded = 0; } else { - handle = new MariaDBMonitor; + handle = new MYSQL_MONITOR; + HASHTABLE *server_info = hashtable_alloc(MAX_NUM_SLAVES, + hashtable_item_strhash, hashtable_item_strcmp); + + if (server_info == NULL) + { + delete handle; + hashtable_free(server_info); + return NULL; + } + + hashtable_memory_fns(server_info, hashtable_item_strdup, info_copy_func, + hashtable_item_free, info_free_func); + handle->server_info = server_info; handle->shutdown = 0; handle->id = config_get_global_options()->id; handle->warn_set_standalone_master = true; @@ -895,14 +1087,13 @@ startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params) handle->auto_rejoin = config_get_bool(params, CN_AUTO_REJOIN); handle->verify_master_failure = config_get_bool(params, CN_VERIFY_MASTER_FAILURE); handle->master_failure_timeout = config_get_integer(params, CN_MASTER_FAILURE_TIMEOUT); - - MXS_MONITORED_SERVER** excluded_array = NULL; - int n_excluded = mon_config_get_servers(params, CN_NO_PROMOTE_SERVERS, monitor, &excluded_array); - for (int i = 0; i < n_excluded; i++) + handle->excluded_servers = NULL; + handle->n_excluded = mon_config_get_servers(params, CN_NO_PROMOTE_SERVERS, monitor, + &handle->excluded_servers); + if (handle->n_excluded < 0) { - handle->excluded_servers.push_back(excluded_array[i]); + error = true; } - MXS_FREE(excluded_array); if (!set_replication_credentials(handle, params)) { @@ -916,10 +1107,15 @@ startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params) error = true; } - init_server_info(handle); + if (!init_server_info(handle, monitor->monitored_servers)) + { + error = true; + } if (error) { + hashtable_free(handle->server_info); + MXS_FREE(handle->excluded_servers); delete handle; handle = NULL; } @@ -930,6 +1126,7 @@ startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params) if (thread_start(&handle->thread, monitorMain, handle, 0) == NULL) { MXS_ERROR("Failed to start monitor thread for monitor '%s'.", monitor->name); + hashtable_free(handle->server_info); delete handle; handle = NULL; } @@ -946,7 +1143,7 @@ startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params) static void stopMonitor(MXS_MONITOR *mon) { - MariaDBMonitor *handle = (MariaDBMonitor *) mon->handle; + MYSQL_MONITOR *handle = (MYSQL_MONITOR *) mon->handle; handle->shutdown = 1; thread_wait(handle->thread); @@ -967,7 +1164,7 @@ static bool stop_monitor(MXS_MONITOR* mon) bool actually_stopped = false; - MariaDBMonitor *handle = static_cast(mon->handle); + MYSQL_MONITOR *handle = static_cast(mon->handle); if (handle->status == MXS_MONITOR_RUNNING) { @@ -978,10 +1175,9 @@ static bool stop_monitor(MXS_MONITOR* mon) return actually_stopped; } -static string monitored_servers_to_string(const ServerVector& array) +static string monitored_servers_to_string(MXS_MONITORED_SERVER** array, size_t array_size) { string rval; - size_t array_size = array.size(); if (array_size > 0) { const char* separator = ""; @@ -1002,7 +1198,7 @@ static string monitored_servers_to_string(const ServerVector& array) */ static void diagnostics(DCB *dcb, const MXS_MONITOR *mon) { - const MariaDBMonitor *handle = (const MariaDBMonitor *)mon->handle; + const MYSQL_MONITOR *handle = (const MYSQL_MONITOR *)mon->handle; dcb_printf(dcb, "Automatic failover: %s\n", handle->auto_failover ? "Enabled" : "Disabled"); dcb_printf(dcb, "Failcount: %d\n", handle->failcount); @@ -1013,16 +1209,17 @@ static void diagnostics(DCB *dcb, const MXS_MONITOR *mon) dcb_printf(dcb, "Detect replication lag: %s\n", (handle->replicationHeartbeat) ? "Enabled" : "Disabled"); dcb_printf(dcb, "Detect stale master: %s\n", (handle->detectStaleMaster == 1) ? "Enabled" : "Disabled"); - if (handle->excluded_servers.size() > 0) + if (handle->n_excluded > 0) { dcb_printf(dcb, "Non-promotable servers (failover): "); - dcb_printf(dcb, "%s\n", monitored_servers_to_string(handle->excluded_servers).c_str()); + dcb_printf(dcb, "%s\n", + monitored_servers_to_string(handle->excluded_servers, handle->n_excluded).c_str()); } dcb_printf(dcb, "\nServer information:\n-------------------\n\n"); for (MXS_MONITORED_SERVER *db = mon->monitored_servers; db; db = db->next) { - const MySqlServerInfo* serv_info = get_server_info(handle, db); + MySqlServerInfo *serv_info = get_server_info(handle, db); dcb_printf(dcb, "Server: %s\n", db->server->unique_name); dcb_printf(dcb, "Server ID: %" PRId64 "\n", serv_info->server_id); dcb_printf(dcb, "Read only: %s\n", serv_info->read_only ? "YES" : "NO"); @@ -1068,7 +1265,7 @@ static json_t* diagnostics_json(const MXS_MONITOR *mon) { json_t* rval = json_object(); - const MariaDBMonitor *handle = (const MariaDBMonitor *)mon->handle; + const MYSQL_MONITOR *handle = (const MYSQL_MONITOR *)mon->handle; json_object_set_new(rval, "monitor_id", json_integer(handle->id)); json_object_set_new(rval, "detect_stale_master", json_boolean(handle->detectStaleMaster)); json_object_set_new(rval, "detect_stale_slave", json_boolean(handle->detectStaleSlave)); @@ -1087,9 +1284,9 @@ static json_t* diagnostics_json(const MXS_MONITOR *mon) { json_object_set_new(rval, "script", json_string(handle->script.c_str())); } - if (handle->excluded_servers.size() > 0) + if (handle->n_excluded > 0) { - string list = monitored_servers_to_string(handle->excluded_servers); + string list = monitored_servers_to_string(handle->excluded_servers, handle->n_excluded); json_object_set_new(rval, CN_NO_PROMOTE_SERVERS, json_string(list.c_str())); } if (mon->monitored_servers) @@ -1099,7 +1296,7 @@ static json_t* diagnostics_json(const MXS_MONITOR *mon) for (MXS_MONITORED_SERVER *db = mon->monitored_servers; db; db = db->next) { json_t* srv = json_object(); - const MySqlServerInfo* serv_info = get_server_info(handle, db); + MySqlServerInfo *serv_info = get_server_info(handle, db); json_object_set_new(srv, "name", json_string(db->server->unique_name)); json_object_set_new(srv, "server_id", json_integer(serv_info->server_id)); json_object_set_new(srv, "master_id", json_integer(serv_info->slave_status.master_server_id)); @@ -1151,7 +1348,7 @@ static enum mysql_server_version get_server_version(MXS_MONITORED_SERVER* db) return MYSQL_SERVER_VERSION_51; } -static bool do_show_slave_status(MariaDBMonitor* mon, +static bool do_show_slave_status(MYSQL_MONITOR* mon, MySqlServerInfo* serv_info, MXS_MONITORED_SERVER* database) { @@ -1313,7 +1510,7 @@ static bool do_show_slave_status(MariaDBMonitor* mon, * @param handle Cluster monitor * @return True, if a slave has an event more recent than master_failure_timeout. */ -static bool slave_receiving_events(MariaDBMonitor* handle) +static bool slave_receiving_events(MYSQL_MONITOR* handle) { ss_dassert(handle->master); bool received_event = false; @@ -1337,7 +1534,7 @@ static bool slave_receiving_events(MariaDBMonitor* handle) return received_event; } -static inline void monitor_mysql_db(MariaDBMonitor* mon, +static inline void monitor_mysql_db(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* database, MySqlServerInfo *serv_info) { @@ -1372,7 +1569,7 @@ static MXS_MONITORED_SERVER *build_mysql51_replication_tree(MXS_MONITOR *mon) MXS_MONITORED_SERVER* database = mon->monitored_servers; MXS_MONITORED_SERVER *ptr, *rval = NULL; int i; - MariaDBMonitor *handle = static_cast(mon->handle); + MYSQL_MONITOR *handle = static_cast(mon->handle); while (database) { @@ -1478,7 +1675,7 @@ static MXS_MONITORED_SERVER *build_mysql51_replication_tree(MXS_MONITOR *mon) static void monitorDatabase(MXS_MONITOR *mon, MXS_MONITORED_SERVER *database) { - MariaDBMonitor* handle = static_cast(mon->handle); + MYSQL_MONITOR* handle = static_cast(mon->handle); /* Don't probe servers in maintenance mode */ if (SERVER_IN_MAINT(database->server)) @@ -1706,7 +1903,7 @@ static void visit_node(struct graph_node *node, struct graph_node **stack, * member. Nodes in a group get a positive group ID where the nodes not in a * group get a group ID of 0. */ -void find_graph_cycles(MariaDBMonitor *handle, MXS_MONITORED_SERVER *database, int nservers) +void find_graph_cycles(MYSQL_MONITOR *handle, MXS_MONITORED_SERVER *database, int nservers) { struct graph_node graph[nservers]; struct graph_node *stack[nservers]; @@ -1812,7 +2009,7 @@ void find_graph_cycles(MariaDBMonitor *handle, MXS_MONITORED_SERVER *database, i * * @return True if standalone master should be used */ -bool standalone_master_required(MariaDBMonitor *handle, MXS_MONITORED_SERVER *db) +bool standalone_master_required(MYSQL_MONITOR *handle, MXS_MONITORED_SERVER *db) { int candidates = 0; @@ -1849,7 +2046,7 @@ bool standalone_master_required(MariaDBMonitor *handle, MXS_MONITORED_SERVER *db * @param handle Monitor instance * @param db Monitor servers */ -bool set_standalone_master(MariaDBMonitor *handle, MXS_MONITORED_SERVER *db) +bool set_standalone_master(MYSQL_MONITOR *handle, MXS_MONITORED_SERVER *db) { bool rval = false; @@ -1883,7 +2080,7 @@ bool set_standalone_master(MariaDBMonitor *handle, MXS_MONITORED_SERVER *db) return rval; } -bool failover_not_possible(MariaDBMonitor* handle) +bool failover_not_possible(MYSQL_MONITOR* handle) { bool rval = false; @@ -1910,7 +2107,7 @@ bool failover_not_possible(MariaDBMonitor* handle) static void monitorMain(void *arg) { - MariaDBMonitor *handle = (MariaDBMonitor *) arg; + MYSQL_MONITOR *handle = (MYSQL_MONITOR *) arg; MXS_MONITOR* mon = handle->monitor; MXS_MONITORED_SERVER *ptr; bool replication_heartbeat; @@ -2480,7 +2677,7 @@ static int get_row_count(MXS_MONITORED_SERVER *database, const char* query) * @param handle The monitor handle * @param database The number database server */ -static void set_master_heartbeat(MariaDBMonitor *handle, MXS_MONITORED_SERVER *database) +static void set_master_heartbeat(MYSQL_MONITOR *handle, MXS_MONITORED_SERVER *database) { unsigned long id = handle->id; time_t heartbeat; @@ -2601,7 +2798,7 @@ static void set_master_heartbeat(MariaDBMonitor *handle, MXS_MONITORED_SERVER *d */ static void set_slave_heartbeat(MXS_MONITOR* mon, MXS_MONITORED_SERVER *database) { - MariaDBMonitor *handle = (MariaDBMonitor*) mon->handle; + MYSQL_MONITOR *handle = (MYSQL_MONITOR*) mon->handle; unsigned long id = handle->id; time_t heartbeat; char select_heartbeat_query[256] = ""; @@ -2711,7 +2908,7 @@ static void set_slave_heartbeat(MXS_MONITOR* mon, MXS_MONITORED_SERVER *database static MXS_MONITORED_SERVER *get_replication_tree(MXS_MONITOR *mon, int num_servers) { - MariaDBMonitor* handle = (MariaDBMonitor*) mon->handle; + MYSQL_MONITOR* handle = (MYSQL_MONITOR*) mon->handle; MXS_MONITORED_SERVER *ptr; MXS_MONITORED_SERVER *backend; SERVER *current; @@ -3111,7 +3308,7 @@ void check_maxscale_schema_replication(MXS_MONITOR *monitor) * @todo Currently this only works with flat replication topologies and * needs to be moved inside mysqlmon as it is MariaDB specific code. */ -bool mon_process_failover(MariaDBMonitor* monitor, uint32_t failover_timeout, bool* cluster_modified_out) +bool mon_process_failover(MYSQL_MONITOR* monitor, uint32_t failover_timeout, bool* cluster_modified_out) { ss_dassert(*cluster_modified_out == false); bool rval = true; @@ -3192,7 +3389,7 @@ bool mon_process_failover(MariaDBMonitor* monitor, uint32_t failover_timeout, bo * @param server Slave to update * @return Slave server info. NULL on error, or if server is not a slave. */ -static MySqlServerInfo* update_slave_info(MariaDBMonitor* mon, MXS_MONITORED_SERVER* server) +static MySqlServerInfo* update_slave_info(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* server) { MySqlServerInfo* info = get_server_info(mon, server); if (info->slave_status.slave_sql_running && @@ -3256,7 +3453,7 @@ static bool check_replication_settings(const MXS_MONITORED_SERVER* server, MySql * @param err_out Json object for error printing. Can be NULL. * @return True, if given slave is a valid promotion candidate. */ -bool switchover_check_preferred_master(MariaDBMonitor* mon, MXS_MONITORED_SERVER* preferred, json_t** err_out) +bool switchover_check_preferred_master(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* preferred, json_t** err_out) { ss_dassert(preferred); bool rval = true; @@ -3315,7 +3512,7 @@ bool is_candidate_better(const MySqlServerInfo* current_best_info, const MySqlSe * @param err_out json object for error printing. Can be NULL. * @return The found master, or NULL if not found */ -MXS_MONITORED_SERVER* select_new_master(MariaDBMonitor* mon, ServerVector* slaves_out, json_t** err_out) +MXS_MONITORED_SERVER* select_new_master(MYSQL_MONITOR* mon, ServerVector* slaves_out, json_t** err_out) { ss_dassert(slaves_out && slaves_out->size() == 0); /* Select a new master candidate. Selects the one with the latest event in relay log. @@ -3408,7 +3605,7 @@ MXS_MONITORED_SERVER* select_new_master(MariaDBMonitor* mon, ServerVector* slave * @param err_out Json error output * @return True if relay log was processed within time limit, or false if time ran out or an error occurred. */ -bool failover_wait_relay_log(MariaDBMonitor* mon, MXS_MONITORED_SERVER* new_master, int seconds_remaining, +bool failover_wait_relay_log(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* new_master, int seconds_remaining, json_t** err_out) { MySqlServerInfo* master_info = get_server_info(mon, new_master); @@ -3461,7 +3658,7 @@ bool failover_wait_relay_log(MariaDBMonitor* mon, MXS_MONITORED_SERVER* new_mast return rval; } -bool start_external_replication(MariaDBMonitor* mon, MXS_MONITORED_SERVER* new_master, json_t** err_out) +bool start_external_replication(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* new_master, json_t** err_out) { bool rval = false; string change_cmd = generate_change_master_cmd(mon, mon->external_master_host, mon->external_master_port); @@ -3488,7 +3685,7 @@ bool start_external_replication(MariaDBMonitor* mon, MXS_MONITORED_SERVER* new_m * @param err_out json object for error printing. Can be NULL. * @return True if successful */ -bool promote_new_master(MariaDBMonitor* mon, MXS_MONITORED_SERVER* new_master, json_t** err_out) +bool promote_new_master(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* new_master, json_t** err_out) { bool success = false; MXS_NOTICE("Promoting server '%s' to master.", new_master->server->unique_name); @@ -3529,7 +3726,7 @@ bool promote_new_master(MariaDBMonitor* mon, MXS_MONITORED_SERVER* new_master, j * @param master_port Master port * @return Generated query */ -string generate_change_master_cmd(MariaDBMonitor* mon, const string& master_host, int master_port) +string generate_change_master_cmd(MYSQL_MONITOR* mon, const string& master_host, int master_port) { std::stringstream change_cmd; change_cmd << "CHANGE MASTER TO MASTER_HOST = '" << master_host << "', "; @@ -3583,7 +3780,7 @@ bool redirect_one_slave(MXS_MONITORED_SERVER* slave, const char* change_cmd) * @param redirected_slaves A vector where to insert successfully redirected slaves. Can be NULL. * @return The number of slaves successfully redirected. */ -int redirect_slaves(MariaDBMonitor* mon, MXS_MONITORED_SERVER* new_master, const ServerVector& slaves, +int redirect_slaves(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* new_master, const ServerVector& slaves, ServerVector* redirected_slaves = NULL) { MXS_NOTICE("Redirecting slaves to new master."); @@ -3641,7 +3838,7 @@ void print_redirect_errors(MXS_MONITORED_SERVER* first_server, const ServerVecto * @param err_out Json output * @return True if successful */ -static bool do_failover(MariaDBMonitor* mon, json_t** err_out) +static bool do_failover(MYSQL_MONITOR* mon, json_t** err_out) { // Topology has already been tested to be simple. if (mon->master_gtid_domain < 0) @@ -3796,7 +3993,7 @@ static bool update_replication_settings(MXS_MONITORED_SERVER *database, MySqlSer * @param info Server info structure for saving result * @return True if successful */ -static bool update_gtids(MariaDBMonitor* mon, MXS_MONITORED_SERVER *database, MySqlServerInfo* info) +static bool update_gtids(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER *database, MySqlServerInfo* info) { StringVector row; const char query[] = "SELECT @@gtid_current_pos, @@gtid_binlog_pos;"; @@ -3826,7 +4023,7 @@ static bool update_gtids(MariaDBMonitor* mon, MXS_MONITORED_SERVER *database, My * @param err_out json object for error printing. Can be NULL. * @return True if successful. */ -static bool switchover_demote_master(MariaDBMonitor* mon, +static bool switchover_demote_master(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* current_master, MySqlServerInfo* info, json_t** err_out) @@ -4029,7 +4226,7 @@ static bool switchover_wait_slaves_catchup(const ServerVector& slaves, const Gti * @return True if commands were accepted. This does not guarantee that replication proceeds * successfully. */ -static bool switchover_start_slave(MariaDBMonitor* mon, MXS_MONITORED_SERVER* old_master, SERVER* new_master) +static bool switchover_start_slave(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* old_master, SERVER* new_master) { bool rval = false; std::string change_cmd = generate_change_master_cmd(mon, new_master->name, new_master->port); @@ -4080,7 +4277,7 @@ static string get_connection_errors(const ServerVector& servers) * @param seconds_remaining How long can we wait * @return True, if at least one slave got the new event within the time limit */ -static bool wait_cluster_stabilization(MariaDBMonitor* mon, +static bool wait_cluster_stabilization(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* new_master, const ServerVector& slaves, int seconds_remaining) @@ -4174,7 +4371,7 @@ static bool wait_cluster_stabilization(MariaDBMonitor* mon, * @return True if successful. If false, the cluster can be in various situations depending on which step * failed. In practice, manual intervention is usually required on failure. */ -static bool do_switchover(MariaDBMonitor* mon, MXS_MONITORED_SERVER* current_master, +static bool do_switchover(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* current_master, MXS_MONITORED_SERVER* new_master, json_t** err_out) { MXS_MONITORED_SERVER* demotion_target = current_master ? current_master : mon->master; @@ -4371,7 +4568,7 @@ static void read_server_variables(MXS_MONITORED_SERVER* database, MySqlServerInf * @param master_info Master info * @return True if slave can replicate from master */ -static bool can_replicate_from(MariaDBMonitor* mon, +static bool can_replicate_from(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* slave, MySqlServerInfo* slave_info, MXS_MONITORED_SERVER* master, MySqlServerInfo* master_info) { @@ -4402,7 +4599,7 @@ static bool can_replicate_from(MariaDBMonitor* mon, * @param master_info Master server info * @return True, if server is a rejoin suspect. */ -static bool server_is_rejoin_suspect(MariaDBMonitor* mon, MXS_MONITORED_SERVER* server, +static bool server_is_rejoin_suspect(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* server, MySqlServerInfo* master_info) { bool is_suspect = false; @@ -4446,7 +4643,7 @@ static bool server_is_rejoin_suspect(MariaDBMonitor* mon, MXS_MONITORED_SERVER* * @return False, if there were possible rejoinable servers but communications error to master server * prevented final checks. */ -static bool get_joinable_servers(MariaDBMonitor* mon, ServerVector* output) +static bool get_joinable_servers(MYSQL_MONITOR* mon, ServerVector* output) { ss_dassert(output); MXS_MONITORED_SERVER* master = mon->master; @@ -4497,7 +4694,7 @@ static bool get_joinable_servers(MariaDBMonitor* mon, ServerVector* output) * @param joinable_servers Which servers to rejoin * @return The number of servers successfully rejoined */ -static uint32_t do_rejoin(MariaDBMonitor* mon, const ServerVector& joinable_servers) +static uint32_t do_rejoin(MYSQL_MONITOR* mon, const ServerVector& joinable_servers) { SERVER* master = mon->master->server; uint32_t servers_joined = 0; @@ -4567,7 +4764,7 @@ static bool join_cluster(MXS_MONITORED_SERVER* server, const char* change_cmd) * @param mon Cluster monitor * @param setting_name Setting to disable */ -static void disable_setting(MariaDBMonitor* mon, const char* setting) +static void disable_setting(MYSQL_MONITOR* mon, const char* setting) { MXS_CONFIG_PARAMETER p = {}; p.name = const_cast(setting); @@ -4581,7 +4778,7 @@ static void disable_setting(MariaDBMonitor* mon, const char* setting) * @param mon Cluster monitor * @return True, if cluster can be joined */ -static bool cluster_can_be_joined(MariaDBMonitor* mon) +static bool cluster_can_be_joined(MYSQL_MONITOR* mon) { return (mon->master != NULL && SERVER_IS_MASTER(mon->master->server) && mon->master_gtid_domain >= 0); } diff --git a/server/modules/monitor/mariadbmon/mariadbmon.hh b/server/modules/monitor/mariadbmon/mariadbmon.hh index 18cf3cc60..4a4bca451 100644 --- a/server/modules/monitor/mariadbmon/mariadbmon.hh +++ b/server/modules/monitor/mariadbmon/mariadbmon.hh @@ -1,5 +1,6 @@ #pragma once - +#ifndef _MARIADBMON_H +#define _MARIADBMON_H /* * Copyright (c) 2016 MariaDB Corporation Ab * @@ -14,41 +15,29 @@ */ /** - * @file mariadbmon.hh - The MySQL monitor + * @file mysqlmon.hh - The MySQL monitor */ #include #include -#include -#include +#include +#include +#include #include #include -#include "utilities.hh" - -extern const int PORT_UNKNOWN; -extern const int64_t SERVER_ID_UNKNOWN; - -typedef std::tr1::unordered_map ServerInfoMap; -typedef std::vector ServerVector; - +using std::string; // MySQL Monitor module instance -class MariaDBMonitor +class MYSQL_MONITOR { -private: - MariaDBMonitor(const MariaDBMonitor&); - MariaDBMonitor& operator = (const MariaDBMonitor&); public: - MariaDBMonitor(); - ~MariaDBMonitor(); - MXS_MONITOR* monitor; /**< Generic monitor object */ THREAD thread; /**< Monitor thread */ int shutdown; /**< Flag to shutdown the monitor thread */ int status; /**< Monitor status */ MXS_MONITORED_SERVER *master; /**< Master server for MySQL Master/Slave replication */ - ServerInfoMap server_info; /**< Contains server specific information */ + HASHTABLE *server_info; /**< Contains server specific information */ bool warn_set_standalone_master; /**< Log a warning when setting standalone master */ unsigned long id; /**< Monitor ID */ @@ -78,10 +67,13 @@ public: bool auto_failover; /**< If automatic master failover is enabled */ bool auto_rejoin; /**< Attempt to start slave replication on standalone servers or servers * replicating from the wrong master automatically. */ - ServerVector excluded_servers; /**< Servers banned for master promotion during auto-failover. */ + MXS_MONITORED_SERVER** excluded_servers; /**< Servers banned for master promotion during auto-failover. */ + int n_excluded; /**< Number of excluded servers */ // Other settings string script; /**< Script to call when state changes occur on servers */ uint64_t events; /**< enabled events */ bool allow_cluster_recovery; /**< Allow failed servers to rejoin the cluster */ }; + +#endif diff --git a/server/modules/monitor/mariadbmon/utilities.cc b/server/modules/monitor/mariadbmon/utilities.cc deleted file mode 100644 index 7fc22dcd3..000000000 --- a/server/modules/monitor/mariadbmon/utilities.cc +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2018 MariaDB Corporation Ab - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file and at www.mariadb.com/bsl11. - * - * Change Date: 2020-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2 or later of the General - * Public License. - */ - -#include "utilities.hh" - -#include -#include -#include -#include -#include - -#include "mariadbmon.hh" - -Gtid::Gtid() - : domain(0) - , server_id(SERVER_ID_UNKNOWN) - , sequence(0) -{} - -Gtid::Gtid(const char* str, int64_t search_domain) - : domain(0) - , server_id(SERVER_ID_UNKNOWN) - , sequence(0) -{ - // Autoselect only allowed with one triplet - ss_dassert(search_domain >= 0 || strchr(str, ',') == NULL); - parse_triplet(str); - if (search_domain >= 0 && domain != search_domain) - { - // Search for the correct triplet. - bool found = false; - for (const char* next_triplet = strchr(str, ','); - next_triplet != NULL && !found; - next_triplet = strchr(next_triplet, ',')) - { - parse_triplet(++next_triplet); - if (domain == search_domain) - { - found = true; - } - } - ss_dassert(found); - } -} - -bool Gtid::operator == (const Gtid& rhs) const -{ - return domain == rhs.domain && - server_id != SERVER_ID_UNKNOWN && server_id == rhs.server_id && - sequence == rhs.sequence; -} - -string Gtid::to_string() const -{ - std::stringstream ss; - if (server_id != SERVER_ID_UNKNOWN) - { - ss << domain << "-" << server_id << "-" << sequence; - } - return ss.str(); -} - -void Gtid::parse_triplet(const char* str) -{ - ss_debug(int rv = ) sscanf(str, "%" PRIu32 "-%" PRId64 "-%" PRIu64, &domain, &server_id, &sequence); - ss_dassert(rv == 3); -} - -SlaveStatusInfo::SlaveStatusInfo() - : master_server_id(SERVER_ID_UNKNOWN) - , master_port(0) - , slave_io_running(false) - , slave_sql_running(false) - , read_master_log_pos(0) -{} - -MySqlServerInfo::MySqlServerInfo() - : server_id(SERVER_ID_UNKNOWN) - , group(0) - , read_only(false) - , slave_configured(false) - , binlog_relay(false) - , n_slaves_configured(0) - , n_slaves_running(0) - , slave_heartbeats(0) - , heartbeat_period(0) - , latest_event(0) - , gtid_domain_id(-1) - , version(MYSQL_SERVER_VERSION_51) -{} - -int64_t MySqlServerInfo::relay_log_events() -{ - if (slave_status.gtid_io_pos.server_id != SERVER_ID_UNKNOWN && - gtid_current_pos.server_id != SERVER_ID_UNKNOWN && - slave_status.gtid_io_pos.domain == gtid_current_pos.domain && - slave_status.gtid_io_pos.sequence >= gtid_current_pos.sequence) - { - return slave_status.gtid_io_pos.sequence - gtid_current_pos.sequence; - } - return -1; -} diff --git a/server/modules/monitor/mariadbmon/utilities.hh b/server/modules/monitor/mariadbmon/utilities.hh deleted file mode 100644 index 0db5b5062..000000000 --- a/server/modules/monitor/mariadbmon/utilities.hh +++ /dev/null @@ -1,127 +0,0 @@ -#pragma once - -/* - * Copyright (c) 2018 MariaDB Corporation Ab - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file and at www.mariadb.com/bsl11. - * - * Change Date: 2020-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2 or later of the General - * Public License. - */ - -#include -#include - -using std::string; - -enum mysql_server_version -{ - MYSQL_SERVER_VERSION_100, - MYSQL_SERVER_VERSION_55, - MYSQL_SERVER_VERSION_51 -}; - -class Gtid -{ -public: - uint32_t domain; - int64_t server_id; // Is actually 32bit unsigned. 0 is only used by server versions <= 10.1 - uint64_t sequence; - Gtid(); - - /** - * Parse a Gtid-triplet from a string. In case of a multi-triplet value, only the triplet with - * the given domain is returned. - * - * @param str Gtid string - * @param search_domain The Gtid domain whose triplet should be returned. Negative domain stands for - * autoselect, which is only allowed when the string contains one triplet. - */ - Gtid(const char* str, int64_t search_domain = -1); - - bool operator == (const Gtid& rhs) const; - - string to_string() const; -private: - void parse_triplet(const char* str); -}; - -// Contains data returned by one row of SHOW ALL SLAVES STATUS -class SlaveStatusInfo -{ -public: - int64_t master_server_id; /**< The master's server_id value. Valid ids are 32bit unsigned. -1 is - * unread/error. */ - string master_host; /**< Master server host name. */ - int master_port; /**< Master server port. */ - bool slave_io_running; /**< Whether the slave I/O thread is running and connected. */ - bool slave_sql_running; /**< Whether or not the SQL thread is running. */ - string master_log_file; /**< Name of the master binary log file that the I/O thread is currently - * reading from. */ - uint64_t read_master_log_pos; /**< Position up to which the I/O thread has read in the current master - * binary log file. */ - Gtid gtid_io_pos; /**< Gtid I/O position of the slave thread. Only shows the triplet with - * the current master domain. */ - string last_error; /**< Last IO or SQL error encountered. */ - - SlaveStatusInfo(); -}; - -// This class groups some miscellaneous replication related settings together. -class ReplicationSettings -{ -public: - bool gtid_strict_mode; /**< Enable additional checks for replication */ - bool log_bin; /**< Is binary logging enabled */ - bool log_slave_updates; /**< Does the slave log replicated events to binlog */ - ReplicationSettings() - : gtid_strict_mode(false) - , log_bin(false) - , log_slave_updates(false) - {} -}; - -/** - * Monitor specific information about a server - * - * Note: These are initialized in @c init_server_info - */ -class MySqlServerInfo -{ -public: - int64_t server_id; /**< Value of @@server_id. Valid values are 32bit unsigned. */ - int group; /**< Multi-master group where this server belongs, - * 0 for servers not in groups */ - bool read_only; /**< Value of @@read_only */ - bool slave_configured; /**< Whether SHOW SLAVE STATUS returned rows */ - bool binlog_relay; /** Server is a Binlog Relay */ - int n_slaves_configured; /**< Number of configured slave connections*/ - int n_slaves_running; /**< Number of running slave connections */ - int slave_heartbeats; /**< Number of received heartbeats */ - double heartbeat_period; /**< The time interval between heartbeats */ - time_t latest_event; /**< Time when latest event was received from the master */ - int64_t gtid_domain_id; /**< The value of gtid_domain_id, the domain which is used for - * new non-replicated events. */ - Gtid gtid_current_pos; /**< Gtid of latest event. Only shows the triplet - * with the current master domain. */ - Gtid gtid_binlog_pos; /**< Gtid of latest event written to binlog. Only shows - * the triplet with the current master domain. */ - SlaveStatusInfo slave_status; /**< Data returned from SHOW SLAVE STATUS */ - ReplicationSettings rpl_settings; /**< Miscellaneous replication related settings */ - mysql_server_version version; /**< Server version, 10.X, 5.5 or 5.1 */ - - MySqlServerInfo(); - - /** - * Calculate how many events are left in the relay log. If gtid_current_pos is ahead of Gtid_IO_Pos, - * or a server_id is unknown, an error value is returned. - * - * @return Number of events in relay log according to latest queried info. A negative value signifies - * an error in the gtid-values. - */ - int64_t relay_log_events(); -}; From 236e906d88d1abf74dc162066b2d7e5090c66a1d Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Sat, 24 Feb 2018 15:37:50 +0200 Subject: [PATCH 09/18] Revert "Turn MariaDB Monitor struct to class with public fields" This reverts commit cb6f70119d9857b277306e9af5881fe29c574a32. --- server/core/monitor.cc | 2 +- .../modules/monitor/mariadbmon/mariadbmon.cc | 59 +++++++++-------- .../modules/monitor/mariadbmon/mariadbmon.hh | 64 ++++++++++--------- 3 files changed, 65 insertions(+), 60 deletions(-) diff --git a/server/core/monitor.cc b/server/core/monitor.cc index ab7e9f167..113f56b80 100644 --- a/server/core/monitor.cc +++ b/server/core/monitor.cc @@ -1773,7 +1773,7 @@ void mon_process_state_changes(MXS_MONITOR *monitor, const char *script, uint64_ master_up = true; } - if (script && *script && (events & event)) + if (script && (events & event)) { monitor_launch_script(monitor, ptr, script, monitor->script_timeout); } diff --git a/server/modules/monitor/mariadbmon/mariadbmon.cc b/server/modules/monitor/mariadbmon/mariadbmon.cc index ee10f4347..6ba45add6 100644 --- a/server/modules/monitor/mariadbmon/mariadbmon.cc +++ b/server/modules/monitor/mariadbmon/mariadbmon.cc @@ -31,7 +31,6 @@ #include #include #include -#include #include // TODO: For monitorAddParameters #include "../../../core/internal/monitor.h" @@ -990,10 +989,8 @@ static bool set_replication_credentials(MYSQL_MONITOR *handle, const MXS_CONFIG_ if (*repl_user && *repl_pw) { - handle->replication_user = repl_user; - char* decrypted = decrypt_password(repl_pw); - handle->replication_password = decrypted; - MXS_FREE(decrypted); + handle->replication_user = MXS_STRDUP_A(repl_user); + handle->replication_password = decrypt_password(repl_pw); rval = true; } @@ -1036,22 +1033,22 @@ startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params) if (handle) { handle->shutdown = 0; - handle->script.clear(); - handle->replication_user.clear(); - handle->replication_password.clear(); + MXS_FREE(handle->script); + MXS_FREE(handle->replication_user); + MXS_FREE(handle->replication_password); MXS_FREE(handle->excluded_servers); handle->excluded_servers = NULL; handle->n_excluded = 0; } else { - handle = new MYSQL_MONITOR; + handle = (MYSQL_MONITOR *) MXS_MALLOC(sizeof(MYSQL_MONITOR)); HASHTABLE *server_info = hashtable_alloc(MAX_NUM_SLAVES, hashtable_item_strhash, hashtable_item_strcmp); - if (server_info == NULL) + if (handle == NULL || server_info == NULL) { - delete handle; + MXS_FREE(handle); hashtable_free(server_info); return NULL; } @@ -1063,6 +1060,7 @@ startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params) handle->id = config_get_global_options()->id; handle->warn_set_standalone_master = true; handle->master_gtid_domain = -1; + handle->external_master_host[0] = '\0'; handle->external_master_port = PORT_UNKNOWN; handle->monitor = monitor; } @@ -1079,14 +1077,15 @@ startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params) handle->failcount = config_get_integer(params, CN_FAILCOUNT); handle->allow_cluster_recovery = config_get_bool(params, "allow_cluster_recovery"); handle->mysql51_replication = config_get_bool(params, "mysql51_replication"); - handle->script = config_get_string(params, "script"); + handle->script = config_copy_string(params, "script"); handle->events = config_get_enum(params, "events", mxs_monitor_event_enum_values); + handle->auto_failover = config_get_bool(params, CN_AUTO_FAILOVER); handle->failover_timeout = config_get_integer(params, CN_FAILOVER_TIMEOUT); handle->switchover_timeout = config_get_integer(params, CN_SWITCHOVER_TIMEOUT); - handle->auto_failover = config_get_bool(params, CN_AUTO_FAILOVER); - handle->auto_rejoin = config_get_bool(params, CN_AUTO_REJOIN); handle->verify_master_failure = config_get_bool(params, CN_VERIFY_MASTER_FAILURE); handle->master_failure_timeout = config_get_integer(params, CN_MASTER_FAILURE_TIMEOUT); + handle->auto_rejoin = config_get_bool(params, CN_AUTO_REJOIN); + handle->excluded_servers = NULL; handle->n_excluded = mon_config_get_servers(params, CN_NO_PROMOTE_SERVERS, monitor, &handle->excluded_servers); @@ -1115,8 +1114,9 @@ startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params) if (error) { hashtable_free(handle->server_info); + MXS_FREE(handle->script); MXS_FREE(handle->excluded_servers); - delete handle; + MXS_FREE(handle); handle = NULL; } else @@ -1127,7 +1127,8 @@ startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params) { MXS_ERROR("Failed to start monitor thread for monitor '%s'.", monitor->name); hashtable_free(handle->server_info); - delete handle; + MXS_FREE(handle->script); + MXS_FREE(handle); handle = NULL; } } @@ -1206,7 +1207,8 @@ static void diagnostics(DCB *dcb, const MXS_MONITOR *mon) dcb_printf(dcb, "Switchover timeout: %u\n", handle->switchover_timeout); dcb_printf(dcb, "Automatic rejoin: %s\n", handle->auto_rejoin ? "Enabled" : "Disabled"); dcb_printf(dcb, "MaxScale monitor ID: %lu\n", handle->id); - dcb_printf(dcb, "Detect replication lag: %s\n", (handle->replicationHeartbeat) ? "Enabled" : "Disabled"); + dcb_printf(dcb, "Detect replication lag: %s\n", (handle->replicationHeartbeat == 1) ? + "Enabled" : "Disabled"); dcb_printf(dcb, "Detect stale master: %s\n", (handle->detectStaleMaster == 1) ? "Enabled" : "Disabled"); if (handle->n_excluded > 0) @@ -1280,9 +1282,9 @@ static json_t* diagnostics_json(const MXS_MONITOR *mon) json_object_set_new(rval, CN_SWITCHOVER_TIMEOUT, json_integer(handle->switchover_timeout)); json_object_set_new(rval, CN_AUTO_REJOIN, json_boolean(handle->auto_rejoin)); - if (!handle->script.empty()) + if (handle->script) { - json_object_set_new(rval, "script", json_string(handle->script.c_str())); + json_object_set_new(rval, "script", json_string(handle->script)); } if (handle->n_excluded > 0) { @@ -2110,7 +2112,7 @@ monitorMain(void *arg) MYSQL_MONITOR *handle = (MYSQL_MONITOR *) arg; MXS_MONITOR* mon = handle->monitor; MXS_MONITORED_SERVER *ptr; - bool replication_heartbeat; + int replication_heartbeat; bool detect_stale_master; int num_servers = 0; MXS_MONITORED_SERVER *root_master = NULL; @@ -2283,20 +2285,21 @@ monitorMain(void *arg) if (master_info->slave_status.master_host != handle->external_master_host || master_info->slave_status.master_port != handle->external_master_port) { - const string new_ext_host = master_info->slave_status.master_host; + const char* new_ext_host = master_info->slave_status.master_host.c_str(); const int new_ext_port = master_info->slave_status.master_port; if (handle->external_master_port == PORT_UNKNOWN) { MXS_NOTICE("Cluster master server is replicating from an external master: %s:%d", - new_ext_host.c_str(), new_ext_port); + new_ext_host, new_ext_port); } else { MXS_NOTICE("The external master of the cluster has changed: %s:%d -> %s:%d.", - handle->external_master_host.c_str(), handle->external_master_port, - new_ext_host.c_str(), new_ext_port); + handle->external_master_host, handle->external_master_port, + new_ext_host, new_ext_port); } - handle->external_master_host = new_ext_host; + snprintf(handle->external_master_host, sizeof(handle->external_master_host), + "%s", new_ext_host); handle->external_master_port = new_ext_port; } } @@ -2306,7 +2309,7 @@ monitorMain(void *arg) { MXS_NOTICE("Cluster lost the external master."); } - handle->external_master_host.clear(); + handle->external_master_host[0] = '\0'; handle->external_master_port = PORT_UNKNOWN; } } @@ -2467,7 +2470,7 @@ monitorMain(void *arg) * After updating the status of all servers, check if monitor events * need to be launched. */ - mon_process_state_changes(mon, handle->script.c_str(), handle->events); + mon_process_state_changes(mon, handle->script, handle->events); bool failover_performed = false; // Has an automatic failover been performed this loop? if (handle->auto_failover) @@ -3666,7 +3669,7 @@ bool start_external_replication(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* new_ma mxs_mysql_query(new_master->con, "START SLAVE;") == 0) { MXS_NOTICE("New master starting replication from external master %s:%d.", - mon->external_master_host.c_str(), mon->external_master_port); + mon->external_master_host, mon->external_master_port); rval = true; } else diff --git a/server/modules/monitor/mariadbmon/mariadbmon.hh b/server/modules/monitor/mariadbmon/mariadbmon.hh index 4a4bca451..5c82192e6 100644 --- a/server/modules/monitor/mariadbmon/mariadbmon.hh +++ b/server/modules/monitor/mariadbmon/mariadbmon.hh @@ -19,61 +19,63 @@ */ #include -#include +#include +#include +#include +#include +#include #include #include #include +#include +#include #include +#include +#include #include -using std::string; // MySQL Monitor module instance -class MYSQL_MONITOR +struct MYSQL_MONITOR { -public: - MXS_MONITOR* monitor; /**< Generic monitor object */ THREAD thread; /**< Monitor thread */ int shutdown; /**< Flag to shutdown the monitor thread */ int status; /**< Monitor status */ - MXS_MONITORED_SERVER *master; /**< Master server for MySQL Master/Slave replication */ - HASHTABLE *server_info; /**< Contains server specific information */ - bool warn_set_standalone_master; /**< Log a warning when setting standalone master */ unsigned long id; /**< Monitor ID */ - - // Values updated by monitor - int64_t master_gtid_domain; /**< Gtid domain currently used by the master */ - string external_master_host; /**< External master host, for fail/switchover */ - int external_master_port; /**< External master port */ - - // Replication topology detection settings - bool mysql51_replication; /**< Use MySQL 5.1 replication */ + int replicationHeartbeat; /**< Monitor flag for MySQL replication heartbeat */ bool detectStaleMaster; /**< Monitor flag for MySQL replication Stale Master detection */ bool detectStaleSlave; /**< Monitor flag for MySQL replication Stale Master detection */ bool multimaster; /**< Detect and handle multi-master topologies */ bool ignore_external_masters; /**< Ignore masters outside of the monitor configuration */ + int disableMasterFailback; /**< Monitor flag for Galera Cluster Master failback */ + int availableWhenDonor; /**< Monitor flag for Galera Cluster Donor availability */ + int disableMasterRoleSetting; /**< Monitor flag to disable setting master role */ + bool mysql51_replication; /**< Use MySQL 5.1 replication */ + MXS_MONITORED_SERVER *master; /**< Master server for MySQL Master/Slave replication */ + char* script; /**< Script to call when state changes occur on servers */ + uint64_t events; /**< enabled events */ + HASHTABLE *server_info; /**< Contains server specific information */ bool detect_standalone_master; /**< If standalone master are detected */ - bool replicationHeartbeat; /**< Monitor flag for MySQL replication heartbeat */ - - // Failover, switchover and rejoin settings - string replication_user; /**< Replication user for CHANGE MASTER TO-commands */ - string replication_password; /**< Replication password for CHANGE MASTER TO-commands */ - int failcount; /**< How many monitoring cycles master must be down before auto-failover - * begins */ + int failcount; /**< How many monitoring cycles servers must be + down before failover is initiated */ + bool allow_cluster_recovery; /**< Allow failed servers to rejoin the cluster */ + bool warn_set_standalone_master; /**< Log a warning when setting standalone master */ + bool auto_failover; /**< If automatic master failover is enabled */ uint32_t failover_timeout; /**< Timeout in seconds for the master failover */ uint32_t switchover_timeout; /**< Timeout in seconds for the master switchover */ + char* replication_user; /**< Replication user for failover */ + char* replication_password; /**< Replication password for failover*/ bool verify_master_failure; /**< Whether master failure is verified via slaves */ - int master_failure_timeout; /**< Master failure verification (via slaves) time in seconds */ - bool auto_failover; /**< If automatic master failover is enabled */ + int master_failure_timeout; /**< Time in seconds to wait before doing failover */ + int64_t master_gtid_domain; /**< Gtid domain currently used by the master */ + char external_master_host[MAX_SERVER_ADDRESS_LEN]; /**< External master host, for fail/switchover */ + int external_master_port; /**< External master port */ bool auto_rejoin; /**< Attempt to start slave replication on standalone servers or servers - * replicating from the wrong master automatically. */ - MXS_MONITORED_SERVER** excluded_servers; /**< Servers banned for master promotion during auto-failover. */ + replicating from the wrong master. */ int n_excluded; /**< Number of excluded servers */ + MXS_MONITORED_SERVER** excluded_servers; /**< Servers banned for master promotion during auto-failover. */ - // Other settings - string script; /**< Script to call when state changes occur on servers */ - uint64_t events; /**< enabled events */ - bool allow_cluster_recovery; /**< Allow failed servers to rejoin the cluster */ + MXS_MONITOR* monitor; }; #endif From 0bbf0246f96659341cf06844f2e14d8501bf2c65 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Sat, 24 Feb 2018 15:40:21 +0200 Subject: [PATCH 10/18] Revert "Compile mariadbmon.h as C++" This reverts commit 60d57aee61d96832aeec1b8a61d36803c38ca77c. --- .../modules/monitor/mariadbmon/mariadbmon.cc | 2 +- .../{mariadbmon.hh => mariadbmon.h} | 33 +++++++++++-------- 2 files changed, 20 insertions(+), 15 deletions(-) rename server/modules/monitor/mariadbmon/{mariadbmon.hh => mariadbmon.h} (95%) diff --git a/server/modules/monitor/mariadbmon/mariadbmon.cc b/server/modules/monitor/mariadbmon/mariadbmon.cc index 6ba45add6..a0597f153 100644 --- a/server/modules/monitor/mariadbmon/mariadbmon.cc +++ b/server/modules/monitor/mariadbmon/mariadbmon.cc @@ -17,7 +17,7 @@ #define MXS_MODULE_NAME "mariadbmon" -#include "mariadbmon.hh" +#include "mariadbmon.h" #include #include #include diff --git a/server/modules/monitor/mariadbmon/mariadbmon.hh b/server/modules/monitor/mariadbmon/mariadbmon.h similarity index 95% rename from server/modules/monitor/mariadbmon/mariadbmon.hh rename to server/modules/monitor/mariadbmon/mariadbmon.h index 5c82192e6..59f71e9a4 100644 --- a/server/modules/monitor/mariadbmon/mariadbmon.hh +++ b/server/modules/monitor/mariadbmon/mariadbmon.h @@ -15,28 +15,31 @@ */ /** - * @file mysqlmon.hh - The MySQL monitor + * @file mysqlmon.h - The MySQL monitor */ -#include +#include #include #include #include -#include -#include - -#include -#include -#include -#include -#include #include #include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include -// MySQL Monitor module instance -struct MYSQL_MONITOR +MXS_BEGIN_DECLS + +/** + * The handle for an instance of a MySQL Monitor module + */ +typedef struct { THREAD thread; /**< Monitor thread */ int shutdown; /**< Flag to shutdown the monitor thread */ @@ -76,6 +79,8 @@ struct MYSQL_MONITOR MXS_MONITORED_SERVER** excluded_servers; /**< Servers banned for master promotion during auto-failover. */ MXS_MONITOR* monitor; -}; +} MYSQL_MONITOR; + +MXS_END_DECLS #endif From b67ab83486452a121b3bdb738a851dbf7e366932 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Sat, 24 Feb 2018 15:43:15 +0200 Subject: [PATCH 11/18] Revert "Use dedicated header in NDBClusterMon" This reverts commit b9d80f6061d6b536d7a15febf0367e5f6dba0e84. --- .../modules/monitor/mariadbmon/mariadbmon.cc | 2 +- .../{mariadbmon/mariadbmon.h => mysqlmon.h} | 4 +- .../monitor/ndbclustermon/ndbclustermon.c | 10 ++--- .../monitor/ndbclustermon/ndbclustermon.h | 39 ++++++++++++------- 4 files changed, 33 insertions(+), 22 deletions(-) rename server/modules/monitor/{mariadbmon/mariadbmon.h => mysqlmon.h} (98%) diff --git a/server/modules/monitor/mariadbmon/mariadbmon.cc b/server/modules/monitor/mariadbmon/mariadbmon.cc index a0597f153..d73bf4603 100644 --- a/server/modules/monitor/mariadbmon/mariadbmon.cc +++ b/server/modules/monitor/mariadbmon/mariadbmon.cc @@ -17,7 +17,7 @@ #define MXS_MODULE_NAME "mariadbmon" -#include "mariadbmon.h" +#include "../mysqlmon.h" #include #include #include diff --git a/server/modules/monitor/mariadbmon/mariadbmon.h b/server/modules/monitor/mysqlmon.h similarity index 98% rename from server/modules/monitor/mariadbmon/mariadbmon.h rename to server/modules/monitor/mysqlmon.h index 59f71e9a4..11ae6b591 100644 --- a/server/modules/monitor/mariadbmon/mariadbmon.h +++ b/server/modules/monitor/mysqlmon.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _MARIADBMON_H -#define _MARIADBMON_H +#ifndef _MYSQLMON_H +#define _MYSQLMON_H /* * Copyright (c) 2016 MariaDB Corporation Ab * diff --git a/server/modules/monitor/ndbclustermon/ndbclustermon.c b/server/modules/monitor/ndbclustermon/ndbclustermon.c index b31e995e2..2f305716a 100644 --- a/server/modules/monitor/ndbclustermon/ndbclustermon.c +++ b/server/modules/monitor/ndbclustermon/ndbclustermon.c @@ -17,7 +17,7 @@ #define MXS_MODULE_NAME "ndbclustermon" -#include "ndbclustermon.h" +#include "../mysqlmon.h" #include #include @@ -103,7 +103,7 @@ MXS_MODULE* MXS_CREATE_MODULE() static void * startMonitor(MXS_MONITOR *mon, const MXS_CONFIG_PARAMETER *params) { - NDBC_MONITOR *handle = mon->handle; + MYSQL_MONITOR *handle = mon->handle; bool have_events = false, script_error = false; if (handle != NULL) @@ -113,7 +113,7 @@ startMonitor(MXS_MONITOR *mon, const MXS_CONFIG_PARAMETER *params) } else { - if ((handle = (NDBC_MONITOR *) MXS_MALLOC(sizeof(NDBC_MONITOR))) == NULL) + if ((handle = (MYSQL_MONITOR *) MXS_MALLOC(sizeof(MYSQL_MONITOR))) == NULL) { return NULL; } @@ -154,7 +154,7 @@ startMonitor(MXS_MONITOR *mon, const MXS_CONFIG_PARAMETER *params) static void stopMonitor(MXS_MONITOR *mon) { - NDBC_MONITOR *handle = (NDBC_MONITOR *) mon->handle; + MYSQL_MONITOR *handle = (MYSQL_MONITOR *) mon->handle; handle->shutdown = 1; thread_wait(handle->thread); @@ -306,7 +306,7 @@ monitorDatabase(MXS_MONITORED_SERVER *database, char *defaultUser, char *default static void monitorMain(void *arg) { - NDBC_MONITOR *handle = (NDBC_MONITOR*)arg; + MYSQL_MONITOR *handle = (MYSQL_MONITOR*)arg; MXS_MONITOR* mon = handle->monitor; MXS_MONITORED_SERVER *ptr; size_t nrounds = 0; diff --git a/server/modules/monitor/ndbclustermon/ndbclustermon.h b/server/modules/monitor/ndbclustermon/ndbclustermon.h index 1d21d1aec..d01064d98 100644 --- a/server/modules/monitor/ndbclustermon/ndbclustermon.h +++ b/server/modules/monitor/ndbclustermon/ndbclustermon.h @@ -1,6 +1,5 @@ -#pragma once -#ifndef _NDBCMON_H -#define _NDBCMON_H +#ifndef _MYSQLMON_H +#define _MYSQLMON_H /* * Copyright (c) 2016 MariaDB Corporation Ab * @@ -14,27 +13,39 @@ * Public License. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + /** * @file ndbclustermon.h - The NDB Cluster monitor * */ -#include -#include -#include - -// The handle for an instance of a NDB Cluster Monitor module +/** + * The handle for an instance of a NDB Cluster Monitor module + */ typedef struct { - THREAD thread; /**< Monitor thread */ SPINLOCK lock; /**< The monitor spinlock */ - unsigned long id; /**< Monitor ID */ - uint64_t events; /*< enabled events */ + pthread_t tid; /**< id of monitor thread */ int shutdown; /**< Flag to shutdown the monitor thread */ int status; /**< Monitor status */ - MXS_MONITORED_SERVER *master; /**< Master server for MySQL Master/Slave replication */ + unsigned long id; /**< Monitor ID */ + MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */ char* script; /*< Script to call when state changes occur on servers */ - MXS_MONITOR* monitor; -} NDBC_MONITOR; + bool events[MAX_MONITOR_EVENT]; /*< enabled events */ +} MYSQL_MONITOR; #endif From 351f574e01ab1dfab5a1ea506f21d1c1163ec35b Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Sat, 24 Feb 2018 15:54:27 +0200 Subject: [PATCH 12/18] Update version number --- VERSION22.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION22.cmake b/VERSION22.cmake index ff9093763..75afea86f 100644 --- a/VERSION22.cmake +++ b/VERSION22.cmake @@ -5,7 +5,7 @@ set(MAXSCALE_VERSION_MAJOR "2" CACHE STRING "Major version") set(MAXSCALE_VERSION_MINOR "2" CACHE STRING "Minor version") -set(MAXSCALE_VERSION_PATCH "2" CACHE STRING "Patch version") +set(MAXSCALE_VERSION_PATCH "3" CACHE STRING "Patch version") # This should only be incremented if a package is rebuilt set(MAXSCALE_BUILD_NUMBER 1 CACHE STRING "Release number") From 9b36ba5e0a465b8f57dcacd4cfcdad32e1854aa8 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Sat, 24 Feb 2018 17:02:51 +0200 Subject: [PATCH 13/18] MXS-1677: Don't assume all queries are in text form After a temporary table is created, readwritesplit will check whether a query drops or targets that temporary table. The check for query type was missing from the table dropping part of the code. The temporary table read part was checking that the query is a text form query. Added a debug assertion to the query parsing function in qc_sqlite to catch this type of interface misuse. --- query_classifier/qc_sqlite/qc_sqlite.cc | 1 + server/modules/routing/readwritesplit/rwsplit_route_stmt.cc | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/query_classifier/qc_sqlite/qc_sqlite.cc b/query_classifier/qc_sqlite/qc_sqlite.cc index ad0b10bac..713586cdc 100644 --- a/query_classifier/qc_sqlite/qc_sqlite.cc +++ b/query_classifier/qc_sqlite/qc_sqlite.cc @@ -3544,6 +3544,7 @@ static bool parse_query(GWBUF* query, uint32_t collect) { MXS_ERROR("The provided buffer does not contain a COM_QUERY, but a %s.", STRPACKETTYPE(MYSQL_GET_COMMAND(data))); + ss_dassert(!true); } } else diff --git a/server/modules/routing/readwritesplit/rwsplit_route_stmt.cc b/server/modules/routing/readwritesplit/rwsplit_route_stmt.cc index 8917d77f7..a9d6890c9 100644 --- a/server/modules/routing/readwritesplit/rwsplit_route_stmt.cc +++ b/server/modules/routing/readwritesplit/rwsplit_route_stmt.cc @@ -834,10 +834,10 @@ handle_multi_temp_and_load(RWSplitSession *rses, GWBUF *querybuf, /** * Check if the query has anything to do with temporary tables. */ - if (rses->have_tmp_tables) + if (rses->have_tmp_tables && is_packet_a_query(packet_type)) { check_drop_tmp_table(rses, querybuf); - if (is_packet_a_query(packet_type) && is_read_tmp_table(rses, querybuf, *qtype)) + if (is_read_tmp_table(rses, querybuf, *qtype)) { *qtype |= QUERY_TYPE_MASTER_READ; } From aab039f783a29e4ad2a4047895bd9817d28b8d95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Sat, 24 Feb 2018 05:52:03 +0200 Subject: [PATCH 14/18] MXS-1677: Add test case Added test case that reproduces the problem and verified that it is fixed. --- maxscale-system-test/CMakeLists.txt | 4 ++++ maxscale-system-test/mxs1677_temp_table.cpp | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 maxscale-system-test/mxs1677_temp_table.cpp diff --git a/maxscale-system-test/CMakeLists.txt b/maxscale-system-test/CMakeLists.txt index 5c481a7ce..343ccf2ff 100644 --- a/maxscale-system-test/CMakeLists.txt +++ b/maxscale-system-test/CMakeLists.txt @@ -605,6 +605,10 @@ add_test_executable(mxs1643_extra_events.cpp mxs1643_extra_events mxs1643_extra_ # https://jira.mariadb.org/browse/MXS-1653 add_test_executable(mxs1653_ps_hang.cpp mxs1653_ps_hang replication LABELS REPL_BACKEND) +# MXS-1677: sysbench failed to initialize w/ MaxScale read/write splitter +# https://jira.mariadb.org/browse/MXS-1677 +add_test_executable(mxs1677_temp_table.cpp mxs1677_temp_table replication LABELS REPL_BACKEND) + # 'namedserverfilter' test add_test_executable(namedserverfilter.cpp namedserverfilter namedserverfilter LABELS namedserverfilter LIGHT REPL_BACKEND) diff --git a/maxscale-system-test/mxs1677_temp_table.cpp b/maxscale-system-test/mxs1677_temp_table.cpp new file mode 100644 index 000000000..e74679889 --- /dev/null +++ b/maxscale-system-test/mxs1677_temp_table.cpp @@ -0,0 +1,18 @@ +/** + * MXS-1677: Error messages logged for non-text queries after temporary table is created + * + * https://jira.mariadb.org/browse/MXS-1677 + */ +#include "testconnections.h" + +int main(int argc, char** argv) +{ + TestConnections test(argc, argv); + + test.maxscales->connect(); + test.try_query(test.maxscales->conn_rwsplit[0], "CREATE TEMPORARY TABLE test.temp(id INT)"); + test.maxscales->disconnect(); + + test.check_log_err(0, "The provided buffer does not contain a COM_QUERY, but a COM_QUIT", false); + return test.global_result; +} From 13c222488ee8347fc1b2b50da33219c6887ea1b8 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Sun, 25 Feb 2018 17:09:45 +0200 Subject: [PATCH 15/18] RWS: Log packet length when logging transaction status --- server/modules/routing/readwritesplit/rwsplit_mysql.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/modules/routing/readwritesplit/rwsplit_mysql.cc b/server/modules/routing/readwritesplit/rwsplit_mysql.cc index a2b4f1de7..6abd8fd24 100644 --- a/server/modules/routing/readwritesplit/rwsplit_mysql.cc +++ b/server/modules/routing/readwritesplit/rwsplit_mysql.cc @@ -174,12 +174,13 @@ log_transaction_status(RWSplitSession *rses, GWBUF *querybuf, uint32_t qtype) MXS_SESSION *ses = rses->client_dcb->session; const char *autocommit = session_is_autocommit(ses) ? "[enabled]" : "[disabled]"; const char *transaction = session_trx_is_active(ses) ? "[open]" : "[not open]"; + uint32_t plen = MYSQL_GET_PACKET_LEN(querybuf); const char *querytype = qtypestr == NULL ? "N/A" : qtypestr; const char *hint = querybuf->hint == NULL ? "" : ", Hint:"; const char *hint_type = querybuf->hint == NULL ? "" : STRHINTTYPE(querybuf->hint->type); - MXS_INFO("> Autocommit: %s, trx is %s, cmd: (0x%02x) %s, type: %s, stmt: %.*s%s %s", - autocommit, transaction, command, STRPACKETTYPE(command), + MXS_INFO("> Autocommit: %s, trx is %s, cmd: (0x%02x) %s, plen: %u, type: %s, pastmt: %.*s%s %s", + autocommit, transaction, command, STRPACKETTYPE(command), plen, querytype, len, sql, hint, hint_type); MXS_FREE(qtypestr); From b7cc793c400672a0faef668c239dcc0d98553e90 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Tue, 27 Feb 2018 00:03:01 +0200 Subject: [PATCH 16/18] MXS-1688 Add test that reveals DATE_ADD problem --- query_classifier/test/maxscale.test | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/query_classifier/test/maxscale.test b/query_classifier/test/maxscale.test index 6b9b59449..e2fda41d4 100644 --- a/query_classifier/test/maxscale.test +++ b/query_classifier/test/maxscale.test @@ -86,4 +86,12 @@ select * from db1.t1 union select * from db2.t2; select names from t; call p1(); -call p1(@var); \ No newline at end of file +call p1(@var); + +# MXS-1688 +select id from db2.t1 where DATE_SUB("2017-06-15", INTERVAL 10 DAY) < "2017-06-15"; +select id from db2.t1 where SUBDATE("2017-06-15", INTERVAL 10 DAY) < "2017-06-15"; +select id from db2.t1 where DATE_ADD("2017-06-15", INTERVAL 10 DAY) < "2017-06-15"; +select id from db2.t1 where ADDDATE("2017-06-15", INTERVAL 10 DAY) < "2017-06-15"; +SELECT '2008-12-31 23:59:59' + INTERVAL 1 SECOND; +SELECT '2005-01-01' - INTERVAL 1 SECOND; \ No newline at end of file From 0c206ff42843558f13d17d78f112913d27f8a91b Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Tue, 27 Feb 2018 02:55:05 +0200 Subject: [PATCH 17/18] MXS-1688 Handle ...INTERVAL N "INTERVAL N " is now handled as an expression in itself and as asuch will cause both statements such as "SELECT '2008-12-31 23:59:59' + INTERVAL 1 SECOND;" and "select id from db2.t1 where DATE_ADD("2017-06-15", INTERVAL 10 DAY) < "2017-06-15";" to be handled correctly. The compare test program contains some heuristic checking, as the the embedded parser will in all cases report date manipulation as the use of the add_date_interval() function. --- .../qc_sqlite/sqlite-src-3110100/src/parse.y | 4 +- query_classifier/test/compare.cc | 60 ++++++++++++++++++- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/query_classifier/qc_sqlite/sqlite-src-3110100/src/parse.y b/query_classifier/qc_sqlite/sqlite-src-3110100/src/parse.y index f3f7e0007..fad997933 100644 --- a/query_classifier/qc_sqlite/sqlite-src-3110100/src/parse.y +++ b/query_classifier/qc_sqlite/sqlite-src-3110100/src/parse.y @@ -1948,10 +1948,10 @@ expr(A) ::= expr(X) BITAND|BITOR|LSHIFT|RSHIFT(OP) expr(Y). expr(A) ::= expr(X) PLUS|MINUS(OP) expr(Y). {spanBinaryExpr(&A,pParse,@OP,&X,&Y);} %ifdef MAXSCALE -expr(A) ::= expr(X) PLUS|MINUS INTERVAL INTEGER id. { +expr(A) ::= INTERVAL INTEGER(X) id. { // Here we could check that id is one of MICROSECOND, SECOND, MINUTE // HOUR, DAY, WEEK, etc. - A=X; + spanExpr(&A, pParse, @X, &X); } %endif expr(A) ::= expr(X) STAR|SLASH|REM(OP) expr(Y). diff --git a/query_classifier/test/compare.cc b/query_classifier/test/compare.cc index 7b6fc611e..35185a0fd 100644 --- a/query_classifier/test/compare.cc +++ b/query_classifier/test/compare.cc @@ -1069,6 +1069,11 @@ public: return rv; } + const std::string& name() const + { + return m_name; + } + void print(ostream& out) const { out << m_name; @@ -1118,6 +1123,19 @@ bool operator == (const QcFunctionInfo& lhs, const QcFunctionInfo& rhs) return lhs.eq(rhs); } +void collect_missing_function_names(const std::set& one, + const std::set& other, + std::set* pNames) +{ + for (std::set::const_iterator i = one.begin(); i != one.end(); ++i) + { + if (other.count(*i) == 0) + { + pNames->insert(i->name()); + } + } +} + bool compare_get_function_info(QUERY_CLASSIFIER* pClassifier1, GWBUF* pCopy1, QUERY_CLASSIFIER* pClassifier2, GWBUF* pCopy2) { @@ -1151,7 +1169,47 @@ bool compare_get_function_info(QUERY_CLASSIFIER* pClassifier1, GWBUF* pCopy1, } else { - ss << "ERR: " << f1 << " != " << f2; + std::set names1; + collect_missing_function_names(f1, f2, &names1); + + std::set names2; + collect_missing_function_names(f2, f1, &names2); + + bool real_error = false; + + // We assume that names1 are from the qc_mysqlembedded and names2 from qc_sqlite. + // The embedded parser reports all date_add(), adddate(), date_sub() and subdate() + // functions as date_add_interval(). Further, all "DATE + INTERVAL ..." cases become + // use of date_add_interval() functions. + for (std::set::iterator i = names1.begin(); i != names1.end(); ++i) + { + if (*i == "date_add_interval") + { + if ((names2.count("date_add") == 0) && + (names2.count("adddate") == 0) && + (names2.count("date_sub") == 0) && + (names2.count("subdate") == 0) && + (names2.count("+") == 0) && + (names2.count("-") == 0)) + { + real_error = true; + } + } + else + { + real_error = true; + } + } + + if (real_error) + { + ss << "ERR: " << f1 << " != " << f2; + } + else + { + ss << "Ok : " << f1 << " != " << f2; + success = true; + } } report(success, ss.str()); From b46c52d65c18534ef865ae54cbce66f559fa9468 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Tue, 27 Feb 2018 18:50:18 +0200 Subject: [PATCH 18/18] MXS-1684 Match space+linefeed as linefeed A linefeed is whitespace, so given the rules "\n"+ return '\n' {SPACE} ; a line consisting of space followed by a linefeed, will be matched as space and not as a linefeed and hence will cause the parser to barf. --- server/modules/filter/dbfwfilter/token.l | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/modules/filter/dbfwfilter/token.l b/server/modules/filter/dbfwfilter/token.l index 324e2cbf5..c27c02c76 100644 --- a/server/modules/filter/dbfwfilter/token.l +++ b/server/modules/filter/dbfwfilter/token.l @@ -36,7 +36,7 @@ USTR [%-_[:alnum:][:punct:]]+ CMP [=<>!]+ %% -"\n"+ return '\n'; +{SPACE}"\n"+ return '\n'; {COMMENT} return FWTOK_COMMENT; deny|allow return FWTOK_DENY; /** This should be removed at some point */ rule return FWTOK_RULE;