diff --git a/include/MySQL_Session.h b/include/MySQL_Session.h index daadc53b9..748c04ce8 100644 --- a/include/MySQL_Session.h +++ b/include/MySQL_Session.h @@ -239,6 +239,7 @@ class MySQL_Session Session_Regex **match_regexes; void *ldap_ctx; + ProxySQL_Node_Address * proxysql_node_address; // this is used ONLY for Admin, and only if the other party is another proxysql instance part of a cluster // this variable is relevant only if status == SETTING_VARIABLE enum variable_name changing_variable_idx; diff --git a/include/proxysql_structs.h b/include/proxysql_structs.h index 0523a04dd..e2764977f 100644 --- a/include/proxysql_structs.h +++ b/include/proxysql_structs.h @@ -444,6 +444,7 @@ class MySQL_ResultSet; class Query_Processor_Output; class MySrvC; class Web_Interface_plugin; +class ProxySQL_Node_Address; #endif /* PROXYSQL_CLASSES */ //#endif /* __cplusplus */ diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 09a23d7c7..8402c5acb 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -502,6 +502,7 @@ MySQL_Session::MySQL_Session() { last_HG_affected_rows = -1; // #1421 : advanced support for LAST_INSERT_ID() ldap_ctx = NULL; + proxysql_node_address = NULL; } void MySQL_Session::init() { @@ -599,6 +600,10 @@ MySQL_Session::~MySQL_Session() { GloMyLdapAuth->ldap_ctx_free(ldap_ctx); ldap_ctx = NULL; } + if (proxysql_node_address) { + delete proxysql_node_address; + proxysql_node_address = NULL; + } } diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index bd962cc18..7ca310d52 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -493,6 +493,8 @@ static int http_handler(void *cls, struct MHD_Connection *connection, const char #define ADMIN_SQLITE_TABLE_RUNTIME_PROXYSQL_SERVERS "CREATE TABLE runtime_proxysql_servers (hostname VARCHAR NOT NULL , port INT NOT NULL DEFAULT 6032 , weight INT CHECK (weight >= 0) NOT NULL DEFAULT 0 , comment VARCHAR NOT NULL DEFAULT '' , PRIMARY KEY (hostname, port) )" +#define STATS_SQLITE_TABLE_PROXYSQL_SERVERS_CLIENTS_STATUS "CREATE TABLE stats_proxysql_servers_clients_status (uuid VARCHAR NOT NULL , hostname VARCHAR NOT NULL , port INT NOT NULL , last_seen_at INT NOT NULL , PRIMARY KEY (uuid, hostname, port) )" + #define STATS_SQLITE_TABLE_PROXYSQL_SERVERS_STATUS "CREATE TABLE stats_proxysql_servers_status (hostname VARCHAR NOT NULL , port INT NOT NULL DEFAULT 6032 , weight INT CHECK (weight >= 0) NOT NULL DEFAULT 0 , master VARCHAR NOT NULL , global_version INT NOT NULL , check_age_us INT NOT NULL , ping_time_us INT NOT NULL, checks_OK INT NOT NULL , checks_ERR INT NOT NULL , PRIMARY KEY (hostname, port) )" #define STATS_SQLITE_TABLE_PROXYSQL_SERVERS_METRICS "CREATE TABLE stats_proxysql_servers_metrics (hostname VARCHAR NOT NULL , port INT NOT NULL DEFAULT 6032 , weight INT CHECK (weight >= 0) NOT NULL DEFAULT 0 , comment VARCHAR NOT NULL DEFAULT '' , response_time_ms INT NOT NULL , Uptime_s INT NOT NULL , last_check_ms INT NOT NULL , Queries INT NOT NULL , Client_Connections_connected INT NOT NULL , Client_Connections_created INT NOT NULL , PRIMARY KEY (hostname, port) )" @@ -1211,6 +1213,50 @@ bool admin_handler_command_kill_connection(char *query_no_space, unsigned int qu * return true if the command is not a valid one and needs to be executed by SQLite (that will return an error) */ bool admin_handler_command_proxysql(char *query_no_space, unsigned int query_no_space_length, MySQL_Session *sess, ProxySQL_Admin *pa) { + if (!(strncasecmp("PROXYSQL CLUSTER_NODE_UUID ", query_no_space, strlen("PROXYSQL CLUSTER_NODE_UUID ")))) { + int l = strlen("PROXYSQL CLUSTER_NODE_UUID "); + if (sess->client_myds->addr.port == 0) { + proxy_warning("Received PROXYSQL CLUSTER_NODE_UUID not from TCP socket. Exiting client\n"); + SPA->send_MySQL_ERR(&sess->client_myds->myprot, (char *)"Received PROXYSQL CLUSTER_NODE_UUID not from TCP socket"); + sess->client_myds->shut_soft(); + return false; + } + if (query_no_space_length==l+36) { + uuid_t uu; + if (uuid_parse(query_no_space+l, uu)==0) { + proxy_info("Received PROXYSQL CLUSTER_NODE_UUID from %s:%d : %s\n", sess->client_myds->addr.addr, sess->client_myds->addr.port, query_no_space+l); + if (sess->proxysql_node_address==NULL) { + sess->proxysql_node_address = new ProxySQL_Node_Address(sess->client_myds->addr.addr, sess->client_myds->addr.port); + sess->proxysql_node_address->uuid = strdup(query_no_space+l); + proxy_info("Created new link with Cluster node %s:%d : %s\n", sess->client_myds->addr.addr, sess->client_myds->addr.port, query_no_space+l); + SPA->send_MySQL_OK(&sess->client_myds->myprot, NULL); + return false; + } else { + if (strcmp(query_no_space+l, sess->proxysql_node_address->uuid)) { + proxy_error("Cluster node %s:%d is sending a new UUID : %s . Former UUID : %s . Exiting client\n", sess->client_myds->addr.addr, sess->client_myds->addr.port, query_no_space+l, sess->proxysql_node_address->uuid); + SPA->send_MySQL_ERR(&sess->client_myds->myprot, (char *)"Received PROXYSQL CLUSTER_NODE_UUID with a new UUID not matching the previous one"); + sess->client_myds->shut_soft(); + return false; + } else { + proxy_info("Cluster node %s:%d is sending again its UUID : %s\n", sess->client_myds->addr.addr, sess->client_myds->addr.port, query_no_space+l); + SPA->send_MySQL_OK(&sess->client_myds->myprot, NULL); + return false; + } + } + return false; + } else { + proxy_warning("Received PROXYSQL CLUSTER_NODE_UUID from %s:%d with invalid format: %s . Exiting client\n", sess->client_myds->addr.addr, sess->client_myds->addr.port, query_no_space+l); + SPA->send_MySQL_ERR(&sess->client_myds->myprot, (char *)"Received PROXYSQL CLUSTER_NODE_UUID with invalid format"); + sess->client_myds->shut_soft(); + return false; + } + } else { + proxy_warning("Received PROXYSQL CLUSTER_NODE_UUID from %s:%d with invalid format: %s . Exiting client\n", sess->client_myds->addr.addr, sess->client_myds->addr.port, query_no_space+l); + SPA->send_MySQL_ERR(&sess->client_myds->myprot, (char *)"Received PROXYSQL CLUSTER_NODE_UUID with invalid format"); + sess->client_myds->shut_soft(); + return false; + } + } if (query_no_space_length==strlen("PROXYSQL READONLY") && !strncasecmp("PROXYSQL READONLY",query_no_space, query_no_space_length)) { // this command enables admin_read_only , so the admin module is in read_only mode proxy_info("Received PROXYSQL READONLY command\n"); @@ -4623,6 +4669,20 @@ __end_show_commands: } __run_query: + if (sess->proxysql_node_address) { + if (sess->client_myds->active) { + time_t now = time(NULL); + string q = "INSERT OR REPLACE INTO stats_proxysql_servers_clients_status (uuid, hostname, port, last_seen_at) VALUES (\""; + q += sess->proxysql_node_address->uuid; + q += "\",\""; + q += sess->proxysql_node_address->hostname; + q += "\","; + q += std::to_string(sess->proxysql_node_address->port); + q += ","; + q += std::to_string(now) + ")"; + SPA->statsdb->execute(q.c_str()); + } + } if (run_query) { ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; if (sess->session_type == PROXYSQL_SESSION_ADMIN) { // no stats @@ -5418,6 +5478,7 @@ bool ProxySQL_Admin::init() { insert_into_tables_defs(tables_defs_stats,"stats_proxysql_servers_checksums", STATS_SQLITE_TABLE_PROXYSQL_SERVERS_CHECKSUMS); insert_into_tables_defs(tables_defs_stats,"stats_proxysql_servers_metrics", STATS_SQLITE_TABLE_PROXYSQL_SERVERS_METRICS); insert_into_tables_defs(tables_defs_stats,"stats_proxysql_servers_status", STATS_SQLITE_TABLE_PROXYSQL_SERVERS_STATUS); + insert_into_tables_defs(tables_defs_stats,"stats_proxysql_servers_clients_status", STATS_SQLITE_TABLE_PROXYSQL_SERVERS_CLIENTS_STATUS); // upgrade mysql_servers if needed (upgrade from previous version) disk_upgrade_mysql_servers(); diff --git a/lib/ProxySQL_Cluster.cpp b/lib/ProxySQL_Cluster.cpp index 6bd64cc93..76bfe08a5 100644 --- a/lib/ProxySQL_Cluster.cpp +++ b/lib/ProxySQL_Cluster.cpp @@ -108,6 +108,10 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { if (strcmp(row[0], PROXYSQL_VERSION)==0) { proxy_info("Cluster: clustering with peer %s:%d . Remote version: %s . Self version: %s\n", node->hostname, node->port, row[0], PROXYSQL_VERSION); same_version = true; + std::string q = "PROXYSQL CLUSTER_NODE_UUID "; + q += GloVars.uuid; + proxy_info("Cluster: sending CLUSTER_NODE_UUID %s to peer %s:%d\n", GloVars.uuid, node->hostname, node->port); + rc_query = mysql_query(conn, q.c_str()); } else { proxy_warning("Cluster: different ProxySQL version with peer %s:%d . Remote: %s . Self: %s\n", node->hostname, node->port, row[0], PROXYSQL_VERSION); }