From c396313cbf444fff6b3cb1929700169cd324701d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Sun, 7 May 2023 21:32:14 +0200 Subject: [PATCH 1/7] Add new 'servers_defaults' capability to 'mysql_hostgroup_attributes' --- include/MySQL_HostGroups_Manager.h | 37 +++++++- lib/MySQL_HostGroups_Manager.cpp | 144 +++++++++++++++++++++++++++-- lib/ProxySQL_Admin.cpp | 37 +++++++- 3 files changed, 201 insertions(+), 17 deletions(-) diff --git a/include/MySQL_HostGroups_Manager.h b/include/MySQL_HostGroups_Manager.h index 34636acfa..5be5ae801 100644 --- a/include/MySQL_HostGroups_Manager.h +++ b/include/MySQL_HostGroups_Manager.h @@ -64,7 +64,7 @@ using json = nlohmann::json; #define MYHGM_GEN_ADMIN_RUNTIME_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 ORDER BY hostgroup_id, hostname, port" -#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 '' , comment VARCHAR NOT NULL DEFAULT '')" +#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 '')" typedef std::unordered_map umap_mysql_errors; @@ -145,10 +145,10 @@ class MySrvC { // MySQL Server Container uint16_t port; uint16_t gtid_port; uint16_t flags; - unsigned int weight; + int64_t weight; enum MySerStatus status; unsigned int compression; - unsigned int max_connections; + int64_t max_connections; unsigned int aws_aurora_current_lag_us; unsigned int max_replication_lag; unsigned int max_connections_used; // The maximum number of connections that has been opened @@ -166,11 +166,33 @@ class MySrvC { // MySQL Server Container unsigned long long bytes_recv; bool shunned_automatic; bool shunned_and_kill_all_connections; // if a serious failure is detected, this will cause all connections to die even if the server is just shunned - bool use_ssl; + int32_t use_ssl; char *comment; MySrvConnList *ConnectionsUsed; MySrvConnList *ConnectionsFree; - MySrvC(char *, uint16_t, uint16_t, unsigned int, enum MySerStatus, unsigned int, unsigned int _max_connections, unsigned int _max_replication_lag, unsigned int _use_ssl, unsigned int _max_latency_ms, char *_comment); + /** + * @brief ConstructConstructss a new MySQL Server Container. + * @details For 'server_defaults' parameters, if '-1' is supplied, they try to be obtained from + * 'servers_defaults' entry from 'mysql_hostgroup_attributes' when adding the server to it's target + * hostgroup(via 'MySQL_HostGroups_Manager::add'), if not found, value is set with 'mysql_servers' + * defaults. + * @param addr Address of the server, specified either by IP or hostname. + * @param port Server port. + * @param gitd_port If non-zero, enables GTID tracking for the server. + * @param _weight Server weight. 'server_defaults' param, check @details. + * @param _status Initial server status. + * @param _compression Enables compression for server connections. + * @param _max_connections Max server connections. 'server_defaults' param, check @details. + * @param _max_replication_lag If non-zero, enables replication lag checks. + * @param _use_ssl Enables SSL for server connections. 'servers_defaults' param, check @details. + * @param _max_latency_ms Max ping server latency. When exceeded, server gets excluded from conn-pool. + * @param _comment User defined comment. + */ + MySrvC( + char* addr, uint16_t port, uint16_t gitd_port, int64_t _weight, enum MySerStatus _status, unsigned int _compression, + int64_t _max_connections, unsigned int _max_replication_lag, int32_t _use_ssl, unsigned int _max_latency_ms, + char* _comment + ); ~MySrvC(); void connect_error(int, bool get_mutex=true); void shun_and_killall(); @@ -222,6 +244,11 @@ class MyHGC { // MySQL Host Group Container bool initialized; // this variable controls if attributes were ever configured or not. Used by reset_attributes() json ignore_session_variables_json; // the JSON format of ignore_session_variables } attributes; + struct { + int64_t weight; + int64_t max_connections; + int32_t use_ssl; + } servers_defaults; void reset_attributes(); MyHGC(int); ~MyHGC(); diff --git a/lib/MySQL_HostGroups_Manager.cpp b/lib/MySQL_HostGroups_Manager.cpp index 3cbe229e3..f85f88fcb 100644 --- a/lib/MySQL_HostGroups_Manager.cpp +++ b/lib/MySQL_HostGroups_Manager.cpp @@ -25,7 +25,11 @@ #include "ev.h" +#include #include +#include + +using std::function; #ifdef TEST_AURORA static unsigned long long array_mysrvc_total = 0; @@ -727,7 +731,11 @@ void MySrvConnList::drop_all_connections() { } -MySrvC::MySrvC(char *add, uint16_t p, uint16_t gp, unsigned int _weight, enum MySerStatus _status, unsigned int _compression /*, uint8_t _charset */, unsigned int _max_connections, unsigned int _max_replication_lag, unsigned int _use_ssl, unsigned int _max_latency_ms, char *_comment) { +MySrvC::MySrvC( + char* add, uint16_t p, uint16_t gp, int64_t _weight, enum MySerStatus _status, unsigned int _compression, + int64_t _max_connections, unsigned int _max_replication_lag, int32_t _use_ssl, unsigned int _max_latency_ms, + char* _comment +) { address=strdup(add); port=p; gtid_port=gp; @@ -877,6 +885,10 @@ MyHGC::MyHGC(int _hid) { new_connections_now = 0; attributes.initialized = false; reset_attributes(); + // Uninitialized server defaults. Should later be initialized via 'mysql_hostgroup_attributes'. + servers_defaults.weight = -1; + servers_defaults.max_connections = -1; + servers_defaults.use_ssl = -1; } void MyHGC::reset_attributes() { @@ -2579,7 +2591,7 @@ SQLite3_result * MySQL_HostGroups_Manager::dump_table_mysql(const string& name) } else if (name == "mysql_replication_hostgroups") { query=(char *)"SELECT writer_hostgroup, reader_hostgroup, check_type, comment FROM mysql_replication_hostgroups"; } 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, comment FROM mysql_hostgroup_attributes ORDER BY hostgroup_id"; + 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; } else { @@ -3428,6 +3440,32 @@ void MySQL_HostGroups_Manager::add(MySrvC *mysrvc, unsigned int _hid) { mysrvc->queries_sent = get_prometheus_counter_val(this->status.p_connection_pool_queries_map, endpoint_id); MyHGC *myhgc=MyHGC_lookup(_hid); + + if (mysrvc->weight == -1) { + if (myhgc->servers_defaults.weight != -1) { + mysrvc->weight = myhgc->servers_defaults.weight; + } else { + // Same harcoded default as in 'CREATE TABLE mysql_servers ...' + mysrvc->weight = 1; + } + } + if (mysrvc->max_connections == -1) { + if (myhgc->servers_defaults.max_connections != -1) { + mysrvc->max_connections = myhgc->servers_defaults.max_connections; + } else { + // Same harcoded default as in 'CREATE TABLE mysql_servers ...' + mysrvc->max_connections = 1000; + } + } + if (mysrvc->use_ssl == -1) { + if (myhgc->servers_defaults.use_ssl != -1) { + mysrvc->use_ssl = myhgc->servers_defaults.use_ssl; + } else { + // Same harcoded default as in 'CREATE TABLE mysql_servers ...' + mysrvc->use_ssl = 0; + } + } + myhgc->mysrvs->add(mysrvc); } @@ -6847,6 +6885,93 @@ bool AWS_Aurora_Info::update(int r, int _port, char *_end_addr, int maxl, int al return ret; } +/** + * @brief Helper function used to try to extract a value from the JSON field 'servers_defaults'. + * + * @param j JSON object constructed from 'servers_defaults' field. + * @param hid Hostgroup for which the 'servers_defaults' is defined in 'mysql_hostgroup_attributes'. Used for + * error logging. + * @param key The key for the value to be extracted. + * @param val_check A validation function, checks if the value is within a expected range. + * + * @return The value extracted from the supplied JSON. In case of error '-1', and error cause is logged. + */ +template ::value, bool>::type = true> +T j_get_srv_default_int_val( + const json& j, uint32_t hid, const string& key, const function& val_check +) { + if (j.find(key) != j.end()) { + const json::value_t val_type = j[key].type(); + const char* type_name = j[key].type_name(); + + if (val_type == json::value_t::number_integer || val_type == json::value_t::number_unsigned) { + T val = j[key].get(); + + if (val_check(val)) { + return val; + } else { + proxy_error( + "Invalid value %ld supplied for 'mysql_hostgroup_attributes.servers_defaults.%s' for hostgroup %d." + " Value NOT UPDATED.\n", + static_cast(val), key.c_str(), hid + ); + } + } else { + proxy_error( + "Invalid type '%s'(%hhu) supplied for 'mysql_hostgroup_attributes.servers_defaults.%s' for hostgroup %d." + " Value NOT UPDATED.\n", + type_name, val_type, key.c_str(), hid + ); + } + } + + return static_cast(-1); +} + +/** + * @brief Initializes the supplied 'MyHGC' with the specified 'servers_defaults'. + * @details Input verification is performed in the supplied 'server_defaults'. It's expected to be a valid + * JSON that may contain the following fields: + * - weight: Must be an unsigned integer >= 0. + * - max_connections: Must be an unsigned integer >= 0. + * - use_ssl: Must be a integer with either value 0 or 1. + * + * In case input verification fails for a field, supplied 'MyHGC' is NOT updated for that field. An error + * message is logged specifying the source of the error. + * + * @param servers_defaults String containing a JSON defined in 'mysql_hostgroup_attributes'. + * @param myhgc The 'MyHGC' of the target hostgroup of the supplied 'servers_defaults'. + */ +void init_myhgc_servers_defaults(char* servers_defaults, MyHGC* myhgc) { + uint32_t hid = myhgc->hid; + + if (strcmp(servers_defaults, "") != 0) { + try { + nlohmann::json j = nlohmann::json::parse(servers_defaults); + + const auto weight_check = [] (int64_t weight) -> bool { return weight >= 0; }; + int64_t weight = j_get_srv_default_int_val(j, hid, "weight", weight_check); + + myhgc->servers_defaults.weight = weight; + + const auto max_conns_check = [] (int64_t max_conns) -> bool { return max_conns >= 0; }; + int64_t max_conns = j_get_srv_default_int_val(j, hid, "max_connections", max_conns_check); + + myhgc->servers_defaults.max_connections = max_conns; + + const auto use_ssl_check = [] (int32_t use_ssl) -> bool { return use_ssl == 0 || use_ssl == 1; }; + int32_t use_ssl = j_get_srv_default_int_val(j, hid, "use_ssl", use_ssl_check); + + myhgc->servers_defaults.use_ssl = use_ssl; + } catch (const json::exception& e) { + proxy_error( + "JSON parsing for 'mysql_hostgroup_attributes.servers_defaults' for hostgroup %d failed with exception `%s`.\n", + hid, e.what() + ); + } + } +} + void MySQL_HostGroups_Manager::generate_mysql_hostgroup_attributes_table() { if (incoming_hostgroup_attributes==NULL) { return; @@ -6857,8 +6982,8 @@ void MySQL_HostGroups_Manager::generate_mysql_hostgroup_attributes_table() { const char * query=(const char *)"INSERT INTO mysql_hostgroup_attributes ( " "hostgroup_id, max_num_online_servers, autocommit, free_connections_pct, " "init_connect, multiplex, connection_warming, throttle_connections_per_sec, " - "ignore_session_variables, comment) VALUES " - "(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)"; + "ignore_session_variables, servers_defaults, comment) VALUES " + "(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11)"; //rc=(*proxy_sqlite3_prepare_v2)(mydb3, query, -1, &statement, 0); rc = mydb->prepare_v2(query, &statement); @@ -6894,11 +7019,12 @@ void MySQL_HostGroups_Manager::generate_mysql_hostgroup_attributes_table() { int connection_warming = atoi(r->fields[6]); int throttle_connections_per_sec = atoi(r->fields[7]); char * ignore_session_variables = r->fields[8]; - char * comment = r->fields[9]; - proxy_info("Loading MySQL Hostgroup Attributes info for (%d,%d,%d,%d,\"%s\",%d,%d,%d,\"%s\",\"%s\")\n", + char * servers_defaults = r->fields[9]; + char * comment = r->fields[10]; + proxy_info("Loading MySQL Hostgroup Attributes info for (%d,%d,%d,%d,\"%s\",%d,%d,%d,\"%s\",'%s',\"%s\")\n", hid, max_num_online_servers, autocommit, free_connections_pct, init_connect, multiplex, connection_warming, throttle_connections_per_sec, - ignore_session_variables, comment + ignore_session_variables, servers_defaults, comment ); rc=(*proxy_sqlite3_bind_int64)(statement, 1, hid); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement, 2, max_num_online_servers); ASSERT_SQLITE_OK(rc, mydb); @@ -6909,7 +7035,8 @@ void MySQL_HostGroups_Manager::generate_mysql_hostgroup_attributes_table() { rc=(*proxy_sqlite3_bind_int64)(statement, 7, connection_warming); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement, 8, throttle_connections_per_sec); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_text)(statement, 9, ignore_session_variables, -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, mydb); - rc=(*proxy_sqlite3_bind_text)(statement, 10, comment, -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, mydb); + rc=(*proxy_sqlite3_bind_text)(statement, 10, servers_defaults, -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, mydb); + rc=(*proxy_sqlite3_bind_text)(statement, 11, comment, -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, mydb); SAFE_SQLITE3_STEP2(statement); rc=(*proxy_sqlite3_clear_bindings)(statement); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_reset)(statement); ASSERT_SQLITE_OK(rc, mydb); @@ -6945,6 +7072,7 @@ void MySQL_HostGroups_Manager::generate_mysql_hostgroup_attributes_table() { // TODO: assign the variables } } + init_myhgc_servers_defaults(servers_defaults, myhgc); } for (unsigned int i=0; ilen; i++) { MyHGC *myhgc=(MyHGC *)MyHostGroups->index(i); diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index 1ef4cbf45..2e35084f9 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -587,10 +587,13 @@ MHD_Result http_handler(void *cls, struct MHD_Connection *connection, const char #define ADMIN_SQLITE_TABLE_MYSQL_HOSTGROUP_ATTRIBUTES_V2_5_0 "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 '' , comment VARCHAR NOT NULL DEFAULT '')" -#define ADMIN_SQLITE_TABLE_MYSQL_HOSTGROUP_ATTRIBUTES ADMIN_SQLITE_TABLE_MYSQL_HOSTGROUP_ATTRIBUTES_V2_5_0 +#define ADMIN_SQLITE_TABLE_MYSQL_HOSTGROUP_ATTRIBUTES_V2_5_2 "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 ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_HOSTGROUP_ATTRIBUTES "CREATE TABLE runtime_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 '' , comment VARCHAR NOT NULL DEFAULT '')" +#define ADMIN_SQLITE_TABLE_MYSQL_HOSTGROUP_ATTRIBUTES ADMIN_SQLITE_TABLE_MYSQL_HOSTGROUP_ATTRIBUTES_V2_5_2 +#define ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_HOSTGROUP_ATTRIBUTES "CREATE TABLE runtime_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 ADMIN_SQLITE_TABLE_MYSQL_HOSTGROUP_ATTRIBUTES ADMIN_SQLITE_TABLE_MYSQL_HOSTGROUP_ATTRIBUTES_V2_5_2 // Cluster solution @@ -12170,7 +12173,7 @@ void ProxySQL_Admin::save_mysql_servers_runtime_to_database(bool _runtime) { StrQuery = "INSERT INTO "; if (_runtime) StrQuery += "runtime_"; - StrQuery += "mysql_hostgroup_attributes (hostgroup_id, max_num_online_servers, autocommit, free_connections_pct, init_connect, multiplex, connection_warming, throttle_connections_per_sec, ignore_session_variables, comment) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)"; + StrQuery += "mysql_hostgroup_attributes (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) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11)"; rc = admindb->prepare_v2(StrQuery.c_str(), &statement); ASSERT_SQLITE_OK(rc, admindb); //proxy_info("New mysql_aws_aurora_hostgroups table\n"); @@ -12185,7 +12188,8 @@ void ProxySQL_Admin::save_mysql_servers_runtime_to_database(bool _runtime) { rc=(*proxy_sqlite3_bind_int64)(statement, 7, atol(r->fields[6])); ASSERT_SQLITE_OK(rc, admindb); // connection_warming rc=(*proxy_sqlite3_bind_int64)(statement, 8, atol(r->fields[7])); ASSERT_SQLITE_OK(rc, admindb); // throttle_connections_per_sec rc=(*proxy_sqlite3_bind_text)(statement, 9, r->fields[8], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, admindb); // ignore_session_variables - rc=(*proxy_sqlite3_bind_text)(statement, 10, r->fields[9], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, admindb); // comment + rc=(*proxy_sqlite3_bind_text)(statement, 10, r->fields[9], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, admindb); // servers_defaults + rc=(*proxy_sqlite3_bind_text)(statement, 11, r->fields[10], -1, SQLITE_TRANSIENT); ASSERT_SQLITE_OK(rc, admindb); // comment SAFE_SQLITE3_STEP2(statement); rc=(*proxy_sqlite3_clear_bindings)(statement); ASSERT_SQLITE_OK(rc, admindb); @@ -13208,7 +13212,32 @@ void ProxySQL_Admin::disk_upgrade_mysql_servers() { "SELECT writer_hostgroup, reader_hostgroup, active, aurora_port, domain_name, max_lag_ms, check_interval_ms, " "check_timeout_ms, writer_is_also_reader, new_reader_weight, comment FROM mysql_aws_aurora_hostgroups_v208"); } + + // upgrade mysql_hostgroup_attributes + rci=configdb->check_table_structure((char *)"mysql_hostgroup_attributes",(char *)ADMIN_SQLITE_TABLE_MYSQL_HOSTGROUP_ATTRIBUTES_V2_5_0); + if (rci) { + // upgrade is required + proxy_warning("Detected version pre-v2.5.2 of mysql_hostgroup_attributes\n"); + proxy_warning("ONLINE UPGRADE of table mysql_hostgroup_attributes in progress\n"); + // drop mysql_hostgroup_attributes table with suffix _v250 + configdb->execute("DROP TABLE IF EXISTS mysql_hostgroup_attributes_v250"); + // rename current table to add suffix _v250 + configdb->execute("ALTER TABLE mysql_hostgroup_attributes RENAME TO mysql_hostgroup_attributes_v250"); + // create new table + configdb->build_table((char *)"mysql_hostgroup_attributes",(char *)ADMIN_SQLITE_TABLE_MYSQL_HOSTGROUP_ATTRIBUTES,false); + // copy fields from old table + configdb->execute( + "INSERT INTO mysql_hostgroup_attributes (" + " hostgroup_id, max_num_online_servers, autocommit, free_connections_pct, init_connect, multiplex," + " connection_warming, throttle_connections_per_sec, ignore_session_variables, comment" + ") SELECT" + " hostgroup_id, max_num_online_servers, autocommit, free_connections_pct, init_connect, multiplex," + " connection_warming, throttle_connections_per_sec, ignore_session_variables, comment" + " FROM mysql_hostgroup_attributes_v250" + ); + } configdb->execute("PRAGMA foreign_keys = ON"); + } From d5137fe361eb1907eca9f2553728dd3e6933b435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Sun, 7 May 2023 21:50:59 +0200 Subject: [PATCH 2/7] Add test for 'mysql_hostgroup_attributes' entry 'servers_defaults' --- ...ostgroup_attributes-servers_defaults-t.cpp | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 test/tap/tests/mysql_hostgroup_attributes-servers_defaults-t.cpp diff --git a/test/tap/tests/mysql_hostgroup_attributes-servers_defaults-t.cpp b/test/tap/tests/mysql_hostgroup_attributes-servers_defaults-t.cpp new file mode 100644 index 000000000..3f81424d1 --- /dev/null +++ b/test/tap/tests/mysql_hostgroup_attributes-servers_defaults-t.cpp @@ -0,0 +1,97 @@ +/** + * @file mysql_hostgroup_attributes-servers_defaults-t.cpp + * @brief Simple test checking that 'servers_defaults' values are correctly LOAD TO RUNTIME for + * 'mysql_hostgroup_attributes'. + */ + +#include +#include + +#include +#include + +#include "json.hpp" + +#include "tap.h" +#include "utils.h" +#include "command_line.h" + +using nlohmann::json; +using std::string; + +int update_and_check_servers_defaults(MYSQL* admin, const json& j_servers_defaults) { + const string INSERT_QUERY { + "INSERT INTO mysql_hostgroup_attributes (hostgroup_id, servers_defaults)" + " VALUES (0, '" + j_servers_defaults.dump() + "')" + }; + + // Since the value we are updating in a single field, we can just DELETE each time + MYSQL_QUERY_T(admin, "DELETE FROM mysql_hostgroup_attributes"); + MYSQL_QUERY_T(admin, INSERT_QUERY.c_str()); + MYSQL_QUERY_T(admin, "LOAD MYSQL SERVERS TO RUNTIME"); + + diag("Checking that RUNTIME value matches INSERTED"); + MYSQL_QUERY_T(admin, "SELECT servers_defaults FROM mysql_hostgroup_attributes WHERE hostgroup_id=0"); + MYSQL_RES* myres = mysql_store_result(admin); + MYSQL_ROW myrow = mysql_fetch_row(myres); + json j_runtime_servers_defaults {}; + + if (myrow && myrow[0]) { + try { + j_runtime_servers_defaults = json::parse(myrow[0]); + } catch (const std::exception& e) { + diag("ERROR: Failed to parse retrieved 'servers_defaults' - '%s'", e.what()); + } + } + + mysql_free_result(myres); + + ok( + j_servers_defaults == j_runtime_servers_defaults, + "INSERTED 'servers_defaults' should match RUNTIME value - Exp: `%s`, Act: `%s`", + j_servers_defaults.dump().c_str(), j_runtime_servers_defaults.dump().c_str() + ); + + return EXIT_SUCCESS; +} + +int main(int, char**) { + plan(3); + + CommandLine cl; + + if (cl.getEnv()) { + diag("Failed to get the required environmental variables."); + return EXIT_FAILURE; + } + + MYSQL* admin = mysql_init(NULL); + + if (!mysql_real_connect(admin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin)); + return EXIT_FAILURE; + } + + // Cleanup + MYSQL_QUERY_T(admin, "DELETE FROM mysql_hostgroup_attributes"); + MYSQL_QUERY_T(admin, "LOAD MYSQL SERVERS TO RUNTIME"); + + // 1. Set multiple valid values, and check it's properly set. + json j_multiple_vals { { "weight", 100 }, { "max_connections", 10000 }, { "use_ssl", 1}, }; + update_and_check_servers_defaults(admin, j_multiple_vals); + + // 2. Set single valid value, and see others values have been reset. + json j_single_val { { "weight", 100 } }; + update_and_check_servers_defaults(admin, j_single_val); + + // 3. NOTE: Setting only valid keys isn't enforced right now. In the future, we could at least report a + // sensible error via error log to more easily detect typos. + json j_inv_val { { "weiht", 100 } }; + update_and_check_servers_defaults(admin, j_inv_val); + +cleanup: + + mysql_close(admin); + + return exit_status(); +} From 722ffb310df3f0bae98334370e5359a293e26697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Mon, 8 May 2023 12:54:24 +0200 Subject: [PATCH 3/7] Fix minor doc typos in 'MySrvC' definition --- include/MySQL_HostGroups_Manager.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/include/MySQL_HostGroups_Manager.h b/include/MySQL_HostGroups_Manager.h index 5be5ae801..675cf090e 100644 --- a/include/MySQL_HostGroups_Manager.h +++ b/include/MySQL_HostGroups_Manager.h @@ -155,7 +155,7 @@ class MySrvC { // MySQL Server Container unsigned int connect_OK; unsigned int connect_ERR; unsigned int cur_replication_lag_count; - // note that these variables are in microsecond, while user defines max lantency in millisecond + // note that these variables are in microsecond, while user defines max latency in millisecond unsigned int current_latency_us; unsigned int max_latency_us; time_t time_last_detected_error; @@ -171,7 +171,7 @@ class MySrvC { // MySQL Server Container MySrvConnList *ConnectionsUsed; MySrvConnList *ConnectionsFree; /** - * @brief ConstructConstructss a new MySQL Server Container. + * @brief Constructs a new MySQL Server Container. * @details For 'server_defaults' parameters, if '-1' is supplied, they try to be obtained from * 'servers_defaults' entry from 'mysql_hostgroup_attributes' when adding the server to it's target * hostgroup(via 'MySQL_HostGroups_Manager::add'), if not found, value is set with 'mysql_servers' @@ -197,9 +197,8 @@ class MySrvC { // MySQL Server Container void connect_error(int, bool get_mutex=true); void shun_and_killall(); /** - * Update the maximum number of used connections - * @return - * the maximum number of used connections + * @brief Update the maximum number of used connections + * @return The maximum number of used connections */ unsigned int update_max_connections_used() { From 9a351dcd16c3683d5068dd77c0db38373e9c25a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Mon, 8 May 2023 12:56:33 +0200 Subject: [PATCH 4/7] Improve coverage for tap test for 'servers_defaults' --- ...ostgroup_attributes-servers_defaults-t.cpp | 115 +++++++++++++++--- 1 file changed, 97 insertions(+), 18 deletions(-) diff --git a/test/tap/tests/mysql_hostgroup_attributes-servers_defaults-t.cpp b/test/tap/tests/mysql_hostgroup_attributes-servers_defaults-t.cpp index 3f81424d1..0f8206ee0 100644 --- a/test/tap/tests/mysql_hostgroup_attributes-servers_defaults-t.cpp +++ b/test/tap/tests/mysql_hostgroup_attributes-servers_defaults-t.cpp @@ -1,11 +1,19 @@ /** * @file mysql_hostgroup_attributes-servers_defaults-t.cpp - * @brief Simple test checking that 'servers_defaults' values are correctly LOAD TO RUNTIME for - * 'mysql_hostgroup_attributes'. + * @brief Simple test doing several checks for 'servers_defaults': + * 1. Correct values can be inserted and check on 'mysql_hostgroup_attributes'. + * 2. Correct LOAD TO RUNTIME. + * 3. Correct SAVE TO DISK. + * 4. Runtime values reset when a single new value is set. This doesn't reflect the internal memory values. + * 5. Non-supported key fields in the JSON are allowed. This is by design. + * 6. Invalid values can be inserted, but is reported in the error log. + * 7. Invalid type values can be inserted, but is reported in the error log. */ #include #include +#include +#include #include #include @@ -18,6 +26,7 @@ using nlohmann::json; using std::string; +using std::fstream; int update_and_check_servers_defaults(MYSQL* admin, const json& j_servers_defaults) { const string INSERT_QUERY { @@ -31,20 +40,27 @@ int update_and_check_servers_defaults(MYSQL* admin, const json& j_servers_defaul MYSQL_QUERY_T(admin, "LOAD MYSQL SERVERS TO RUNTIME"); diag("Checking that RUNTIME value matches INSERTED"); - MYSQL_QUERY_T(admin, "SELECT servers_defaults FROM mysql_hostgroup_attributes WHERE hostgroup_id=0"); - MYSQL_RES* myres = mysql_store_result(admin); - MYSQL_ROW myrow = mysql_fetch_row(myres); - json j_runtime_servers_defaults {}; - - if (myrow && myrow[0]) { - try { - j_runtime_servers_defaults = json::parse(myrow[0]); - } catch (const std::exception& e) { - diag("ERROR: Failed to parse retrieved 'servers_defaults' - '%s'", e.what()); + MYSQL_QUERY_T(admin, "SELECT servers_defaults FROM runtime_mysql_hostgroup_attributes WHERE hostgroup_id=0"); + + const auto extract_json_result = [] (MYSQL* admin) { + json j_result {}; + MYSQL_RES* myres = mysql_store_result(admin); + MYSQL_ROW myrow = mysql_fetch_row(myres); + + if (myrow && myrow[0]) { + try { + j_result = json::parse(myrow[0]); + } catch (const std::exception& e) { + diag("ERROR: Failed to parse retrieved 'servers_defaults' - '%s'", e.what()); + } } - } - mysql_free_result(myres); + mysql_free_result(myres); + + return j_result; + }; + + json j_runtime_servers_defaults = extract_json_result(admin); ok( j_servers_defaults == j_runtime_servers_defaults, @@ -52,11 +68,45 @@ int update_and_check_servers_defaults(MYSQL* admin, const json& j_servers_defaul j_servers_defaults.dump().c_str(), j_runtime_servers_defaults.dump().c_str() ); + MYSQL_QUERY_T(admin, "SAVE MYSQL SERVERS TO DISK"); + diag("Checking that DISK value matches INSERTED"); + MYSQL_QUERY_T(admin, "SELECT servers_defaults FROM disk.mysql_hostgroup_attributes WHERE hostgroup_id=0"); + json j_disk_servers_defaults = extract_json_result(admin); + + ok( + j_servers_defaults == j_disk_servers_defaults, + "INSERTED 'servers_defaults' should match RUNTIME value - Exp: `%s`, Act: `%s`", + j_servers_defaults.dump().c_str(), j_disk_servers_defaults.dump().c_str() + ); + return EXIT_SUCCESS; } +void check_matching_logline(fstream& f_log, string regex) { + // Minimal wait for the error log to be written + usleep(500 * 1000); + + std::vector matching_lines { get_matching_lines(f_log, regex) }; + for (const line_match_t& line_match : matching_lines) { + diag( + "Found matching logline - pos: %ld, line: `%s`", + static_cast(std::get(line_match)), + std::get(line_match).c_str() + ); + } + + ok( + matching_lines.size() == 1, + "Expected to find an invalid value logline matching regex - found_lines: %ld", + matching_lines.size() + ); + + // Set the file to the end of stream + f_log.seekg(0, std::ios::end); +} + int main(int, char**) { - plan(3); + plan(12); CommandLine cl; @@ -65,6 +115,16 @@ int main(int, char**) { return EXIT_FAILURE; } + // Open the error log and fetch the final position + const string f_path { get_env("REGULAR_INFRA_DATADIR") + "/proxysql.log" }; + fstream f_log {}; + + int of_err = open_file_and_seek_end(f_path, f_log); + if (of_err) { + diag("Failed to open ProxySQL log file. Aborting further testing..."); + return EXIT_FAILURE; + } + MYSQL* admin = mysql_init(NULL); if (!mysql_real_connect(admin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { @@ -84,11 +144,30 @@ int main(int, char**) { json j_single_val { { "weight", 100 } }; update_and_check_servers_defaults(admin, j_single_val); - // 3. NOTE: Setting only valid keys isn't enforced right now. In the future, we could at least report a - // sensible error via error log to more easily detect typos. - json j_inv_val { { "weiht", 100 } }; + // 3. NOTE: Setting only valid keys isn't enforced right now. This is by design. + json j_inv_key { { "weiht", 100 } }; + update_and_check_servers_defaults(admin, j_inv_key); + + // 4. NOTE: Setting invalid value is allowed, error log should reflect the update error. + json j_inv_val { { "weight", -100 } }; update_and_check_servers_defaults(admin, j_inv_val); + const string inv_val_regex { + "\\[ERROR\\] Invalid value .+\\d supplied for 'mysql_hostgroup_attributes\\.servers_defaults\\.weight'" + " for hostgroup +\\d\\. Value NOT UPDATED\\." + }; + check_matching_logline(f_log, inv_val_regex); + + // 5. NOTE: Setting invalid type is allowed, error log should reflect the update error. + json j_inv_val_type { { "weight", "100" } }; + update_and_check_servers_defaults(admin, j_inv_val_type); + + const string inv_type_regex { + "\\[ERROR\\] Invalid type .*\\(\\d\\) supplied for 'mysql_hostgroup_attributes\\.servers_defaults\\.weight'" + " for hostgroup +\\d\\. Value NOT UPDATED\\." + }; + check_matching_logline(f_log, inv_type_regex); + cleanup: mysql_close(admin); From f8e705df5af2e0b3fc8538b803d3ab427fadae73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Mon, 8 May 2023 17:25:51 +0200 Subject: [PATCH 5/7] Make 'server_defaults' tap test recover previous disk 'mysql_hostgroup_attributes' --- .../tests/mysql_hostgroup_attributes-servers_defaults-t.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/tap/tests/mysql_hostgroup_attributes-servers_defaults-t.cpp b/test/tap/tests/mysql_hostgroup_attributes-servers_defaults-t.cpp index 0f8206ee0..4d1e3f64a 100644 --- a/test/tap/tests/mysql_hostgroup_attributes-servers_defaults-t.cpp +++ b/test/tap/tests/mysql_hostgroup_attributes-servers_defaults-t.cpp @@ -133,6 +133,8 @@ int main(int, char**) { } // Cleanup + MYSQL_QUERY_T(admin, "DROP TABLE mysql_hostgroup_attributes_0508"); + MYSQL_QUERY_T(admin, "CREATE TABLE mysql_hostgroup_attributes_0508 AS SELECT * FROM disk.mysql_hostgroup_attributes"); MYSQL_QUERY_T(admin, "DELETE FROM mysql_hostgroup_attributes"); MYSQL_QUERY_T(admin, "LOAD MYSQL SERVERS TO RUNTIME"); @@ -170,6 +172,8 @@ int main(int, char**) { cleanup: + MYSQL_QUERY_T(admin, "DELETE FROM disk.mysql_hostgroup_attributes"); + MYSQL_QUERY_T(admin, "INSERT INTO disk.mysql_hostgroup_attributes SELECT * FROM mysql_hostgroup_attributes_0508"); mysql_close(admin); return exit_status(); From af3dc2f85a5a373410c7ef0d5614305752554776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Mon, 8 May 2023 17:28:21 +0200 Subject: [PATCH 6/7] Add support for 'servers_defaults' column to 'test_mysql_hostgroup_attributes-1' --- .../tests/test_mysql_hostgroup_attributes-1-t.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/test/tap/tests/test_mysql_hostgroup_attributes-1-t.cpp b/test/tap/tests/test_mysql_hostgroup_attributes-1-t.cpp index f37be3912..85ee8211f 100644 --- a/test/tap/tests/test_mysql_hostgroup_attributes-1-t.cpp +++ b/test/tap/tests/test_mysql_hostgroup_attributes-1-t.cpp @@ -61,15 +61,22 @@ int main(int argc, char** argv) { std::unordered_map queries_and_checksums = { { "0x666CFBEEDB76EE9C", - "INSERT INTO mysql_hostgroup_attributes VALUES (19,1,1,10,'',1,1,10000,'','')" + "INSERT INTO mysql_hostgroup_attributes VALUES (19,1,1,10,'',1,1,10000,'','','')" }, { "0xE2FC2A5FEE8D18DC", - "INSERT INTO mysql_hostgroup_attributes VALUES (19,1,1,10,'',1,1,10000,'',''),(18,2,-1,20,'SET sql_mode=\"\"',0,0,100,'','hello world')", + "INSERT INTO mysql_hostgroup_attributes VALUES (19,1,1,10,'',1,1,10000,'','',''),(18,2,-1,20,'SET sql_mode=\"\"',0,0,100,'','','hello world')", }, { "0xFACE1C64FF1C373E", - "INSERT INTO mysql_hostgroup_attributes VALUES (19,1,1,10,'',1,1,10000,'',''),(18,2,-1,20,'SET sql_mode=\"\"',0,0,100,'','hello world'),(17,0,0,30,'SET long_query_time=0',1,0,123,'{\"session_variables\":[\"tmp_table_size\",\"join_buffer_size\"]}','filtering variables')" + "INSERT INTO mysql_hostgroup_attributes VALUES (19,1,1,10,'',1,1,10000,'','',''),(18,2,-1,20,'SET sql_mode=\"\"',0,0,100,'','','hello world'),(17,0,0,30,'SET long_query_time=0',1,0,123,'{\"session_variables\":[\"tmp_table_size\",\"join_buffer_size\"]}','','filtering variables')" + }, + { + "0x161B2F2BB35BA05E", + "INSERT INTO mysql_hostgroup_attributes VALUES (19,1,1,10,'',1,1,10000,'','','')," + "(18,2,-1,20,'SET sql_mode=\"\"',0,0,100,'','','hello world')," + "(17,0,0,30,'SET long_query_time=0',1,0,123,'{\"session_variables\":[\"tmp_table_size\",\"join_buffer_size\"]}','','filtering variables')," + "(20,3,-1,40,'SET sql_mode=\"\"',1,0,124,'{\"session_variables\":[\"tmp_table_size\",\"join_buffer_size\"]}','{\"weight\": 100, \"max_connections\": 1000}','servers defaults')" }, }; From 1ad30c6ca198a5ef7bbdd37afc093f2aef7665ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Mon, 8 May 2023 20:23:24 +0200 Subject: [PATCH 7/7] Fix invalid query for 'servers_defaults' tap test --- .../tap/tests/mysql_hostgroup_attributes-servers_defaults-t.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tap/tests/mysql_hostgroup_attributes-servers_defaults-t.cpp b/test/tap/tests/mysql_hostgroup_attributes-servers_defaults-t.cpp index 4d1e3f64a..06a190721 100644 --- a/test/tap/tests/mysql_hostgroup_attributes-servers_defaults-t.cpp +++ b/test/tap/tests/mysql_hostgroup_attributes-servers_defaults-t.cpp @@ -133,7 +133,7 @@ int main(int, char**) { } // Cleanup - MYSQL_QUERY_T(admin, "DROP TABLE mysql_hostgroup_attributes_0508"); + MYSQL_QUERY_T(admin, "DROP TABLE IF EXISTS mysql_hostgroup_attributes_0508"); MYSQL_QUERY_T(admin, "CREATE TABLE mysql_hostgroup_attributes_0508 AS SELECT * FROM disk.mysql_hostgroup_attributes"); MYSQL_QUERY_T(admin, "DELETE FROM mysql_hostgroup_attributes"); MYSQL_QUERY_T(admin, "LOAD MYSQL SERVERS TO RUNTIME");