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'.
v2.x_refactor_cluster_mysql_servers
Javier Jaramago Fernández 3 years ago
parent 43d6274060
commit f48dfce6cf

@ -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<std::uint64_t, void *> 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);

@ -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;
}

@ -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);
}

@ -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<MYSQL_RES*>& 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());

Loading…
Cancel
Save