From f48dfce6cf91e73ac4ee2ae0e39c70dd0c950205 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Tue, 11 Jul 2023 17:54:16 +0200 Subject: [PATCH] Simplified resultset and checksum generation for 'mysql_servers' - Resultsets for 'runtime_mysql_servers' and 'mysql_servers_v2' are now directly generated by the SQL query. - Checksum computations are simplified to 'SQLite3_result::raw_checksum' and equivalent function 'mysql_raw_checksum'. --- include/MySQL_HostGroups_Manager.h | 68 +++++++++++++++-- lib/MySQL_HostGroups_Manager.cpp | 115 ++--------------------------- lib/ProxySQL_Admin.cpp | 23 +----- lib/ProxySQL_Cluster.cpp | 70 +----------------- 4 files changed, 76 insertions(+), 200 deletions(-) diff --git a/include/MySQL_HostGroups_Manager.h b/include/MySQL_HostGroups_Manager.h index 3035677a7..d674c58b5 100644 --- a/include/MySQL_HostGroups_Manager.h +++ b/include/MySQL_HostGroups_Manager.h @@ -66,8 +66,55 @@ using json = nlohmann::json; #define MYHGM_MYSQL_HOSTGROUP_ATTRIBUTES "CREATE TABLE mysql_hostgroup_attributes (hostgroup_id INT NOT NULL PRIMARY KEY , max_num_online_servers INT CHECK (max_num_online_servers>=0 AND max_num_online_servers <= 1000000) NOT NULL DEFAULT 1000000 , autocommit INT CHECK (autocommit IN (-1, 0, 1)) NOT NULL DEFAULT -1 , free_connections_pct INT CHECK (free_connections_pct >= 0 AND free_connections_pct <= 100) NOT NULL DEFAULT 10 , init_connect VARCHAR NOT NULL DEFAULT '' , multiplex INT CHECK (multiplex IN (0, 1)) NOT NULL DEFAULT 1 , connection_warming INT CHECK (connection_warming IN (0, 1)) NOT NULL DEFAULT 0 , throttle_connections_per_sec INT CHECK (throttle_connections_per_sec >= 1 AND throttle_connections_per_sec <= 1000000) NOT NULL DEFAULT 1000000 , ignore_session_variables VARCHAR CHECK (JSON_VALID(ignore_session_variables) OR ignore_session_variables = '') NOT NULL DEFAULT '' , servers_defaults VARCHAR CHECK (JSON_VALID(servers_defaults) OR servers_defaults = '') NOT NULL DEFAULT '' , comment VARCHAR NOT NULL DEFAULT '')" -//#define MYHGM_GEN_INCOMING_MYSQL_SERVERS "SELECT hostgroup_id, hostname, port, gtid_port, CASE status WHEN 0 THEN \"ONLINE\" WHEN 1 THEN \"SHUNNED\" WHEN 2 THEN \"OFFLINE_SOFT\" WHEN 3 THEN \"OFFLINE_HARD\" WHEN 4 THEN \"SHUNNED\" END status, weight, compression, max_connections, max_replication_lag, use_ssl, max_latency_ms, comment FROM mysql_servers_incoming ORDER BY hostgroup_id, hostname, port" -#define MYHGM_GEN_ADMIN_MYSQL_SERVERS "SELECT hostgroup_id,hostname,port,gtid_port,status,weight,compression,max_connections,max_replication_lag,use_ssl,max_latency_ms,comment FROM main.mysql_servers ORDER BY hostgroup_id, hostname, port" +/* + * @brief Generates the 'runtime_mysql_servers' resultset exposed to other ProxySQL cluster members. + * @details Makes 'SHUNNED' and 'SHUNNED_REPLICATION_LAG' statuses equivalent to 'ONLINE'. 'SHUNNED' states + * are by definition local transitory states, this is why a 'mysql_servers' table reconfiguration isn't + * normally performed when servers are internally imposed with these statuses. This means, that propagating + * this state to other cluster members is undesired behavior, and so it's generating a different checksum, + * due to a server having this particular state, that will result in extra unnecessary fetching operations. + * The query also filters out 'OFFLINE_HARD' servers, 'OFFLINE_HARD' is a local status which is equivalent to + * a server no longer being part of the table (DELETED state). And so, they shouldn't be propagated. + * + * For placing the query into a single line for debugging purposes: + * ``` + * sed 's/^\t\+"//g; s/"\s\\$//g; s/\\"/"/g' /tmp/select.sql | paste -sd '' + * ``` + */ +#define MYHGM_GEN_CLUSTER_ADMIN_RUNTIME_SERVERS \ + "SELECT " \ + "hostgroup_id, hostname, port, gtid_port," \ + "CASE status" \ + " WHEN 0 THEN \"ONLINE\"" \ + " WHEN 1 THEN \"ONLINE\"" \ + " WHEN 2 THEN \"OFFLINE_SOFT\"" \ + " WHEN 3 THEN \"OFFLINE_HARD\"" \ + " WHEN 4 THEN \"ONLINE\" " \ + "END status," \ + "weight, compression, max_connections, max_replication_lag, use_ssl, max_latency_ms, comment " \ + "FROM mysql_servers " \ + "WHERE status != 3 " \ + "ORDER BY hostgroup_id, hostname, port" \ + +/** + * @brief Generates the 'mysql_servers_v2' resultset exposed to other ProxySQL cluster members. + * @details The generated resultset is used for the checksum computation of the runtime ProxySQL config + * ('mysql_servers_v2' checksum), and it's also forwarded to other cluster members when querying the Admin + * interface with 'CLUSTER_QUERY_MYSQL_SERVERS_V2'. It makes 'SHUNNED' state equivalent to 'ONLINE', and also + * filters out any 'OFFLINE_HARD' entries. This is done because none of the statuses are valid configuration + * statuses, they are local, transient status that ProxySQL uses during operation. + */ +#define MYHGM_GEN_CLUSTER_ADMIN_MYSQL_SERVERS \ + "SELECT " \ + "hostgroup_id, hostname, port, gtid_port, " \ + "CASE" \ + " WHEN status=\"SHUNNED\" THEN \"ONLINE\"" \ + " ELSE status " \ + "END AS status, " \ + "weight, compression, max_connections, max_replication_lag, use_ssl, max_latency_ms, comment " \ + "FROM main.mysql_servers " \ + "WHERE status != \"OFFLINE_HARD\" " \ + "ORDER BY hostgroup_id, hostname, port" \ typedef std::unordered_map umap_mysql_errors; @@ -785,7 +832,20 @@ class MySQL_HostGroups_Manager { SQLite3_result* get_current_mysql_table(const string& name); SQLite3_result * execute_query(char *query, char **error); - SQLite3_result *dump_table_mysql(const string&); + /** + * @brief Creates a resultset with the current full content of the target table. + * @param string The target table. Valid values are: + * - "mysql_aws_aurora_hostgroups" + * - "mysql_galera_hostgroups" + * - "mysql_group_replication_hostgroups" + * - "mysql_replication_hostgroups" + * - "mysql_hostgroup_attributes" + * - "mysql_servers" + * - "cluster_mysql_servers" + * When targeting 'mysql_servers' table is purged and regenerated. + * @return The generated resultset. + */ + SQLite3_result* dump_table_mysql(const string&); /** * @brief Update the public member resulset 'mysql_servers_to_monitor'. This resulset should contain the latest @@ -882,8 +942,6 @@ class MySQL_HostGroups_Manager { MySrvC* find_server_in_hg(unsigned int _hid, const std::string& addr, int port); private: - static uint64_t compute_mysql_servers_raw_checksum(const SQLite3_result* runtime_mysql_servers); - void update_hostgroup_manager_mappings(); uint64_t get_mysql_servers_checksum(SQLite3_result* runtime_mysql_servers = nullptr); uint64_t get_mysql_servers_v2_checksum(SQLite3_result* incoming_mysql_servers_v2 = nullptr); diff --git a/lib/MySQL_HostGroups_Manager.cpp b/lib/MySQL_HostGroups_Manager.cpp index 216288bfd..f9f7791de 100644 --- a/lib/MySQL_HostGroups_Manager.cpp +++ b/lib/MySQL_HostGroups_Manager.cpp @@ -2285,28 +2285,9 @@ uint64_t MySQL_HostGroups_Manager::get_mysql_servers_checksum(SQLite3_result* ru int cols = 0; int affected_rows = 0; - mydb->execute_statement(MYHGM_GEN_ADMIN_RUNTIME_SERVERS, &error, &cols, &affected_rows, &resultset); + mydb->execute_statement(MYHGM_GEN_CLUSTER_ADMIN_RUNTIME_SERVERS, &error, &cols, &affected_rows, &resultset); if (resultset) { - if (resultset->rows_count) { - // Remove 'OFFLINE_HARD' servers since they are not relevant to propagate to other Cluster - // nodes, or relevant for checksum computation. - const size_t init_row_count = resultset->rows_count; - size_t rm_rows_count = 0; - const auto is_offline_server = [&rm_rows_count](SQLite3_row* row) { - if (strcasecmp(row->fields[4], "OFFLINE_HARD") == 0) { - rm_rows_count += 1; - return true; - } else { - return false; - } - }; - resultset->rows.erase( - std::remove_if(resultset->rows.begin(), resultset->rows.end(), is_offline_server), - resultset->rows.end() - ); - resultset->rows_count = init_row_count - rm_rows_count; - } save_runtime_mysql_servers(resultset); } else { proxy_info("Checksum for table %s is 0x%lX\n", "mysql_servers", (long unsigned int)0); @@ -2316,7 +2297,7 @@ uint64_t MySQL_HostGroups_Manager::get_mysql_servers_checksum(SQLite3_result* ru save_runtime_mysql_servers(runtime_mysql_servers); } - table_resultset_checksum[HGM_TABLES::MYSQL_SERVERS] = compute_mysql_servers_raw_checksum(resultset); + table_resultset_checksum[HGM_TABLES::MYSQL_SERVERS] = resultset != nullptr ? resultset->raw_checksum() : 0; proxy_info("Checksum for table %s is 0x%lX\n", "mysql_servers", table_resultset_checksum[HGM_TABLES::MYSQL_SERVERS]); return table_resultset_checksum[HGM_TABLES::MYSQL_SERVERS]; @@ -2342,28 +2323,9 @@ uint64_t MySQL_HostGroups_Manager::get_mysql_servers_v2_checksum(SQLite3_result* int cols = 0; int affected_rows = 0; - GloAdmin->admindb->execute_statement(MYHGM_GEN_ADMIN_MYSQL_SERVERS, &error, &cols, &affected_rows, &resultset); + GloAdmin->admindb->execute_statement(MYHGM_GEN_CLUSTER_ADMIN_MYSQL_SERVERS, &error, &cols, &affected_rows, &resultset); if (resultset) { - if (resultset->rows_count) { - - const size_t init_row_count = resultset->rows_count; - size_t rm_rows_count = 0; - const auto is_offline_server = [&rm_rows_count](SQLite3_row* row) { - if (strcasecmp(row->fields[4], "OFFLINE_HARD") == 0) { - rm_rows_count += 1; - return true; - } else { - return false; - } - }; - resultset->rows.erase( - std::remove_if(resultset->rows.begin(), resultset->rows.end(), is_offline_server), - resultset->rows.end() - ); - resultset->rows_count = init_row_count - rm_rows_count; - - } save_mysql_servers_v2(resultset); } else { proxy_info("Checksum for table %s is 0x%lX\n", "mysql_servers_v2", (long unsigned int)0); @@ -2380,7 +2342,7 @@ uint64_t MySQL_HostGroups_Manager::get_mysql_servers_v2_checksum(SQLite3_result* table_resultset_checksum[MYSQL_AWS_AURORA_HOSTGROUPS] = 0; table_resultset_checksum[MYSQL_HOSTGROUP_ATTRIBUTES] = 0; - table_resultset_checksum[MYSQL_SERVERS_V2] = compute_mysql_servers_raw_checksum(resultset); + table_resultset_checksum[MYSQL_SERVERS_V2] = resultset != nullptr ? resultset->raw_checksum() : 0; proxy_info("Checksum for table %s is 0x%lX\n", "mysql_servers_v2", table_resultset_checksum[MYSQL_SERVERS_V2]); commit_update_checksums_from_tables(); @@ -2988,7 +2950,9 @@ SQLite3_result * MySQL_HostGroups_Manager::dump_table_mysql(const string& name) } else if (name == "mysql_hostgroup_attributes") { query=(char *)"SELECT hostgroup_id, max_num_online_servers, autocommit, free_connections_pct, init_connect, multiplex, connection_warming, throttle_connections_per_sec, ignore_session_variables, servers_defaults, comment FROM mysql_hostgroup_attributes ORDER BY hostgroup_id"; } else if (name == "mysql_servers") { - query = (char *)MYHGM_GEN_ADMIN_RUNTIME_SERVERS; + query = (char *)MYHGM_GEN_ADMIN_RUNTIME_SERVERS; + } else if (name == "cluster_mysql_servers") { + query = (char *)MYHGM_GEN_CLUSTER_ADMIN_RUNTIME_SERVERS; } else { assert(0); } @@ -4293,7 +4257,7 @@ SQLite3_result* MySQL_HostGroups_Manager::get_current_mysql_table(const string& return this->incoming_replication_hostgroups; } else if (name == "mysql_hostgroup_attributes") { return this->incoming_hostgroup_attributes; - } else if (name == "mysql_servers") { + } else if (name == "cluster_mysql_servers") { return this->runtime_mysql_servers; } else if (name == "mysql_servers_v2") { return this->incoming_mysql_servers_v2; @@ -8266,66 +8230,3 @@ void MySQL_HostGroups_Manager::HostGroup_Server_Mapping::remove_HGM(MySrvC* srv) srv->status = MYSQL_SERVER_STATUS_OFFLINE_HARD; srv->ConnectionsFree->drop_all_connections(); } - -/** - * @brief This function computes the checksum of the mysql_servers resultset. -* As the checksum is being calculated, the function replaces the status values with their respective integer values. - * - * @param mysql_servers resultset of mysql_servers or mysql_servers_incoming. - */ -uint64_t MySQL_HostGroups_Manager::compute_mysql_servers_raw_checksum(const SQLite3_result* mysql_servers) { - - if (!mysql_servers || mysql_servers->rows_count == 0) - return 0; - - int status_idx = -1; - - for (int i = 0; i < mysql_servers->columns; i++) { - if (mysql_servers->column_definition[i] && mysql_servers->column_definition[i]->name && - strcmp(mysql_servers->column_definition[i]->name, "status") == 0) { - status_idx = i; - break; - } - } - - if (status_idx == -1) assert(0); - - SpookyHash myhash; - myhash.Init(19, 3); - - for (const SQLite3_row* r : mysql_servers->rows) { - - const char* mapped_status = ""; - const char* status = r->fields[status_idx]; - - if (status) { - if (strcasecmp(status, "OFFLINE_HARD") == 0) - continue; - - if (strcasecmp(status, "ONLINE") == 0 || - strcasecmp(status, "SHUNNED") == 0) { - mapped_status = "0"; - } else if (strcasecmp(status, "OFFLINE_SOFT") == 0) { - mapped_status = "2"; - } - } - - for (int i = 0; i < mysql_servers->columns; i++) { - - if (r->fields[i]) { - if (i != status_idx) { - myhash.Update(r->fields[i], r->sizes[i]); - } else { - myhash.Update(mapped_status, strlen(mapped_status)); - } - } else { - myhash.Update("", 0); - } - } - } - - uint64_t res_hash = 0, hash2 = 0; - myhash.Final(&res_hash, &hash2); - - return res_hash; -} diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index f830b9dda..629208d62 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -3759,7 +3759,7 @@ void admin_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *pkt) { if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats string tn = ""; if (!strncasecmp(CLUSTER_QUERY_RUNTIME_MYSQL_SERVERS, query_no_space, strlen(CLUSTER_QUERY_RUNTIME_MYSQL_SERVERS))) { - tn = "mysql_servers"; + tn = "cluster_mysql_servers"; } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_REPLICATION_HOSTGROUPS, query_no_space, strlen(CLUSTER_QUERY_MYSQL_REPLICATION_HOSTGROUPS))) { tn = "mysql_replication_hostgroups"; } else if (!strncasecmp(CLUSTER_QUERY_MYSQL_GROUP_REPLICATION_HOSTGROUPS, query_no_space, strlen(CLUSTER_QUERY_MYSQL_GROUP_REPLICATION_HOSTGROUPS))) { @@ -3787,29 +3787,10 @@ void admin_session_handler(MySQL_Session *sess, void *_pa, PtrSize_t *pkt) { char *error=NULL; int cols=0; int affected_rows=0; - const char* query = "SELECT hostgroup_id,hostname,port,gtid_port,status,weight,compression,max_connections,max_replication_lag,use_ssl,max_latency_ms,comment FROM main.mysql_servers ORDER BY hostgroup_id, hostname, port"; proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 4, "%s\n", query); GloAdmin->mysql_servers_wrlock(); - GloAdmin->admindb->execute_statement(query, &error , &cols , &affected_rows , &resultset); + GloAdmin->admindb->execute_statement(MYHGM_GEN_CLUSTER_ADMIN_MYSQL_SERVERS, &error, &cols, &affected_rows, &resultset); GloAdmin->mysql_servers_wrunlock(); - if (resultset && resultset->rows_count) { - const size_t init_row_count = resultset->rows_count; - size_t rm_rows_count = 0; - const auto is_offline_server = [&rm_rows_count](SQLite3_row* row) { - if (strcasecmp(row->fields[4], "OFFLINE_HARD") == 0) { - rm_rows_count += 1; - return true; - } else { - return false; - } - }; - resultset->rows.erase( - std::remove_if(resultset->rows.begin(), resultset->rows.end(), is_offline_server), - resultset->rows.end() - ); - - resultset->rows_count = init_row_count - rm_rows_count; - } } else { resultset = MyHGM->dump_table_mysql(tn); } diff --git a/lib/ProxySQL_Cluster.cpp b/lib/ProxySQL_Cluster.cpp index a90968362..71a033e46 100644 --- a/lib/ProxySQL_Cluster.cpp +++ b/lib/ProxySQL_Cluster.cpp @@ -1683,64 +1683,6 @@ int ProxySQL_Cluster::fetch_and_store(MYSQL* conn, const fetch_query& f_query, M return query_res; } -/** - * @brief Generates a hash for the resulset received for the query 'CLUSTER_QUERY_RUNTIME_MYSQL_SERVERS'. - * @details Remember that this query query is intercepted by 'ProxySQL_Admin' and always answered via - * 'MySQL_HostGroups_Manager::dump_table_proxysql_servers'. - * @param resultset The resulset resulting from the mentioned query. - * @return A hash representing the contents of the resulset. - */ -uint64_t mysql_servers_raw_checksum(MYSQL_RES* resultset) { - if (resultset == nullptr) { return 0; } - - uint64_t num_rows = mysql_num_rows(resultset); - if (num_rows == 0) { return 0; } - - MYSQL_FIELD* fields = mysql_fetch_fields(resultset); - uint32_t num_fields = mysql_num_fields(resultset); - uint32_t status_idx = 0; - - for (uint32_t i = 0; i < num_fields; i++) { - if (strcmp(fields[i].name, "status") == 0) { - status_idx = i; - } - } - - SpookyHash myhash {}; - myhash.Init(19,3); - - while (MYSQL_ROW row = mysql_fetch_row(resultset)) { - for (uint32_t i = 0; i < num_fields; i++) { - if (strcmp(row[status_idx], "OFFLINE_HARD") == 0) { - continue; - } - - if (row[i]) { - if (strcmp(fields[i].name, "status") == 0) { - if (strcmp(row[i], "ONLINE") == 0 || strcmp(row[i], "SHUNNED") == 0) { - myhash.Update("0", strlen("0")); - } else { - myhash.Update("2", strlen("1")); - } - } else { - // computing 'strlen' is required see @details - myhash.Update(row[i], strlen(row[i])); - } - } else { - myhash.Update("", 0); - } - } - } - - // restore the initial resulset index - mysql_data_seek(resultset, 0); - - uint64_t res_hash = 0, hash2 = 0; - myhash.Final(&res_hash, &hash2); - - return res_hash; -} - /** * @brief Generates a hash from the received resultsets from executing the following queries in the specified * order: @@ -1761,13 +1703,7 @@ uint64_t compute_servers_tables_raw_checksum(const vector& results, SpookyHash myhash {}; for (size_t i = 0; i < size; i++) { - uint64_t raw_hash = 0; - - if (i == 0) { - raw_hash = mysql_servers_raw_checksum(results[i]); - } else { - raw_hash = mysql_raw_checksum(results[i]); - } + uint64_t raw_hash = mysql_raw_checksum(results[i]); if (raw_hash != 0) { if (init == false) { @@ -1871,7 +1807,7 @@ void ProxySQL_Cluster::pull_runtime_mysql_servers_from_peer(const runtime_mysql_ } if (result != nullptr) { - const uint64_t servers_hash = mysql_servers_raw_checksum(result); + const uint64_t servers_hash = mysql_raw_checksum(result); const string computed_checksum{ get_checksum_from_hash(servers_hash) }; proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Computed checksum for MySQL Servers from peer %s:%d : %s\n", hostname, port, computed_checksum.c_str()); proxy_info("Cluster: Computed checksum for MySQL Servers from peer %s:%d : %s\n", hostname, port, computed_checksum.c_str()); @@ -2116,7 +2052,7 @@ void ProxySQL_Cluster::pull_mysql_servers_v2_from_peer(const mysql_servers_v2_ch bool runtime_checksum_matches = true; if (results[6]) { - const uint64_t runtime_mysql_server_hash = mysql_servers_raw_checksum(results[6]); + const uint64_t runtime_mysql_server_hash = mysql_raw_checksum(results[6]); const std::string runtime_mysql_server_computed_checksum = get_checksum_from_hash(runtime_mysql_server_hash); proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Computed checksum for MySQL Servers from peer %s:%d : %s\n", hostname, port, runtime_mysql_server_computed_checksum.c_str()); proxy_info("Cluster: Computed checksum for MySQL Servers from peer %s:%d : %s\n", hostname, port, runtime_mysql_server_computed_checksum.c_str());