From ee8110a51540f0d5209946f0fa22d4d1ccfc9121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Fri, 18 Aug 2017 00:29:53 +0200 Subject: [PATCH 1/3] Enhancement on Cluster solution This commit introduces: 2 new tables: * `runtime_checksums_values` : stores checksums of configurations in runtime. For now for `mysql_query_rules`, `mysql_servers` and `mysql_users` * `stats_proxysql_servers_checksums` : when clustering is enabled, it collects all metrics from `runtime_checksums_values` from all its peers 3 new global variables that defines it checksum needs to be generated during `LOAD ... TO RUNTIME` * `admin-checksum_mysql_query_rules` * `admin-checksum_mysql_servers` * `admin-checksum_mysql_users` ProxySQL Cluster connections now have timeouts: * 1 second timeout for CONNECT and WRITE * 60 seconds timeout for READ (useful for long poll) --- include/ProxySQL_Cluster.hpp | 18 +++ include/proxysql_admin.h | 9 +- include/proxysql_glovars.hpp | 39 ++++- include/sqlite3db.h | 2 +- lib/MySQL_HostGroups_Manager.cpp | 66 +++++++++ lib/ProxySQL_Admin.cpp | 245 ++++++++++++++++++++++++++++++- lib/ProxySQL_Cluster.cpp | 163 +++++++++++++++++++- lib/ProxySQL_GloVars.cpp | 2 + lib/sqlite3db.cpp | 3 +- 9 files changed, 535 insertions(+), 12 deletions(-) diff --git a/include/ProxySQL_Cluster.hpp b/include/ProxySQL_Cluster.hpp index 1e27d58a6..994cbb050 100644 --- a/include/ProxySQL_Cluster.hpp +++ b/include/ProxySQL_Cluster.hpp @@ -39,6 +39,7 @@ class ProxySQL_Node_Entry { int metrics_idx_prev; int metrics_idx; ProxySQL_Node_Metrics **metrics; + public: uint64_t get_hash(); ProxySQL_Node_Entry(char *_hostname, uint16_t _port, uint64_t _weight, char *_comment); @@ -52,6 +53,7 @@ class ProxySQL_Node_Entry { } void set_comment(char *a); // note, this is strdup() void set_metrics(MYSQL_RES *_r, unsigned long long _response_time); + void set_checksums(MYSQL_RES *_r); char *get_hostname() { // note, NO strdup() return hostname; } @@ -60,6 +62,14 @@ class ProxySQL_Node_Entry { } ProxySQL_Node_Metrics * get_metrics_curr(); ProxySQL_Node_Metrics * get_metrics_prev(); + struct { + ProxySQL_Checksum_Value admin_variables; + ProxySQL_Checksum_Value mysql_variables; + ProxySQL_Checksum_Value mysql_query_rules; + ProxySQL_Checksum_Value mysql_servers; + ProxySQL_Checksum_Value mysql_users; + ProxySQL_Checksum_Value proxysql_servers; + } checksums_values; }; class ProxySQL_Cluster_Nodes { @@ -74,7 +84,9 @@ class ProxySQL_Cluster_Nodes { ~ProxySQL_Cluster_Nodes(); void load_servers_list(SQLite3_result *); bool Update_Node_Metrics(char * _h, uint16_t _p, MYSQL_RES *_r, unsigned long long _response_time); + bool Update_Node_Checksums(char * _h, uint16_t _p, MYSQL_RES *_r); SQLite3_result * dump_table_proxysql_servers(); + SQLite3_result * stats_proxysql_servers_checksums(); SQLite3_result * stats_proxysql_servers_metrics(); }; @@ -101,9 +113,15 @@ class ProxySQL_Cluster { bool Update_Node_Metrics(char * _h, uint16_t _p, MYSQL_RES *_r, unsigned long long _response_time) { return nodes.Update_Node_Metrics(_h, _p, _r, _response_time); } + bool Update_Node_Checksums(char * _h, uint16_t _p, MYSQL_RES *_r) { + return nodes.Update_Node_Checksums(_h, _p, _r); + } SQLite3_result *dump_table_proxysql_servers() { return nodes.dump_table_proxysql_servers(); } + SQLite3_result * get_stats_proxysql_servers_checksums() { + return nodes.stats_proxysql_servers_checksums(); + } SQLite3_result * get_stats_proxysql_servers_metrics() { return nodes.stats_proxysql_servers_metrics(); } diff --git a/include/proxysql_admin.h b/include/proxysql_admin.h index e479cb3d1..335fe5c0d 100644 --- a/include/proxysql_admin.h +++ b/include/proxysql_admin.h @@ -111,7 +111,7 @@ class ProxySQL_Admin { void __insert_or_replace_disktable_select_maintable(); void __attach_db(SQLite3DB *db1, SQLite3DB *db2, char *alias); - void __add_active_users(enum cred_username_type usertype, char *user=NULL); + void __add_active_users(enum cred_username_type usertype, char *user=NULL, uint64_t *hash1 = NULL); void __delete_inactive_users(enum cred_username_type usertype); void add_admin_users(); void __refresh_users(); @@ -141,6 +141,11 @@ class ProxySQL_Admin { void *opt; void **re; } match_regexes; + struct { + bool checksum_mysql_query_rules; + bool checksum_mysql_servers; + bool checksum_mysql_users; + } checksum_variables; void public_add_active_users(enum cred_username_type usertype, char *user=NULL) { __add_active_users(usertype, user); } @@ -201,6 +206,7 @@ class ProxySQL_Admin { void stats___mysql_global(); void stats___mysql_users(); + void stats___proxysql_servers_checksums(); void stats___proxysql_servers_metrics(); int Read_Global_Variables_from_configfile(const char *prefix); @@ -227,6 +233,7 @@ class ProxySQL_Admin { void flush_proxysql_servers__from_memory_to_disk(); void flush_proxysql_servers__from_disk_to_memory(); void save_proxysql_servers_runtime_to_database(bool); + void dump_checksums_values_table(); }; #endif /* __CLASS_PROXYSQL_ADMIN_H */ diff --git a/include/proxysql_glovars.hpp b/include/proxysql_glovars.hpp index 2e132a512..07cbdedb5 100644 --- a/include/proxysql_glovars.hpp +++ b/include/proxysql_glovars.hpp @@ -8,6 +8,33 @@ namespace ez { class ezOptionParser; }; +class ProxySQL_Checksum_Value { + public: + char *checksum; + unsigned long long version; + unsigned long long epoch; + ProxySQL_Checksum_Value() { + checksum = (char *)malloc(20); + memset(checksum,0,20); + version = 0; + epoch = 0; + } + void set_checksum(char *c) { + memset(checksum,0,20); + strncpy(checksum,c,18); + for (int i=2; i<18; i++) { + if (checksum[i]==' ' || checksum[i]==0) { + checksum[i]='0'; + } + } + + } + ~ProxySQL_Checksum_Value() { + free(checksum); + checksum = NULL; + } +}; + class ProxySQL_GlobalVariables { public: ez::ezOptionParser *opt; @@ -48,7 +75,7 @@ class ProxySQL_GlobalVariables { char *pidfile; bool restart_on_error; int restart_delay; - SSL_CTX *ssl_ctx; + SSL_CTX *ssl_ctx; } global; struct mysql { char *server_version; @@ -59,6 +86,16 @@ class ProxySQL_GlobalVariables { unsigned long stack_memory_admin_threads; unsigned long stack_memory_cluster_threads; } statuses; + pthread_mutex_t checksum_mutex; + time_t epoch_version; + struct { + ProxySQL_Checksum_Value admin_variables; + ProxySQL_Checksum_Value mysql_query_rules; + ProxySQL_Checksum_Value mysql_servers; + ProxySQL_Checksum_Value mysql_users; + ProxySQL_Checksum_Value mysql_variables; + ProxySQL_Checksum_Value proxysql_servers; + } checksums_values; ProxySQL_GlobalVariables(); ~ProxySQL_GlobalVariables(); void process_opts_pre(); diff --git a/include/sqlite3db.h b/include/sqlite3db.h index 0414a2c0d..6987c65b3 100644 --- a/include/sqlite3db.h +++ b/include/sqlite3db.h @@ -110,7 +110,7 @@ class SQLite3_result { int columns; int rows_count; char *checksum(); - int64_t raw_checksum(); + uint64_t raw_checksum(); std::vector column_definition; std::vector rows; diff --git a/lib/MySQL_HostGroups_Manager.cpp b/lib/MySQL_HostGroups_Manager.cpp index ed6e80997..e509a49f0 100644 --- a/lib/MySQL_HostGroups_Manager.cpp +++ b/lib/MySQL_HostGroups_Manager.cpp @@ -1,5 +1,6 @@ #include "proxysql.h" #include "cpp.h" +#include "SpookyV2.h" #define char_malloc (char *)malloc #define itostr(__s, __i) { __s=char_malloc(32); sprintf(__s, "%lld", __i); } @@ -698,6 +699,69 @@ bool MySQL_HostGroups_Manager::commit() { mydb->execute("DELETE FROM mysql_group_replication_hostgroups"); generate_mysql_group_replication_hostgroups_table(); } + + + if ( GloAdmin && GloAdmin->checksum_variables.checksum_mysql_servers ) { + uint64_t hash1, hash2; + SpookyHash myhash; + char buf[80]; + myhash.Init(19,3); + MySrvC *mysrvc=NULL; + for (unsigned int i=0; ilen; i++) { + MyHGC *myhgc=(MyHGC *)MyHostGroups->index(i); + for (unsigned int j=0; jmysrvs->servers->len; j++) { + mysrvc=myhgc->mysrvs->idx(j); + // hostgroup + sprintf(buf,"%u",mysrvc->myhgc->hid); + myhash.Update(buf,strlen(buf)); + // hoatname + if (mysrvc->address) { + myhash.Update(mysrvc->address,strlen(mysrvc->address)); + } else { myhash.Update("",0); } + // port + sprintf(buf,"%u",mysrvc->port); + myhash.Update(buf,strlen(buf)); + // status + sprintf(buf,"%u",mysrvc->status); + myhash.Update(buf,strlen(buf)); + // weight + sprintf(buf,"%u",mysrvc->weight); + myhash.Update(buf,strlen(buf)); + // compression + sprintf(buf,"%u",mysrvc->compression); + myhash.Update(buf,strlen(buf)); + // max_connections + sprintf(buf,"%u",mysrvc->max_connections); + myhash.Update(buf,strlen(buf)); + // max_replication_lag + sprintf(buf,"%u",mysrvc->max_replication_lag); + myhash.Update(buf,strlen(buf)); + // use_ssl + sprintf(buf,"%u",mysrvc->use_ssl); + myhash.Update(buf,strlen(buf)); + // max_latency_ms + sprintf(buf,"%u",mysrvc->max_latency_us); + myhash.Update(buf,strlen(buf)); + if (mysrvc->comment) { + myhash.Update(mysrvc->comment,strlen(mysrvc->comment)); + } else { myhash.Update("",0); } + } + } + myhash.Final(&hash1, &hash2); + uint32_t d32[2]; + memcpy(&d32,&hash1,sizeof(hash1)); + sprintf(buf,"0x%0X%0X", d32[0], d32[1]); + pthread_mutex_lock(&GloVars.checksum_mutex); + GloVars.checksums_values.mysql_servers.set_checksum(buf); + GloVars.checksums_values.mysql_servers.version++; + //struct timespec ts; + //clock_gettime(CLOCK_REALTIME, &ts); + time_t t = time(NULL); + GloVars.checksums_values.mysql_servers.epoch = t; + GloVars.epoch_version = t; + pthread_mutex_unlock(&GloVars.checksum_mutex); + } + __sync_fetch_and_add(&status.servers_table_version,1); pthread_cond_broadcast(&status.servers_table_version_cond); pthread_mutex_unlock(&status.servers_table_version_lock); @@ -726,6 +790,8 @@ void MySQL_HostGroups_Manager::purge_mysql_servers_table() { } } + + void MySQL_HostGroups_Manager::generate_mysql_servers_table(int *_onlyhg) { int rc; sqlite3_stmt *statement1=NULL; diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index d5fdf22fc..2a31fa80c 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -170,6 +170,8 @@ pthread_mutex_t users_mutex = PTHREAD_MUTEX_INITIALIZER; #define ADMIN_SQLITE_RUNTIME_MYSQL_USERS "CREATE TABLE runtime_mysql_users (username VARCHAR NOT NULL , password VARCHAR , active INT CHECK (active IN (0,1)) NOT NULL DEFAULT 1 , use_ssl INT CHECK (use_ssl IN (0,1)) NOT NULL DEFAULT 0 , default_hostgroup INT NOT NULL DEFAULT 0 , default_schema VARCHAR , schema_locked INT CHECK (schema_locked IN (0,1)) NOT NULL DEFAULT 0 , transaction_persistent INT CHECK (transaction_persistent IN (0,1)) NOT NULL DEFAULT 1 , fast_forward INT CHECK (fast_forward IN (0,1)) NOT NULL DEFAULT 0 , backend INT CHECK (backend IN (0,1)) NOT NULL DEFAULT 1 , frontend INT CHECK (frontend IN (0,1)) NOT NULL DEFAULT 1 , max_connections INT CHECK (max_connections >=0) NOT NULL DEFAULT 10000 , PRIMARY KEY (username, backend) , UNIQUE (username, frontend))" +#define ADMIN_SQLITE_RUNTIME_CHECKSUMS_VALUES "CREATE TABLE runtime_checksums_values (name VARCHAR NOT NULL , version INT NOT NULL , epoch INT NOT NULL , checksum VARCHAR NOT NULL , PRIMARY KEY (name))" + // mysql_query_rules in v1.1.0 #define ADMIN_SQLITE_TABLE_MYSQL_QUERY_RULES_V1_1_0 "CREATE TABLE mysql_query_rules (rule_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , active INT CHECK (active IN (0,1)) NOT NULL DEFAULT 0 , username VARCHAR , schemaname VARCHAR , flagIN INT NOT NULL DEFAULT 0 , match_digest VARCHAR , match_pattern VARCHAR , negate_match_pattern INT CHECK (negate_match_pattern IN (0,1)) NOT NULL DEFAULT 0 , flagOUT INT , replace_pattern VARCHAR , destination_hostgroup INT DEFAULT NULL , cache_ttl INT CHECK(cache_ttl > 0) , reconnect INT CHECK (reconnect IN (0,1)) DEFAULT NULL , timeout INT UNSIGNED , delay INT UNSIGNED , error_msg VARCHAR , apply INT CHECK(apply IN (0,1)) NOT NULL DEFAULT 0)" @@ -262,6 +264,8 @@ pthread_mutex_t users_mutex = PTHREAD_MUTEX_INITIALIZER; #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) )" +#define STATS_SQLITE_TABLE_PROXYSQL_SERVERS_CHECKSUMS "CREATE TABLE stats_proxysql_servers_checksums (hostname VARCHAR NOT NULL , port INT NOT NULL DEFAULT 6032 , name VARCHAR NOT NULL , version INT NOT NULL , epoch INT NOT NULL , checksum VARCHAR NOT NULL , PRIMARY KEY (hostname, port, name) )" + static char * admin_variables_names[]= { @@ -277,6 +281,9 @@ static char * admin_variables_names[]= { (char *)"cluster_username", (char *)"cluster_password", (char *)"cluster_check_interval_ms", + (char *)"checksum_mysql_query_rules", + (char *)"checksum_mysql_servers", + (char *)"checksum_mysql_users", #ifdef DEBUG (char *)"debug", #endif /* DEBUG */ @@ -1470,9 +1477,11 @@ void ProxySQL_Admin::GenericRefreshStatistics(const char *query_no_space, unsign bool runtime_mysql_query_rules=false; bool runtime_proxysql_servers=false; + bool runtime_checksums_values=false; bool monitor_mysql_server_group_replication_log=false; + bool stats_proxysql_servers_checksums = false; bool stats_proxysql_servers_metrics = false; bool stats_proxysql_servers_status = false; @@ -1504,6 +1513,8 @@ void ProxySQL_Admin::GenericRefreshStatistics(const char *query_no_space, unsign if (strstr(query_no_space,"stats_mysql_users")) { stats_mysql_users=true; refresh=true; } + if (strstr(query_no_space,"stats_proxysql_servers_checksums")) + { stats_proxysql_servers_checksums = true; refresh = true; } if (strstr(query_no_space,"stats_proxysql_servers_metrics")) { stats_proxysql_servers_metrics = true; refresh = true; } if (strstr(query_no_space,"stats_proxysql_servers_status")) @@ -1534,6 +1545,9 @@ void ProxySQL_Admin::GenericRefreshStatistics(const char *query_no_space, unsign if (strstr(query_no_space,"runtime_proxysql_servers")) { runtime_proxysql_servers=true; refresh=true; } + if (strstr(query_no_space,"runtime_checksums_values")) { + runtime_checksums_values=true; refresh=true; + } } } if (strstr(query_no_space,"mysql_server_group_replication_log")) { @@ -1570,6 +1584,9 @@ void ProxySQL_Admin::GenericRefreshStatistics(const char *query_no_space, unsign if (stats_proxysql_servers_metrics) { stats___proxysql_servers_metrics(); } + if (stats_proxysql_servers_checksums) { + stats___proxysql_servers_checksums(); + } // if (stats_proxysql_servers_status) { // stats___proxysql_servers_status(); // } @@ -1599,6 +1616,9 @@ void ProxySQL_Admin::GenericRefreshStatistics(const char *query_no_space, unsign if (runtime_scheduler) { save_scheduler_runtime_to_database(true); } + if (runtime_checksums_values) { + dump_checksums_values_table(); + } } if (monitor_mysql_server_group_replication_log) { if (GloMyMon) { @@ -2738,6 +2758,9 @@ ProxySQL_Admin::ProxySQL_Admin() { variables.cluster_username=strdup((char *)""); variables.cluster_password=strdup((char *)""); variables.cluster_check_interval_ms=1000; + checksum_variables.checksum_mysql_query_rules = true; + checksum_variables.checksum_mysql_servers = true; + checksum_variables.checksum_mysql_users = true; #ifdef DEBUG variables.debug=GloVars.global.gdbg; #endif /* DEBUG */ @@ -2842,6 +2865,7 @@ bool ProxySQL_Admin::init() { insert_into_tables_defs(tables_defs_admin,"runtime_mysql_servers", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_SERVERS); insert_into_tables_defs(tables_defs_admin,"mysql_users", ADMIN_SQLITE_TABLE_MYSQL_USERS); insert_into_tables_defs(tables_defs_admin,"runtime_mysql_users", ADMIN_SQLITE_RUNTIME_MYSQL_USERS); + insert_into_tables_defs(tables_defs_admin,"runtime_checksums_values", ADMIN_SQLITE_RUNTIME_CHECKSUMS_VALUES); insert_into_tables_defs(tables_defs_admin,"runtime_mysql_replication_hostgroups", ADMIN_SQLITE_TABLE_RUNTIME_MYSQL_REPLICATION_HOSTGROUPS); insert_into_tables_defs(tables_defs_admin,"mysql_replication_hostgroups", ADMIN_SQLITE_TABLE_MYSQL_REPLICATION_HOSTGROUPS); insert_into_tables_defs(tables_defs_admin,"mysql_group_replication_hostgroups", ADMIN_SQLITE_TABLE_MYSQL_GROUP_REPLICATION_HOSTGROUPS); @@ -2887,6 +2911,7 @@ bool ProxySQL_Admin::init() { insert_into_tables_defs(tables_defs_admin,"proxysql_servers", ADMIN_SQLITE_TABLE_PROXYSQL_SERVERS); insert_into_tables_defs(tables_defs_config,"proxysql_servers", ADMIN_SQLITE_TABLE_PROXYSQL_SERVERS); insert_into_tables_defs(tables_defs_admin,"runtime_proxysql_servers", ADMIN_SQLITE_TABLE_RUNTIME_PROXYSQL_SERVERS); + 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); @@ -3326,6 +3351,15 @@ char * ProxySQL_Admin::get_variable(char *name) { if (!strcasecmp(name,"hash_passwords")) { return strdup((variables.hash_passwords ? "true" : "false")); } + if (!strcasecmp(name,"checksum_mysql_query_rules")) { + return strdup((checksum_variables.checksum_mysql_query_rules ? "true" : "false")); + } + if (!strcasecmp(name,"checksum_mysql_servers")) { + return strdup((checksum_variables.checksum_mysql_servers ? "true" : "false")); + } + if (!strcasecmp(name,"checksum_mysql_users")) { + return strdup((checksum_variables.checksum_mysql_users ? "true" : "false")); + } #ifdef DEBUG if (!strcasecmp(name,"debug")) { return strdup((variables.debug ? "true" : "false")); @@ -3535,6 +3569,39 @@ bool ProxySQL_Admin::set_variable(char *name, char *value) { // this is the pub } return false; } + if (!strcasecmp(name,"checksum_mysql_query_rules")) { + if (strcasecmp(value,"true")==0 || strcasecmp(value,"1")==0) { + checksum_variables.checksum_mysql_query_rules=true; + return true; + } + if (strcasecmp(value,"false")==0 || strcasecmp(value,"0")==0) { + checksum_variables.checksum_mysql_query_rules=false; + return true; + } + return false; + } + if (!strcasecmp(name,"checksum_mysql_servers")) { + if (strcasecmp(value,"true")==0 || strcasecmp(value,"1")==0) { + checksum_variables.checksum_mysql_servers=true; + return true; + } + if (strcasecmp(value,"false")==0 || strcasecmp(value,"0")==0) { + checksum_variables.checksum_mysql_servers=false; + return true; + } + return false; + } + if (!strcasecmp(name,"checksum_mysql_users")) { + if (strcasecmp(value,"true")==0 || strcasecmp(value,"1")==0) { + checksum_variables.checksum_mysql_users=true; + return true; + } + if (strcasecmp(value,"false")==0 || strcasecmp(value,"0")==0) { + checksum_variables.checksum_mysql_users=false; + return true; + } + return false; + } if (!strcasecmp(name,"read_only")) { if (strcasecmp(value,"true")==0 || strcasecmp(value,"1")==0) { variables.admin_read_only=true; @@ -3943,6 +4010,37 @@ void ProxySQL_Admin::stats___mysql_query_rules() { delete resultset; } +void ProxySQL_Admin::stats___proxysql_servers_checksums() { + statsdb->execute("BEGIN"); + statsdb->execute("DELETE FROM stats_proxysql_servers_checksums"); + SQLite3_result *resultset=NULL; + resultset=GloProxyCluster->get_stats_proxysql_servers_checksums(); + if (resultset) { + int rc; + sqlite3_stmt *statement1=NULL; + sqlite3 *mydb3=statsdb->get_db(); + char *query1=NULL; + query1=(char *)"INSERT INTO stats_proxysql_servers_checksums VALUES (?1, ?2, ?3, ?4, ?5, ?6)"; + rc=sqlite3_prepare_v2(mydb3, query1, -1, &statement1, 0); + assert(rc==SQLITE_OK); + for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { + SQLite3_row *r1=*it; + rc=sqlite3_bind_text(statement1, 1, r1->fields[0], -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); + rc=sqlite3_bind_int64(statement1, 2, atoi(r1->fields[1])); assert(rc==SQLITE_OK); + rc=sqlite3_bind_text(statement1, 3, r1->fields[2], -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); + rc=sqlite3_bind_int64(statement1, 4, atoi(r1->fields[3])); assert(rc==SQLITE_OK); + rc=sqlite3_bind_int64(statement1, 5, atoi(r1->fields[4])); assert(rc==SQLITE_OK); + rc=sqlite3_bind_text(statement1, 6, r1->fields[5], -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); + SAFE_SQLITE3_STEP(statement1); + rc=sqlite3_clear_bindings(statement1); assert(rc==SQLITE_OK); + rc=sqlite3_reset(statement1); assert(rc==SQLITE_OK); + } + sqlite3_finalize(statement1); + } + statsdb->execute("COMMIT"); + delete resultset; +} + void ProxySQL_Admin::stats___proxysql_servers_metrics() { //SQLite3_result * resultset=GloProxyCluster->get_stats_proxysql_servers_metrics(); //if (resultset==NULL) return; @@ -4464,16 +4562,41 @@ void ProxySQL_Admin::add_admin_users() { } void ProxySQL_Admin::__refresh_users() { + bool calculate_checksum = false; + if (checksum_variables.checksum_mysql_servers) { + calculate_checksum = true; + } + if (calculate_checksum) + pthread_mutex_lock(&GloVars.checksum_mutex); __delete_inactive_users(USERNAME_BACKEND); __delete_inactive_users(USERNAME_FRONTEND); GloMyAuth->set_all_inactive(USERNAME_BACKEND); GloMyAuth->set_all_inactive(USERNAME_FRONTEND); add_admin_users(); - __add_active_users(USERNAME_BACKEND); - __add_active_users(USERNAME_FRONTEND); + uint64_t hashB, hashF; + if (calculate_checksum) { + __add_active_users(USERNAME_BACKEND, NULL, &hashB); + __add_active_users(USERNAME_FRONTEND, NULL, &hashF); + } else { + __add_active_users(USERNAME_BACKEND); + __add_active_users(USERNAME_FRONTEND); + } GloMyAuth->remove_inactives(USERNAME_BACKEND); GloMyAuth->remove_inactives(USERNAME_FRONTEND); set_variable((char *)"admin_credentials",(char *)""); + if (calculate_checksum) { + uint64_t hash1 = hashB + hashF; // overflow allowed + uint32_t d32[2]; + char buf[20]; + memcpy(&d32, &hash1, sizeof(hash1)); + sprintf(buf,"0x%0X%0X", d32[0], d32[1]); + GloVars.checksums_values.mysql_users.set_checksum(buf); + GloVars.checksums_values.mysql_users.version++; + time_t t = time(NULL); + GloVars.checksums_values.mysql_users.epoch = t; + GloVars.epoch_version = t; + pthread_mutex_unlock(&GloVars.checksum_mutex); + } } void ProxySQL_Admin::send_MySQL_OK(MySQL_Protocol *myprot, char *msg, int rows) { @@ -4514,10 +4637,14 @@ void ProxySQL_Admin::__delete_inactive_users(enum cred_username_type usertype) { } #define ADDUSER_STMT_RAW -void ProxySQL_Admin::__add_active_users(enum cred_username_type usertype, char *__user) { +void ProxySQL_Admin::__add_active_users(enum cred_username_type usertype, char *__user, uint64_t *hash1) { char *error=NULL; int cols=0; int affected_rows=0; + SpookyHash myhash; + if (hash1) { + myhash.Init(19,3); + } #ifdef ADDUSER_STMT_RAW sqlite3_stmt *statement=NULL; #else @@ -4526,7 +4653,11 @@ void ProxySQL_Admin::__add_active_users(enum cred_username_type usertype, char * char *str=NULL; char *query=NULL; if (__user==NULL) { - str=(char *)"SELECT username,password,use_ssl,default_hostgroup,default_schema,schema_locked,transaction_persistent,fast_forward,max_connections FROM main.mysql_users WHERE %s=1 AND active=1 AND default_hostgroup>=0"; + if (hash1) { + str=(char *)"SELECT username,password,use_ssl,default_hostgroup,default_schema,schema_locked,transaction_persistent,fast_forward,max_connections FROM main.mysql_users WHERE %s=1 AND active=1 AND default_hostgroup>=0 ORDER BY username"; + } else { + str=(char *)"SELECT username,password,use_ssl,default_hostgroup,default_schema,schema_locked,transaction_persistent,fast_forward,max_connections FROM main.mysql_users WHERE %s=1 AND active=1 AND default_hostgroup>=0"; + } query=(char *)malloc(strlen(str)+15); sprintf(query,str,(usertype==USERNAME_BACKEND ? "backend" : "frontend")); } else { @@ -4547,6 +4678,16 @@ void ProxySQL_Admin::__add_active_users(enum cred_username_type usertype, char * while ((rc=sqlite3_step(statement))==SQLITE_ROW) { SQLite3_row *r=new SQLite3_row(cols); r->add_fields(statement); + if (hash1) { + for (int i=0; ifields[i]) { + myhash.Update(r->fields[i],r->sizes[i]); + } else { + myhash.Update("",0); + } + } + } + #else for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { SQLite3_row *r=*it; @@ -4603,6 +4744,11 @@ void ProxySQL_Admin::__add_active_users(enum cred_username_type usertype, char * if (statement) { sqlite3_finalize(statement); } + if (hash1) { + uint64_t h1, h2; + myhash.Final(&h1, &h2); + *hash1 = h1; + } #else if (resultset) delete resultset; #endif @@ -4610,6 +4756,69 @@ void ProxySQL_Admin::__add_active_users(enum cred_username_type usertype, char * } +void ProxySQL_Admin::dump_checksums_values_table() { + char *q = (char *)"REPLACE INTO runtime_checksums_values VALUES (?1 , ?2 , ?3 , ?4)"; + sqlite3_stmt *statement1 = NULL; + sqlite3 *mydb3 = admindb->get_db(); + rc=sqlite3_prepare_v2(mydb3, q, -1, &statement1, 0); + assert(rc==SQLITE_OK); + pthread_mutex_lock(&GloVars.checksum_mutex); + admindb->execute((char *)"BEGIN"); + admindb->execute((char *)"DELETE FROM runtime_checksums_values"); + + rc=sqlite3_bind_text(statement1, 1, "admin_variables", -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); + rc=sqlite3_bind_int64(statement1, 2, GloVars.checksums_values.admin_variables.version); assert(rc==SQLITE_OK); + rc=sqlite3_bind_int64(statement1, 3, GloVars.checksums_values.admin_variables.epoch); assert(rc==SQLITE_OK); + rc=sqlite3_bind_text(statement1, 4, GloVars.checksums_values.admin_variables.checksum, -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); + SAFE_SQLITE3_STEP(statement1); + rc=sqlite3_clear_bindings(statement1); assert(rc==SQLITE_OK); + rc=sqlite3_reset(statement1); assert(rc==SQLITE_OK); + + rc=sqlite3_bind_text(statement1, 1, "mysql_query_rules", -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); + rc=sqlite3_bind_int64(statement1, 2, GloVars.checksums_values.mysql_query_rules.version); assert(rc==SQLITE_OK); + rc=sqlite3_bind_int64(statement1, 3, GloVars.checksums_values.mysql_query_rules.epoch); assert(rc==SQLITE_OK); + rc=sqlite3_bind_text(statement1, 4, GloVars.checksums_values.mysql_query_rules.checksum, -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); + SAFE_SQLITE3_STEP(statement1); + rc=sqlite3_clear_bindings(statement1); assert(rc==SQLITE_OK); + rc=sqlite3_reset(statement1); assert(rc==SQLITE_OK); + + rc=sqlite3_bind_text(statement1, 1, "mysql_servers", -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); + rc=sqlite3_bind_int64(statement1, 2, GloVars.checksums_values.mysql_servers.version); assert(rc==SQLITE_OK); + rc=sqlite3_bind_int64(statement1, 3, GloVars.checksums_values.mysql_servers.epoch); assert(rc==SQLITE_OK); + rc=sqlite3_bind_text(statement1, 4, GloVars.checksums_values.mysql_servers.checksum, -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); + SAFE_SQLITE3_STEP(statement1); + rc=sqlite3_clear_bindings(statement1); assert(rc==SQLITE_OK); + rc=sqlite3_reset(statement1); assert(rc==SQLITE_OK); + + rc=sqlite3_bind_text(statement1, 1, "mysql_users", -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); + rc=sqlite3_bind_int64(statement1, 2, GloVars.checksums_values.mysql_users.version); assert(rc==SQLITE_OK); + rc=sqlite3_bind_int64(statement1, 3, GloVars.checksums_values.mysql_users.epoch); assert(rc==SQLITE_OK); + rc=sqlite3_bind_text(statement1, 4, GloVars.checksums_values.mysql_users.checksum, -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); + SAFE_SQLITE3_STEP(statement1); + rc=sqlite3_clear_bindings(statement1); assert(rc==SQLITE_OK); + rc=sqlite3_reset(statement1); assert(rc==SQLITE_OK); + + rc=sqlite3_bind_text(statement1, 1, "mysql_variables", -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); + rc=sqlite3_bind_int64(statement1, 2, GloVars.checksums_values.mysql_variables.version); assert(rc==SQLITE_OK); + rc=sqlite3_bind_int64(statement1, 3, GloVars.checksums_values.mysql_variables.epoch); assert(rc==SQLITE_OK); + rc=sqlite3_bind_text(statement1, 4, GloVars.checksums_values.mysql_variables.checksum, -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); + SAFE_SQLITE3_STEP(statement1); + rc=sqlite3_clear_bindings(statement1); assert(rc==SQLITE_OK); + rc=sqlite3_reset(statement1); assert(rc==SQLITE_OK); + + rc=sqlite3_bind_text(statement1, 1, "proxysql_servers", -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); + rc=sqlite3_bind_int64(statement1, 2, GloVars.checksums_values.proxysql_servers.version); assert(rc==SQLITE_OK); + rc=sqlite3_bind_int64(statement1, 3, GloVars.checksums_values.proxysql_servers.epoch); assert(rc==SQLITE_OK); + rc=sqlite3_bind_text(statement1, 4, GloVars.checksums_values.proxysql_servers.checksum, -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); + SAFE_SQLITE3_STEP(statement1); + rc=sqlite3_clear_bindings(statement1); assert(rc==SQLITE_OK); + rc=sqlite3_reset(statement1); assert(rc==SQLITE_OK); + + admindb->execute((char *)"COMMIT"); + pthread_mutex_unlock(&GloVars.checksum_mutex); + sqlite3_finalize(statement1); +} + void ProxySQL_Admin::save_mysql_users_runtime_to_database(bool _runtime) { char *query=NULL; if (_runtime) { @@ -5104,6 +5313,20 @@ char * ProxySQL_Admin::load_mysql_query_rules_to_runtime() { proxy_error("Error on %s : %s\n", query, error); } else { GloQPro->wrlock(); + if (checksum_variables.checksum_mysql_query_rules) { + pthread_mutex_lock(&GloVars.checksum_mutex); + uint64_t hash1 = resultset->raw_checksum(); + uint32_t d32[2]; + char buf[20]; + memcpy(&d32, &hash1, sizeof(hash1)); + sprintf(buf,"0x%0X%0X", d32[0], d32[1]); + GloVars.checksums_values.mysql_query_rules.set_checksum(buf); + GloVars.checksums_values.mysql_query_rules.version++; + time_t t = time(NULL); + GloVars.checksums_values.mysql_query_rules.epoch = t; + GloVars.epoch_version = t; + pthread_mutex_unlock(&GloVars.checksum_mutex); + } GloQPro->reset_all(false); QP_rule_t * nqpr; for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { @@ -6151,6 +6374,20 @@ void ProxySQL_Admin::load_proxysql_servers_to_runtime() { proxy_error("Error on %s : %s\n", query, error); } else { GloProxyCluster->load_servers_list(resultset); +// if (checksum_variables.checksum_mysql_query_rules) { + pthread_mutex_lock(&GloVars.checksum_mutex); + uint64_t hash1 = resultset->raw_checksum(); + uint32_t d32[2]; + char buf[20]; + memcpy(&d32, &hash1, sizeof(hash1)); + sprintf(buf,"0x%0X%0X", d32[0], d32[1]); + GloVars.checksums_values.proxysql_servers.set_checksum(buf); + GloVars.checksums_values.proxysql_servers.version++; + time_t t = time(NULL); + GloVars.checksums_values.proxysql_servers.epoch = t; + GloVars.epoch_version = t; + pthread_mutex_unlock(&GloVars.checksum_mutex); +// } } if (resultset) delete resultset; resultset=NULL; diff --git a/lib/ProxySQL_Cluster.cpp b/lib/ProxySQL_Cluster.cpp index c14fad7a7..dfa229fce 100644 --- a/lib/ProxySQL_Cluster.cpp +++ b/lib/ProxySQL_Cluster.cpp @@ -37,6 +37,7 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { //char *query1 = (char *)"SELECT 1"; // in future this will be used for "light check" char *query2 = (char *)"SELECT * FROM stats_mysql_global ORDER BY Variable_Name"; + char *query3 = (char *)"SELECT * FROM runtime_checksums_values ORDER BY name"; char *username = NULL; char *password = NULL; bool rc_bool = true; @@ -54,6 +55,11 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { GloProxyCluster->get_credentials(&username, &password); // TODO: add options, like timeout if (strlen(username)) { // do not monitor if the username is empty + unsigned int timeout = 1; + unsigned int timeout_long = 60; + mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); + mysql_options(conn, MYSQL_OPT_READ_TIMEOUT, &timeout_long); + mysql_options(conn, MYSQL_OPT_WRITE_TIMEOUT, &timeout); rc_conn = mysql_real_connect(conn, node->hostname, username, password, NULL, node->port, NULL, 0); char *query = query2; if (rc_conn) { @@ -67,6 +73,7 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { rc_bool = GloProxyCluster->Update_Node_Metrics(node->hostname, node->port, result, elapsed_time_us); mysql_free_result(result); unsigned long long elapsed_time_ms = elapsed_time_us / 1000; +/* int e_ms = (int)elapsed_time_ms; //fprintf(stderr,"Elapsed time = %d ms\n", e_ms); int ci = __sync_fetch_and_add(&GloProxyCluster->cluster_check_interval_ms,0); @@ -75,8 +82,28 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { usleep((ci-e_ms)*1000); // remember, usleep is in us } } +*/ + query = query3; + unsigned long long before_query_time2=monotonic_time(); + rc_query = mysql_query(conn,query); + if ( rc_query == 0 ) { + MYSQL_RES *result = mysql_store_result(conn); + unsigned long long after_query_time2=monotonic_time(); + unsigned long long elapsed_time_us2 = (after_query_time2 - before_query_time2); + rc_bool = GloProxyCluster->Update_Node_Checksums(node->hostname, node->port, result); + mysql_free_result(result); + unsigned long long elapsed_time_ms2 = elapsed_time_us2 / 1000; + int e_ms = (int)elapsed_time_ms + int(elapsed_time_ms2); + //fprintf(stderr,"Elapsed time = %d ms\n", e_ms); + int ci = __sync_fetch_and_add(&GloProxyCluster->cluster_check_interval_ms,0); + if (ci > e_ms) { + if (rc_bool) { + usleep((ci-e_ms)*1000); // remember, usleep is in us + } + } + } + } - } if (glovars.shutdown == 0) { // we arent' shutting down, but the query failed @@ -204,6 +231,47 @@ ProxySQL_Node_Metrics * ProxySQL_Node_Entry::get_metrics_prev() { return m; } +void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { + MYSQL_ROW row; + while ((row = mysql_fetch_row(_r))) { + if (strcmp(row[0],"admin_variables")==0) { + checksums_values.admin_variables.version = atoll(row[1]); + checksums_values.admin_variables.epoch = atoll(row[2]); + strcpy(checksums_values.admin_variables.checksum, row[3]); + continue; + } + if (strcmp(row[0],"mysql_query_rules")==0) { + checksums_values.mysql_query_rules.version = atoll(row[1]); + checksums_values.mysql_query_rules.epoch = atoll(row[2]); + strcpy(checksums_values.mysql_query_rules.checksum, row[3]); + continue; + } + if (strcmp(row[0],"mysql_servers")==0) { + checksums_values.mysql_servers.version = atoll(row[1]); + checksums_values.mysql_servers.epoch = atoll(row[2]); + strcpy(checksums_values.mysql_servers.checksum, row[3]); + continue; + } + if (strcmp(row[0],"mysql_users")==0) { + checksums_values.mysql_users.version = atoll(row[1]); + checksums_values.mysql_users.epoch = atoll(row[2]); + strcpy(checksums_values.mysql_users.checksum, row[3]); + continue; + } + if (strcmp(row[0],"mysql_variables")==0) { + checksums_values.mysql_variables.version = atoll(row[1]); + checksums_values.mysql_variables.epoch = atoll(row[2]); + strcpy(checksums_values.mysql_variables.checksum, row[3]); + continue; + } + if (strcmp(row[0],"proxysql_servers")==0) { + checksums_values.proxysql_servers.version = atoll(row[1]); + checksums_values.proxysql_servers.epoch = atoll(row[2]); + strcpy(checksums_values.proxysql_servers.checksum, row[3]); + continue; + } + } +} void ProxySQL_Node_Entry::set_metrics(MYSQL_RES *_r, unsigned long long _response_time) { MYSQL_ROW row; metrics_idx_prev = metrics_idx; @@ -323,6 +391,21 @@ void ProxySQL_Cluster_Nodes::load_servers_list(SQLite3_result *resultset) { pthread_mutex_unlock(&mutex); } +// if it returns false , the node doesn't exist anymore and the monitor should stop +bool ProxySQL_Cluster_Nodes::Update_Node_Checksums(char * _h, uint16_t _p, MYSQL_RES *_r) { + bool ret = false; + uint64_t hash_ = generate_hash(_h, _p); + pthread_mutex_lock(&mutex); + std::unordered_map::iterator ite = umap_proxy_nodes.find(hash_); + if (ite != umap_proxy_nodes.end()) { + ProxySQL_Node_Entry * node = ite->second; + node->set_checksums(_r); + ret = true; + } + pthread_mutex_unlock(&mutex); + return ret; +} + // if it returns false , the node doesn't exist anymore and the monitor should stop bool ProxySQL_Cluster_Nodes::Update_Node_Metrics(char * _h, uint16_t _p, MYSQL_RES *_r, unsigned long long _response_time) { bool ret = false; @@ -338,6 +421,78 @@ bool ProxySQL_Cluster_Nodes::Update_Node_Metrics(char * _h, uint16_t _p, MYSQL_R return ret; } +SQLite3_result * ProxySQL_Cluster_Nodes::stats_proxysql_servers_checksums() { + const int colnum=6; + SQLite3_result *result=new SQLite3_result(colnum); + result->add_column_definition(SQLITE_TEXT,"hostname"); + result->add_column_definition(SQLITE_TEXT,"port"); + result->add_column_definition(SQLITE_TEXT,"name"); + result->add_column_definition(SQLITE_TEXT,"version"); + result->add_column_definition(SQLITE_TEXT,"epoch"); + result->add_column_definition(SQLITE_TEXT,"checksum"); + + char buf[32]; + int k; + pthread_mutex_lock(&mutex); + unsigned long long curtime = monotonic_time(); + for( std::unordered_map::iterator it = umap_proxy_nodes.begin(); it != umap_proxy_nodes.end(); ) { + ProxySQL_Node_Entry * node = it->second; + ProxySQL_Checksum_Value * vals[6]; + vals[0] = &node->checksums_values.admin_variables; + vals[1] = &node->checksums_values.mysql_query_rules; + vals[2] = &node->checksums_values.mysql_servers; + vals[3] = &node->checksums_values.mysql_users; + vals[4] = &node->checksums_values.mysql_variables; + vals[5] = &node->checksums_values.proxysql_servers; + for (int i=0; i<6 ; i++) { + ProxySQL_Checksum_Value *v = vals[i]; + char **pta=(char **)malloc(sizeof(char *)*colnum); + pta[0]=strdup(node->get_hostname()); + sprintf(buf,"%d", node->get_port()); + pta[1]=strdup(buf); + + switch (i) { + case 0: + pta[2]=strdup((char *)"admin_variables"); + break; + case 1: + pta[2]=strdup((char *)"mysql_query_rules"); + break; + case 2: + pta[2]=strdup((char *)"mysql_servers"); + break; + case 3: + pta[2]=strdup((char *)"mysql_users"); + break; + case 4: + pta[2]=strdup((char *)"mysql_variables"); + break; + case 5: + pta[2]=strdup((char *)"proxysql_servers"); + break; + default: + break; + } + sprintf(buf,"%llu", v->version); + pta[3]=strdup(buf); + sprintf(buf,"%llu", v->epoch); + pta[4]=strdup(buf); + pta[5]=strdup(v->checksum); + + + result->add_row(pta); + for (k=0; kadd_column_definition(SQLITE_TEXT,"Queries"); result->add_column_definition(SQLITE_TEXT,"Client_Connections_connected"); result->add_column_definition(SQLITE_TEXT,"Client_Connections_created"); - + char buf[32]; int k; pthread_mutex_lock(&mutex); unsigned long long curtime = monotonic_time(); - for( std::unordered_map::iterator it = umap_proxy_nodes.begin(); it != umap_proxy_nodes.end(); ) { + for( std::unordered_map::iterator it = umap_proxy_nodes.begin(); it != umap_proxy_nodes.end(); ) { ProxySQL_Node_Entry * node = it->second; char **pta=(char **)malloc(sizeof(char *)*colnum); pta[0]=strdup(node->get_hostname()); @@ -379,7 +534,7 @@ SQLite3_result * ProxySQL_Cluster_Nodes::stats_proxysql_servers_metrics() { pta[8]=strdup(buf); sprintf(buf,"%llu", curr->Client_Connections_created); pta[9]=strdup(buf); - + result->add_row(pta); for (k=0; krows_count==0) return 0; uint64_t hash1, hash2; SpookyHash myhash; @@ -276,6 +276,7 @@ int64_t SQLite3_result::raw_checksum() { char *SQLite3_result::checksum() { uint64_t hash1=raw_checksum(); char buf[128]; + memset(buf,'0',128); uint32_t d32[2]; memcpy(&d32,&hash1,sizeof(hash1)); sprintf(buf,"0x%X%X", d32[0], d32[1]); From 2a76f95e31fb43117651436e3afeb76ab86e8e95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Fri, 25 Aug 2017 13:13:28 +0200 Subject: [PATCH 2/3] Further enhancement on Cluster Extended class ProxySQL_Checksum_Value() in ProxySQL_Cluster module to support further metrics Implemeted `SELECT GLOBAL_CHECKSUM()` and relative tracking of global checksums Added variable `admin-cluster_check_status_frequency` to check peer's global status at regular intervals --- include/ProxySQL_Cluster.hpp | 32 +++- include/proxysql_admin.h | 1 + include/proxysql_glovars.hpp | 4 + lib/MySQL_HostGroups_Manager.cpp | 2 + lib/MySQL_Session.cpp | 4 +- lib/ProxySQL_Admin.cpp | 69 ++++++++- lib/ProxySQL_Cluster.cpp | 241 ++++++++++++++++++++++++++----- lib/ProxySQL_GloVars.cpp | 47 +++++- 8 files changed, 351 insertions(+), 49 deletions(-) diff --git a/include/ProxySQL_Cluster.hpp b/include/ProxySQL_Cluster.hpp index 994cbb050..649d49323 100644 --- a/include/ProxySQL_Cluster.hpp +++ b/include/ProxySQL_Cluster.hpp @@ -8,6 +8,18 @@ #define PROXYSQL_NODE_METRICS_LEN 5 +class ProxySQL_Checksum_Value_2: public ProxySQL_Checksum_Value { + public: + time_t last_updated; + time_t last_changed; + unsigned int diff_check; + ProxySQL_Checksum_Value_2() { + ProxySQL_Checksum_Value(); + last_changed = 0; + last_updated = 0; + diff_check = 0; + } +}; class ProxySQL_Node_Metrics { public: @@ -63,13 +75,14 @@ class ProxySQL_Node_Entry { ProxySQL_Node_Metrics * get_metrics_curr(); ProxySQL_Node_Metrics * get_metrics_prev(); struct { - ProxySQL_Checksum_Value admin_variables; - ProxySQL_Checksum_Value mysql_variables; - ProxySQL_Checksum_Value mysql_query_rules; - ProxySQL_Checksum_Value mysql_servers; - ProxySQL_Checksum_Value mysql_users; - ProxySQL_Checksum_Value proxysql_servers; + ProxySQL_Checksum_Value_2 admin_variables; + ProxySQL_Checksum_Value_2 mysql_variables; + ProxySQL_Checksum_Value_2 mysql_query_rules; + ProxySQL_Checksum_Value_2 mysql_servers; + ProxySQL_Checksum_Value_2 mysql_users; + ProxySQL_Checksum_Value_2 proxysql_servers; } checksums_values; + uint64_t global_checksum; }; class ProxySQL_Cluster_Nodes { @@ -84,6 +97,7 @@ class ProxySQL_Cluster_Nodes { ~ProxySQL_Cluster_Nodes(); void load_servers_list(SQLite3_result *); bool Update_Node_Metrics(char * _h, uint16_t _p, MYSQL_RES *_r, unsigned long long _response_time); + bool Update_Global_Checksum(char * _h, uint16_t _p, MYSQL_RES *_r); bool Update_Node_Checksums(char * _h, uint16_t _p, MYSQL_RES *_r); SQLite3_result * dump_table_proxysql_servers(); SQLite3_result * stats_proxysql_servers_checksums(); @@ -100,6 +114,7 @@ class ProxySQL_Cluster { char *cluster_password; public: int cluster_check_interval_ms; + int cluster_check_status_frequency; ProxySQL_Cluster(); ~ProxySQL_Cluster(); void init() {}; @@ -113,7 +128,10 @@ class ProxySQL_Cluster { bool Update_Node_Metrics(char * _h, uint16_t _p, MYSQL_RES *_r, unsigned long long _response_time) { return nodes.Update_Node_Metrics(_h, _p, _r, _response_time); } - bool Update_Node_Checksums(char * _h, uint16_t _p, MYSQL_RES *_r) { + bool Update_Global_Checksum(char * _h, uint16_t _p, MYSQL_RES *_r) { + return nodes.Update_Global_Checksum(_h, _p, _r); + } + bool Update_Node_Checksums(char * _h, uint16_t _p, MYSQL_RES *_r = NULL) { return nodes.Update_Node_Checksums(_h, _p, _r); } SQLite3_result *dump_table_proxysql_servers() { diff --git a/include/proxysql_admin.h b/include/proxysql_admin.h index 335fe5c0d..fc7b84690 100644 --- a/include/proxysql_admin.h +++ b/include/proxysql_admin.h @@ -87,6 +87,7 @@ class ProxySQL_Admin { char * cluster_username; char * cluster_password; int cluster_check_interval_ms; + int cluster_check_status_frequency; #ifdef DEBUG bool debug; #endif /* DEBUG */ diff --git a/include/proxysql_glovars.hpp b/include/proxysql_glovars.hpp index 07cbdedb5..32577a99e 100644 --- a/include/proxysql_glovars.hpp +++ b/include/proxysql_glovars.hpp @@ -95,7 +95,11 @@ class ProxySQL_GlobalVariables { ProxySQL_Checksum_Value mysql_users; ProxySQL_Checksum_Value mysql_variables; ProxySQL_Checksum_Value proxysql_servers; + uint64_t global_checksum; + unsigned long long updates_cnt; + unsigned long long dumped_at; } checksums_values; + uint64_t generate_global_checksum(); ProxySQL_GlobalVariables(); ~ProxySQL_GlobalVariables(); void process_opts_pre(); diff --git a/lib/MySQL_HostGroups_Manager.cpp b/lib/MySQL_HostGroups_Manager.cpp index e509a49f0..79014e5e5 100644 --- a/lib/MySQL_HostGroups_Manager.cpp +++ b/lib/MySQL_HostGroups_Manager.cpp @@ -758,6 +758,8 @@ bool MySQL_HostGroups_Manager::commit() { //clock_gettime(CLOCK_REALTIME, &ts); time_t t = time(NULL); GloVars.checksums_values.mysql_servers.epoch = t; + GloVars.checksums_values.updates_cnt++; + GloVars.generate_global_checksum(); GloVars.epoch_version = t; pthread_mutex_unlock(&GloVars.checksum_mutex); } diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 2d4598cb4..6efb84e0d 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -582,7 +582,7 @@ bool MySQL_Session::handler_special_queries(PtrSize_t *pkt) { || (pkt->size==SELECT_LAST_INSERT_ID_LIMIT1_LEN+5 && strncasecmp((char *)SELECT_LAST_INSERT_ID_LIMIT1,(char *)pkt->ptr+5,pkt->size-5)==0) ) { - char buf[16]; + char buf[32]; sprintf(buf,"%llu",last_insert_id); unsigned int nTrx=NumActiveTransactions(); uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); @@ -604,6 +604,8 @@ bool MySQL_Session::handler_special_queries(PtrSize_t *pkt) { myprot->generate_pkt_EOF(true,NULL,NULL,sid,0, setStatus); sid++; myds->DSS=STATE_SLEEP; l_free(pkt->size,pkt->ptr); + free(p); + free(l); return true; } if (pkt->size==SELECT_VERSION_COMMENT_LEN+5 && strncmp((char *)SELECT_VERSION_COMMENT,(char *)pkt->ptr+5,pkt->size-5)==0) { diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index 2a31fa80c..2d27c442a 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -264,7 +264,7 @@ pthread_mutex_t users_mutex = PTHREAD_MUTEX_INITIALIZER; #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) )" -#define STATS_SQLITE_TABLE_PROXYSQL_SERVERS_CHECKSUMS "CREATE TABLE stats_proxysql_servers_checksums (hostname VARCHAR NOT NULL , port INT NOT NULL DEFAULT 6032 , name VARCHAR NOT NULL , version INT NOT NULL , epoch INT NOT NULL , checksum VARCHAR NOT NULL , PRIMARY KEY (hostname, port, name) )" +#define STATS_SQLITE_TABLE_PROXYSQL_SERVERS_CHECKSUMS "CREATE TABLE stats_proxysql_servers_checksums (hostname VARCHAR NOT NULL , port INT NOT NULL DEFAULT 6032 , name VARCHAR NOT NULL , version INT NOT NULL , epoch INT NOT NULL , checksum VARCHAR NOT NULL , changed_at INT NOT NULL , updated_at INT NOT NULL , diff_check INT NOT NULL , PRIMARY KEY (hostname, port, name) )" @@ -281,6 +281,7 @@ static char * admin_variables_names[]= { (char *)"cluster_username", (char *)"cluster_password", (char *)"cluster_check_interval_ms", + (char *)"cluster_check_status_frequency", (char *)"checksum_mysql_query_rules", (char *)"checksum_mysql_servers", (char *)"checksum_mysql_users", @@ -1869,6 +1870,37 @@ void admin_session_handler(MySQL_Session *sess, ProxySQL_Admin *pa, PtrSize_t *p } } } + + + if ((query_no_space_length == strlen("SELECT GLOBAL_CHECKSUM()")) && (!strncasecmp("SELECT GLOBAL_CHECKSUM()", query_no_space, strlen("SELECT GLOBAL_CHECKSUM()")))) { + char buf[32]; + pthread_mutex_lock(&GloVars.checksum_mutex); + sprintf(buf,"%llu",GloVars.checksums_values.global_checksum); + pthread_mutex_unlock(&GloVars.checksum_mutex); + uint16_t setStatus = 0; + MySQL_Data_Stream *myds=sess->client_myds; + MySQL_Protocol *myprot=&sess->client_myds->myprot; + myds->DSS=STATE_QUERY_SENT_DS; + int sid=1; + myprot->generate_pkt_column_count(true,NULL,NULL,sid,1); sid++; + myprot->generate_pkt_field(true,NULL,NULL,sid,(char *)"",(char *)"",(char *)"",(char *)"CHECKSUM",(char *)"",63,31,MYSQL_TYPE_LONGLONG,161,0,false,0,NULL); sid++; + myds->DSS=STATE_COLUMN_DEFINITION; + myprot->generate_pkt_EOF(true,NULL,NULL,sid,0, setStatus); sid++; + char **p=(char **)malloc(sizeof(char*)*1); + unsigned long *l=(unsigned long *)malloc(sizeof(unsigned long *)*1); + l[0]=strlen(buf);; + p[0]=buf; + myprot->generate_pkt_row(true,NULL,NULL,sid,1,l,p); sid++; + myds->DSS=STATE_ROW; + myprot->generate_pkt_EOF(true,NULL,NULL,sid,0, setStatus); sid++; + myds->DSS=STATE_SLEEP; + run_query=false; + free(l); + free(p); + goto __run_query; + } + + if ((query_no_space_length>8) && (!strncasecmp("PROXYSQL ", query_no_space, 8))) { proxy_debug(PROXY_DEBUG_ADMIN, 4, "Received PROXYSQL command\n"); pthread_mutex_lock(&admin_mutex); @@ -2758,6 +2790,7 @@ ProxySQL_Admin::ProxySQL_Admin() { variables.cluster_username=strdup((char *)""); variables.cluster_password=strdup((char *)""); variables.cluster_check_interval_ms=1000; + variables.cluster_check_status_frequency=10; checksum_variables.checksum_mysql_query_rules = true; checksum_variables.checksum_mysql_servers = true; checksum_variables.checksum_mysql_users = true; @@ -3341,6 +3374,10 @@ char * ProxySQL_Admin::get_variable(char *name) { sprintf(intbuf,"%d",variables.cluster_check_interval_ms); return strdup(intbuf); } + if (!strcasecmp(name,"cluster_check_status_frequency")) { + sprintf(intbuf,"%d",variables.cluster_check_status_frequency); + return strdup(intbuf); + } if (!strcasecmp(name,"refresh_interval")) { sprintf(intbuf,"%d",variables.refresh_interval); return strdup(intbuf); @@ -3551,6 +3588,16 @@ bool ProxySQL_Admin::set_variable(char *name, char *value) { // this is the pub return false; } } + if (!strcasecmp(name,"cluster_check_status_frequency")) { + int intv=atoi(value); + if (intv >= 0 && intv <= 10000) { + variables.cluster_check_status_frequency=intv; + __sync_lock_test_and_set(&GloProxyCluster->cluster_check_status_frequency, intv); + return true; + } else { + return false; + } + } if (!strcasecmp(name,"version")) { if (strcasecmp(value,(char *)PROXYSQL_VERSION)==0) { return true; @@ -4020,7 +4067,7 @@ void ProxySQL_Admin::stats___proxysql_servers_checksums() { sqlite3_stmt *statement1=NULL; sqlite3 *mydb3=statsdb->get_db(); char *query1=NULL; - query1=(char *)"INSERT INTO stats_proxysql_servers_checksums VALUES (?1, ?2, ?3, ?4, ?5, ?6)"; + query1=(char *)"INSERT INTO stats_proxysql_servers_checksums VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)"; rc=sqlite3_prepare_v2(mydb3, query1, -1, &statement1, 0); assert(rc==SQLITE_OK); for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { @@ -4031,6 +4078,9 @@ void ProxySQL_Admin::stats___proxysql_servers_checksums() { rc=sqlite3_bind_int64(statement1, 4, atoi(r1->fields[3])); assert(rc==SQLITE_OK); rc=sqlite3_bind_int64(statement1, 5, atoi(r1->fields[4])); assert(rc==SQLITE_OK); rc=sqlite3_bind_text(statement1, 6, r1->fields[5], -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); + rc=sqlite3_bind_int64(statement1, 7, atoi(r1->fields[6])); assert(rc==SQLITE_OK); + rc=sqlite3_bind_int64(statement1, 8, atoi(r1->fields[7])); assert(rc==SQLITE_OK); + rc=sqlite3_bind_int64(statement1, 9, atoi(r1->fields[8])); assert(rc==SQLITE_OK); SAFE_SQLITE3_STEP(statement1); rc=sqlite3_clear_bindings(statement1); assert(rc==SQLITE_OK); rc=sqlite3_reset(statement1); assert(rc==SQLITE_OK); @@ -4595,6 +4645,8 @@ void ProxySQL_Admin::__refresh_users() { time_t t = time(NULL); GloVars.checksums_values.mysql_users.epoch = t; GloVars.epoch_version = t; + GloVars.generate_global_checksum(); + GloVars.checksums_values.updates_cnt++; pthread_mutex_unlock(&GloVars.checksum_mutex); } } @@ -4757,12 +4809,19 @@ void ProxySQL_Admin::__add_active_users(enum cred_username_type usertype, char * void ProxySQL_Admin::dump_checksums_values_table() { + pthread_mutex_lock(&GloVars.checksum_mutex); + if (GloVars.checksums_values.updates_cnt == GloVars.checksums_values.dumped_at) { + // exit immediately + pthread_mutex_unlock(&GloVars.checksum_mutex); + return; + } else { + GloVars.checksums_values.dumped_at = GloVars.checksums_values.updates_cnt; + } char *q = (char *)"REPLACE INTO runtime_checksums_values VALUES (?1 , ?2 , ?3 , ?4)"; sqlite3_stmt *statement1 = NULL; sqlite3 *mydb3 = admindb->get_db(); rc=sqlite3_prepare_v2(mydb3, q, -1, &statement1, 0); assert(rc==SQLITE_OK); - pthread_mutex_lock(&GloVars.checksum_mutex); admindb->execute((char *)"BEGIN"); admindb->execute((char *)"DELETE FROM runtime_checksums_values"); @@ -5325,6 +5384,8 @@ char * ProxySQL_Admin::load_mysql_query_rules_to_runtime() { time_t t = time(NULL); GloVars.checksums_values.mysql_query_rules.epoch = t; GloVars.epoch_version = t; + GloVars.generate_global_checksum(); + GloVars.checksums_values.updates_cnt++; pthread_mutex_unlock(&GloVars.checksum_mutex); } GloQPro->reset_all(false); @@ -6386,6 +6447,8 @@ void ProxySQL_Admin::load_proxysql_servers_to_runtime() { time_t t = time(NULL); GloVars.checksums_values.proxysql_servers.epoch = t; GloVars.epoch_version = t; + GloVars.generate_global_checksum(); + GloVars.checksums_values.updates_cnt++; pthread_mutex_unlock(&GloVars.checksum_mutex); // } } diff --git a/lib/ProxySQL_Cluster.cpp b/lib/ProxySQL_Cluster.cpp index dfa229fce..572890fbf 100644 --- a/lib/ProxySQL_Cluster.cpp +++ b/lib/ProxySQL_Cluster.cpp @@ -35,12 +35,13 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { mysql_thread_init(); pthread_detach(pthread_self()); - //char *query1 = (char *)"SELECT 1"; // in future this will be used for "light check" + char *query1 = (char *)"SELECT GLOBAL_CHECKSUM()"; // in future this will be used for "light check" char *query2 = (char *)"SELECT * FROM stats_mysql_global ORDER BY Variable_Name"; char *query3 = (char *)"SELECT * FROM runtime_checksums_values ORDER BY name"; char *username = NULL; char *password = NULL; bool rc_bool = true; + int cluster_check_status_frequency_count = 0; MYSQL *conn = mysql_init(NULL); // goto __exit_monitor_thread; if (conn==NULL) { @@ -50,6 +51,7 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { while (glovars.shutdown == 0 && rc_bool == true) { MYSQL * rc_conn = NULL; int rc_query = 0; + bool update_checksum = false; if (username) { free(username); } if (password) { free(password); } GloProxyCluster->get_credentials(&username, &password); @@ -60,19 +62,23 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); mysql_options(conn, MYSQL_OPT_READ_TIMEOUT, &timeout_long); mysql_options(conn, MYSQL_OPT_WRITE_TIMEOUT, &timeout); - rc_conn = mysql_real_connect(conn, node->hostname, username, password, NULL, node->port, NULL, 0); - char *query = query2; + //rc_conn = mysql_real_connect(conn, node->hostname, username, password, NULL, node->port, NULL, CLIENT_COMPRESS); // FIXME: add optional support for compression + rc_conn = mysql_real_connect(conn, node->hostname, username, password, NULL, node->port, NULL, 0); + //char *query = query1; if (rc_conn) { while ( glovars.shutdown == 0 && rc_query == 0 && rc_bool == true) { - unsigned long long before_query_time=monotonic_time(); - rc_query = mysql_query(conn,query); + unsigned long long start_time=monotonic_time(); + //unsigned long long before_query_time=monotonic_time(); + rc_query = mysql_query(conn,query1); if ( rc_query == 0 ) { MYSQL_RES *result = mysql_store_result(conn); - unsigned long long after_query_time=monotonic_time(); - unsigned long long elapsed_time_us = (after_query_time - before_query_time); - rc_bool = GloProxyCluster->Update_Node_Metrics(node->hostname, node->port, result, elapsed_time_us); + //unsigned long long after_query_time=monotonic_time(); + //unsigned long long elapsed_time_us = (after_query_time - before_query_time); + update_checksum = GloProxyCluster->Update_Global_Checksum(node->hostname, node->port, result); mysql_free_result(result); - unsigned long long elapsed_time_ms = elapsed_time_us / 1000; + // FIXME: update metrics are not updated for now. We only check checksum + //rc_bool = GloProxyCluster->Update_Node_Metrics(node->hostname, node->port, result, elapsed_time_us); + //unsigned long long elapsed_time_ms = elapsed_time_us / 1000; /* int e_ms = (int)elapsed_time_ms; //fprintf(stderr,"Elapsed time = %d ms\n", e_ms); @@ -83,26 +89,61 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { } } */ - query = query3; - unsigned long long before_query_time2=monotonic_time(); - rc_query = mysql_query(conn,query); - if ( rc_query == 0 ) { - MYSQL_RES *result = mysql_store_result(conn); - unsigned long long after_query_time2=monotonic_time(); - unsigned long long elapsed_time_us2 = (after_query_time2 - before_query_time2); - rc_bool = GloProxyCluster->Update_Node_Checksums(node->hostname, node->port, result); - mysql_free_result(result); - unsigned long long elapsed_time_ms2 = elapsed_time_us2 / 1000; - int e_ms = (int)elapsed_time_ms + int(elapsed_time_ms2); - //fprintf(stderr,"Elapsed time = %d ms\n", e_ms); - int ci = __sync_fetch_and_add(&GloProxyCluster->cluster_check_interval_ms,0); - if (ci > e_ms) { - if (rc_bool) { - usleep((ci-e_ms)*1000); // remember, usleep is in us + //query = query3; + //unsigned long long before_query_time2=monotonic_time(); + if (update_checksum) { + rc_query = mysql_query(conn,query3); + if ( rc_query == 0 ) { + MYSQL_RES *result = mysql_store_result(conn); + //unsigned long long after_query_time2=monotonic_time(); + //unsigned long long elapsed_time_us2 = (after_query_time2 - before_query_time2); + rc_bool = GloProxyCluster->Update_Node_Checksums(node->hostname, node->port, result); + mysql_free_result(result); + //unsigned long long elapsed_time_ms2 = elapsed_time_us2 / 1000; + //int e_ms = (int)elapsed_time_ms + int(elapsed_time_ms2); + //fprintf(stderr,"Elapsed time = %d ms\n", e_ms); + //int ci = __sync_fetch_and_add(&GloProxyCluster->cluster_check_interval_ms,0); + //if (ci > e_ms) { + // if (rc_bool) { + // tts = 1; + // //usleep((ci-e_ms)*1000); // remember, usleep is in us + // } + //} + } + } else { + GloProxyCluster->Update_Node_Checksums(node->hostname, node->port); + //int ci = __sync_fetch_and_add(&GloProxyCluster->cluster_check_interval_ms,0); + //if (ci > elapsed_time_ms) { + // if (rc_bool) { + // usleep((ci-elapsed_time_ms)*1000); // remember, usleep is in us + // } + //} + } + if (rc_query == 0) { + cluster_check_status_frequency_count++; + int freq = __sync_fetch_and_add(&GloProxyCluster->cluster_check_status_frequency,0); + if (freq && cluster_check_status_frequency_count >= freq) { + cluster_check_status_frequency_count = 0; + unsigned long long before_query_time=monotonic_time(); + rc_query = mysql_query(conn,query2); + if ( rc_query == 0 ) { + MYSQL_RES *result = mysql_store_result(conn); + unsigned long long after_query_time=monotonic_time(); + unsigned long long elapsed_time_us = (after_query_time - before_query_time); + rc_bool = GloProxyCluster->Update_Node_Metrics(node->hostname, node->port, result, elapsed_time_us); + mysql_free_result(result); } } } - + } + unsigned long long end_time=monotonic_time(); + if (rc_query == 0) { + unsigned long long elapsed_time = (end_time - start_time); + unsigned long long e_ms = elapsed_time / 1000; + int ci = __sync_fetch_and_add(&GloProxyCluster->cluster_check_interval_ms,0); + if ((unsigned) ci > e_ms) { + usleep((ci-e_ms)*1000); // remember, usleep is in us + } } } if (glovars.shutdown == 0) { @@ -157,6 +198,7 @@ void ProxySQL_Node_Metrics::reset() { ProxySQL_Node_Entry::ProxySQL_Node_Entry(char *_hostname, uint16_t _port, uint64_t _weight, char * _comment) { hash = 0; + global_checksum = 0; hostname = NULL; if (_hostname) { hostname = strdup(_hostname); @@ -233,45 +275,136 @@ ProxySQL_Node_Metrics * ProxySQL_Node_Entry::get_metrics_prev() { void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { MYSQL_ROW row; - while ((row = mysql_fetch_row(_r))) { + time_t now = time(NULL); + pthread_mutex_lock(&GloVars.checksum_mutex); + while ( _r && (row = mysql_fetch_row(_r))) { if (strcmp(row[0],"admin_variables")==0) { checksums_values.admin_variables.version = atoll(row[1]); checksums_values.admin_variables.epoch = atoll(row[2]); - strcpy(checksums_values.admin_variables.checksum, row[3]); + checksums_values.admin_variables.last_updated = now; + if (strcmp(checksums_values.admin_variables.checksum, row[3])) { + strcpy(checksums_values.admin_variables.checksum, row[3]); + checksums_values.admin_variables.last_changed = now; + checksums_values.admin_variables.diff_check = 1; + } else { + checksums_values.admin_variables.diff_check++; + } + if (strcmp(checksums_values.admin_variables.checksum, GloVars.checksums_values.admin_variables.checksum) == 0) { + checksums_values.admin_variables.diff_check = 0; + } continue; } if (strcmp(row[0],"mysql_query_rules")==0) { checksums_values.mysql_query_rules.version = atoll(row[1]); checksums_values.mysql_query_rules.epoch = atoll(row[2]); - strcpy(checksums_values.mysql_query_rules.checksum, row[3]); + checksums_values.mysql_query_rules.last_updated = now; + if (strcmp(checksums_values.mysql_query_rules.checksum, row[3])) { + strcpy(checksums_values.mysql_query_rules.checksum, row[3]); + checksums_values.mysql_query_rules.last_changed = now; + checksums_values.mysql_query_rules.diff_check = 1; + } else { + checksums_values.mysql_query_rules.diff_check++; + } + if (strcmp(checksums_values.mysql_query_rules.checksum, GloVars.checksums_values.mysql_query_rules.checksum) == 0) { + checksums_values.mysql_query_rules.diff_check = 0; + } continue; } if (strcmp(row[0],"mysql_servers")==0) { checksums_values.mysql_servers.version = atoll(row[1]); checksums_values.mysql_servers.epoch = atoll(row[2]); - strcpy(checksums_values.mysql_servers.checksum, row[3]); + checksums_values.mysql_servers.last_updated = now; + if (strcmp(checksums_values.mysql_servers.checksum, row[3])) { + strcpy(checksums_values.mysql_servers.checksum, row[3]); + checksums_values.mysql_servers.last_changed = now; + checksums_values.mysql_servers.diff_check = 1; + } else { + checksums_values.mysql_servers.diff_check++; + } + if (strcmp(checksums_values.mysql_servers.checksum, GloVars.checksums_values.mysql_servers.checksum) == 0) { + checksums_values.mysql_servers.diff_check = 0; + } continue; } if (strcmp(row[0],"mysql_users")==0) { checksums_values.mysql_users.version = atoll(row[1]); checksums_values.mysql_users.epoch = atoll(row[2]); - strcpy(checksums_values.mysql_users.checksum, row[3]); + checksums_values.mysql_users.last_updated = now; + if (strcmp(checksums_values.mysql_users.checksum, row[3])) { + strcpy(checksums_values.mysql_users.checksum, row[3]); + checksums_values.mysql_users.last_changed = now; + checksums_values.mysql_users.diff_check = 1; + } else { + checksums_values.mysql_users.diff_check++; + } + if (strcmp(checksums_values.mysql_users.checksum, GloVars.checksums_values.mysql_users.checksum) == 0) { + checksums_values.mysql_users.diff_check = 0; + } continue; } if (strcmp(row[0],"mysql_variables")==0) { checksums_values.mysql_variables.version = atoll(row[1]); checksums_values.mysql_variables.epoch = atoll(row[2]); - strcpy(checksums_values.mysql_variables.checksum, row[3]); + checksums_values.mysql_variables.last_updated = now; + if (strcmp(checksums_values.mysql_variables.checksum, row[3])) { + strcpy(checksums_values.mysql_variables.checksum, row[3]); + checksums_values.mysql_variables.last_changed = now; + checksums_values.mysql_variables.diff_check = 1; + } else { + checksums_values.mysql_variables.diff_check++; + } + if (strcmp(checksums_values.mysql_variables.checksum, GloVars.checksums_values.mysql_variables.checksum) == 0) { + checksums_values.mysql_variables.diff_check = 0; + } continue; } if (strcmp(row[0],"proxysql_servers")==0) { checksums_values.proxysql_servers.version = atoll(row[1]); checksums_values.proxysql_servers.epoch = atoll(row[2]); - strcpy(checksums_values.proxysql_servers.checksum, row[3]); + checksums_values.proxysql_servers.last_updated = now; + if (strcmp(checksums_values.proxysql_servers.checksum, row[3])) { + strcpy(checksums_values.proxysql_servers.checksum, row[3]); + checksums_values.proxysql_servers.last_changed = now; + checksums_values.proxysql_servers.diff_check = 1; + } else { + checksums_values.proxysql_servers.diff_check++; + } + if (strcmp(checksums_values.proxysql_servers.checksum, GloVars.checksums_values.proxysql_servers.checksum) == 0) { + checksums_values.proxysql_servers.diff_check = 0; + } continue; } } + if (_r == NULL) { + ProxySQL_Checksum_Value_2 *v = NULL; + v = &checksums_values.admin_variables; + v->last_updated = now; + if (v->diff_check) + v->diff_check++; + v = &checksums_values.mysql_query_rules; + v->last_updated = now; + if (v->diff_check) + v->diff_check++; + v = &checksums_values.mysql_servers; + v->last_updated = now; + if (v->diff_check) + v->diff_check++; + v = &checksums_values.mysql_users; + v->last_updated = now; + if (v->diff_check) + v->diff_check++; + v = &checksums_values.mysql_variables; + v->last_updated = now; + if (v->diff_check) + v->diff_check++; + v = &checksums_values.proxysql_servers; + v->last_updated = now; + if (v->diff_check) + v->diff_check++; + } + pthread_mutex_unlock(&GloVars.checksum_mutex); } + void ProxySQL_Node_Entry::set_metrics(MYSQL_RES *_r, unsigned long long _response_time) { MYSQL_ROW row; metrics_idx_prev = metrics_idx; @@ -405,6 +538,30 @@ bool ProxySQL_Cluster_Nodes::Update_Node_Checksums(char * _h, uint16_t _p, MYSQL pthread_mutex_unlock(&mutex); return ret; } +// if it returns true , the checksum changed +bool ProxySQL_Cluster_Nodes::Update_Global_Checksum(char * _h, uint16_t _p, MYSQL_RES *_r) { + bool ret = true; + uint64_t hash_ = generate_hash(_h, _p); + pthread_mutex_lock(&mutex); + std::unordered_map::iterator ite = umap_proxy_nodes.find(hash_); + if (ite != umap_proxy_nodes.end()) { + ProxySQL_Node_Entry * node = ite->second; + MYSQL_ROW row; + //time_t now = time(NULL); + //pthread_mutex_lock(&GloVars.checksum_mutex); + while ((row = mysql_fetch_row(_r))) { + unsigned long long v = atoll(row[0]); + if (v == node->global_checksum) { + ret = false; + } else { + node->global_checksum = v; + } + } + //pthread_mutex_unlock(&GloVars.checksum_mutex); + } + pthread_mutex_unlock(&mutex); + return ret; +} // if it returns false , the node doesn't exist anymore and the monitor should stop bool ProxySQL_Cluster_Nodes::Update_Node_Metrics(char * _h, uint16_t _p, MYSQL_RES *_r, unsigned long long _response_time) { @@ -422,7 +579,7 @@ bool ProxySQL_Cluster_Nodes::Update_Node_Metrics(char * _h, uint16_t _p, MYSQL_R } SQLite3_result * ProxySQL_Cluster_Nodes::stats_proxysql_servers_checksums() { - const int colnum=6; + const int colnum=9; SQLite3_result *result=new SQLite3_result(colnum); result->add_column_definition(SQLITE_TEXT,"hostname"); result->add_column_definition(SQLITE_TEXT,"port"); @@ -430,14 +587,17 @@ SQLite3_result * ProxySQL_Cluster_Nodes::stats_proxysql_servers_checksums() { result->add_column_definition(SQLITE_TEXT,"version"); result->add_column_definition(SQLITE_TEXT,"epoch"); result->add_column_definition(SQLITE_TEXT,"checksum"); + result->add_column_definition(SQLITE_TEXT,"last_changed"); + result->add_column_definition(SQLITE_TEXT,"last_updated"); + result->add_column_definition(SQLITE_TEXT,"diff_check"); char buf[32]; int k; pthread_mutex_lock(&mutex); - unsigned long long curtime = monotonic_time(); + //unsigned long long curtime = monotonic_time(); for( std::unordered_map::iterator it = umap_proxy_nodes.begin(); it != umap_proxy_nodes.end(); ) { ProxySQL_Node_Entry * node = it->second; - ProxySQL_Checksum_Value * vals[6]; + ProxySQL_Checksum_Value_2 * vals[6]; vals[0] = &node->checksums_values.admin_variables; vals[1] = &node->checksums_values.mysql_query_rules; vals[2] = &node->checksums_values.mysql_servers; @@ -445,7 +605,7 @@ SQLite3_result * ProxySQL_Cluster_Nodes::stats_proxysql_servers_checksums() { vals[4] = &node->checksums_values.mysql_variables; vals[5] = &node->checksums_values.proxysql_servers; for (int i=0; i<6 ; i++) { - ProxySQL_Checksum_Value *v = vals[i]; + ProxySQL_Checksum_Value_2 *v = vals[i]; char **pta=(char **)malloc(sizeof(char *)*colnum); pta[0]=strdup(node->get_hostname()); sprintf(buf,"%d", node->get_port()); @@ -478,6 +638,12 @@ SQLite3_result * ProxySQL_Cluster_Nodes::stats_proxysql_servers_checksums() { sprintf(buf,"%llu", v->epoch); pta[4]=strdup(buf); pta[5]=strdup(v->checksum); + sprintf(buf,"%ld", v->last_changed); + pta[6]=strdup(buf); + sprintf(buf,"%ld", v->last_updated); + pta[7]=strdup(buf); + sprintf(buf,"%u", v->diff_check); + pta[8]=strdup(buf); result->add_row(pta); @@ -583,6 +749,7 @@ ProxySQL_Cluster::ProxySQL_Cluster() { cluster_username = strdup((char *)""); cluster_password = strdup((char *)""); cluster_check_interval_ms = 1000; + cluster_check_status_frequency = 10; } ProxySQL_Cluster::~ProxySQL_Cluster() { diff --git a/lib/ProxySQL_GloVars.cpp b/lib/ProxySQL_GloVars.cpp index a85524162..ecbd8fb23 100644 --- a/lib/ProxySQL_GloVars.cpp +++ b/lib/ProxySQL_GloVars.cpp @@ -3,7 +3,7 @@ #include "cpp.h" #include #include - +#include "SpookyV2.h" static void term_handler(int sig) { proxy_warning("Received TERM signal: shutdown in progress...\n"); @@ -67,6 +67,9 @@ ProxySQL_GlobalVariables::ProxySQL_GlobalVariables() { pthread_mutex_init(&global.start_mutex,NULL); pthread_mutex_init(&checksum_mutex,NULL); epoch_version = 0; + checksums_values.updates_cnt = 0; + checksums_values.dumped_at = 0; + checksums_values.global_checksum = 0; #ifdef DEBUG global.gdb=0; #endif @@ -263,3 +266,45 @@ void ProxySQL_GlobalVariables::process_opts_post() { } #endif }; + + +uint64_t ProxySQL_GlobalVariables::generate_global_checksum() { + SpookyHash myhash; + myhash.Init(9,5); + ProxySQL_Checksum_Value *v = NULL; + v = &checksums_values.admin_variables; + if (v->version) { + myhash.Update(v->checksum,strlen(v->checksum)); + myhash.Update(&v->version,sizeof(v->version)); + } + v = &checksums_values.mysql_query_rules; + if (v->version) { + myhash.Update(v->checksum,strlen(v->checksum)); + myhash.Update(&v->version,sizeof(v->version)); + } + v = &checksums_values.mysql_servers; + if (v->version) { + myhash.Update(v->checksum,strlen(v->checksum)); + myhash.Update(&v->version,sizeof(v->version)); + } + v = &checksums_values.mysql_users; + if (v->version) { + myhash.Update(v->checksum,strlen(v->checksum)); + myhash.Update(&v->version,sizeof(v->version)); + } + v = &checksums_values.mysql_variables; + if (v->version) { + myhash.Update(v->checksum,strlen(v->checksum)); + myhash.Update(&v->version,sizeof(v->version)); + } + v = &checksums_values.proxysql_servers; + if (v->version) { + myhash.Update(v->checksum,strlen(v->checksum)); + myhash.Update(&v->version,sizeof(v->version)); + } + uint64_t h1, h2; + myhash.Final(&h1, &h2); + h1 = h1/2; // ugly way to make it signed within LLONG_MAX + checksums_values.global_checksum = h1; + return h1; +} From e3c4cb9964aea2b40f8d98374c29037c234cb695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Fri, 1 Sep 2017 21:11:48 +0200 Subject: [PATCH 3/3] A lot of changes all related to Cluster In `MySQL_Cluster` class added functions to sync with remote node, like: - `pull_mysql_query_rules_from_peer()` - `pull_mysql_servers_from_peer()` - `pull_mysql_users_from_peer()` - `pull_proxysql_servers_from_peer()` Added 8 new global variables in Admin. 4 variables determine after how many different checks the remote configuration will be synced: - cluster_mysql_query_rules_diffs_before_sync - cluster_mysql_servers_diffs_before_sync - cluster_mysql_users_diffs_before_sync - cluster_proxysql_servers_diffs_before_sync 4 variables determine if after a remote sync the changes need to be written to disk: - cluster_mysql_query_rules_save_to_disk - cluster_mysql_servers_save_to_disk - cluster_mysql_users_save_to_disk - cluster_proxysql_servers_save_to_disk Table `proxysql_servers` is now automatically loaded from disk to memory and into runtime at startup. Added new Admin's command `LOAD PROXYSQL SERVERS FROM CONFIG` to load `proxysql_servers` from config file to memory (not runtime). Internal structures with credentials in MySQL_Authentication moved from `unsorted_map` to `map` : this to ensure the right order when generating the checksum. Config file supports both `address` and `hostname` for `mysql_servers` and `proxysql_servers` , #1091 `ProxySQL_Admin::load_proxysql_servers_to_runtime()` now has a lock or no lock option, to avoid deadlock For now, Cluster module is quite verbose. --- include/MySQL_Authentication.hpp | 4 +- include/ProxySQL_Cluster.hpp | 29 +- include/proxysql_admin.h | 12 +- lib/MySQL_Authentication.cpp | 46 ++- lib/MySQL_HostGroups_Manager.cpp | 73 +++- lib/ProxySQL_Admin.cpp | 301 +++++++++++++-- lib/ProxySQL_Cluster.cpp | 639 ++++++++++++++++++++++++++++++- src/main.cpp | 1 + 8 files changed, 1051 insertions(+), 54 deletions(-) diff --git a/include/MySQL_Authentication.hpp b/include/MySQL_Authentication.hpp index 116e68cd3..6381bfd46 100644 --- a/include/MySQL_Authentication.hpp +++ b/include/MySQL_Authentication.hpp @@ -30,7 +30,7 @@ typedef struct _account_details_t { #endif /* DEBUG */ #define MYSQL_AUTHENTICATION_VERSION "0.2.0902" DEB -typedef std::unordered_map umap_auth; +typedef std::map umap_auth; class PtrArray; @@ -49,6 +49,7 @@ class MySQL_Authentication { creds_group_t creds_backends; creds_group_t creds_frontends; bool _reset(enum cred_username_type usertype); + uint64_t _get_runtime_checksum(enum cred_username_type usertype); public: MySQL_Authentication(); ~MySQL_Authentication(); @@ -64,6 +65,7 @@ class MySQL_Authentication { void remove_inactives(enum cred_username_type usertype); bool set_SHA1(char *username, enum cred_username_type usertype, void *sha_pass); unsigned int memory_usage(); + uint64_t get_runtime_checksum(); }; #endif /* __CLASS_MYSQL_AUTHENTICATION_H */ diff --git a/include/ProxySQL_Cluster.hpp b/include/ProxySQL_Cluster.hpp index 649d49323..b49f864f9 100644 --- a/include/ProxySQL_Cluster.hpp +++ b/include/ProxySQL_Cluster.hpp @@ -51,6 +51,9 @@ class ProxySQL_Node_Entry { int metrics_idx_prev; int metrics_idx; ProxySQL_Node_Metrics **metrics; +// void pull_mysql_query_rules_from_peer(); +// void pull_mysql_servers_from_peer(); +// void pull_proxysql_servers_from_peer(); public: uint64_t get_hash(); @@ -95,13 +98,17 @@ class ProxySQL_Cluster_Nodes { public: ProxySQL_Cluster_Nodes(); ~ProxySQL_Cluster_Nodes(); - void load_servers_list(SQLite3_result *); + void load_servers_list(SQLite3_result *, bool _lock); bool Update_Node_Metrics(char * _h, uint16_t _p, MYSQL_RES *_r, unsigned long long _response_time); bool Update_Global_Checksum(char * _h, uint16_t _p, MYSQL_RES *_r); bool Update_Node_Checksums(char * _h, uint16_t _p, MYSQL_RES *_r); SQLite3_result * dump_table_proxysql_servers(); SQLite3_result * stats_proxysql_servers_checksums(); SQLite3_result * stats_proxysql_servers_metrics(); + void get_peer_to_sync_mysql_query_rules(char **host, uint16_t *port); + void get_peer_to_sync_mysql_servers(char **host, uint16_t *port); + void get_peer_to_sync_mysql_users(char **host, uint16_t *port); + void get_peer_to_sync_proxysql_servers(char **host, uint16_t *port); }; @@ -113,14 +120,26 @@ class ProxySQL_Cluster { char *cluster_username; char *cluster_password; public: + pthread_mutex_t update_mysql_query_rules_mutex; + pthread_mutex_t update_mysql_servers_mutex; + pthread_mutex_t update_mysql_users_mutex; + pthread_mutex_t update_proxysql_servers_mutex; int cluster_check_interval_ms; int cluster_check_status_frequency; + int cluster_mysql_query_rules_diffs_before_sync; + int cluster_mysql_servers_diffs_before_sync; + int cluster_mysql_users_diffs_before_sync; + int cluster_proxysql_servers_diffs_before_sync; + bool cluster_mysql_query_rules_save_to_disk; + bool cluster_mysql_servers_save_to_disk; + bool cluster_mysql_users_save_to_disk; + bool cluster_proxysql_servers_save_to_disk; ProxySQL_Cluster(); ~ProxySQL_Cluster(); void init() {}; void print_version(); - void load_servers_list(SQLite3_result *r) { - nodes.load_servers_list(r); + void load_servers_list(SQLite3_result *r, bool _lock = true) { + nodes.load_servers_list(r, _lock); } void get_credentials(char **, char **); void set_username(char *); @@ -145,5 +164,9 @@ class ProxySQL_Cluster { } void thread_ending(pthread_t); void join_term_thread(); + void pull_mysql_query_rules_from_peer(); + void pull_mysql_servers_from_peer(); + void pull_mysql_users_from_peer(); + void pull_proxysql_servers_from_peer(); }; #endif /* CLASS_PROXYSQL_CLUSTER_H */ diff --git a/include/proxysql_admin.h b/include/proxysql_admin.h index fc7b84690..9a86bb35f 100644 --- a/include/proxysql_admin.h +++ b/include/proxysql_admin.h @@ -88,6 +88,14 @@ class ProxySQL_Admin { char * cluster_password; int cluster_check_interval_ms; int cluster_check_status_frequency; + int cluster_mysql_query_rules_diffs_before_sync; + int cluster_mysql_servers_diffs_before_sync; + int cluster_mysql_users_diffs_before_sync; + int cluster_proxysql_servers_diffs_before_sync; + bool cluster_mysql_query_rules_save_to_disk; + bool cluster_mysql_servers_save_to_disk; + bool cluster_mysql_users_save_to_disk; + bool cluster_proxysql_servers_save_to_disk; #ifdef DEBUG bool debug; #endif /* DEBUG */ @@ -165,6 +173,7 @@ class ProxySQL_Admin { void init_users(); void init_mysql_servers(); void init_mysql_query_rules(); + void init_proxysql_servers(); void save_mysql_users_runtime_to_database(bool _runtime); void save_mysql_servers_runtime_to_database(bool); void admin_shutdown(); @@ -215,6 +224,7 @@ class ProxySQL_Admin { int Read_MySQL_Query_Rules_from_configfile(); int Read_MySQL_Servers_from_configfile(); int Read_Scheduler_from_configfile(); + int Read_ProxySQL_Servers_from_configfile(); void flush_error_log(); void GenericRefreshStatistics(const char *query_no_space, unsigned int query_no_space_length, bool admin); @@ -230,7 +240,7 @@ class ProxySQL_Admin { void flush_configdb(); // 923 // Cluster - void load_proxysql_servers_to_runtime(); + void load_proxysql_servers_to_runtime(bool _lock=true); void flush_proxysql_servers__from_memory_to_disk(); void flush_proxysql_servers__from_disk_to_memory(); void save_proxysql_servers_runtime_to_database(bool); diff --git a/lib/MySQL_Authentication.cpp b/lib/MySQL_Authentication.cpp index c81ab36ab..4a860de3e 100644 --- a/lib/MySQL_Authentication.cpp +++ b/lib/MySQL_Authentication.cpp @@ -90,7 +90,7 @@ bool MySQL_Authentication::add(char * username, char * password, enum cred_usern #else spin_wrlock(&cg.lock); #endif - std::unordered_map::iterator lookup; + std::map::iterator lookup; lookup = cg.bt_map.find(hash1); // few changes will follow, due to issue #802 account_details_t *ad=NULL; @@ -273,7 +273,7 @@ int MySQL_Authentication::increase_frontend_user_connections(char *username, int #else spin_wrlock(&cg.lock); #endif - std::unordered_map::iterator it; + std::map::iterator it; it = cg.bt_map.find(hash1); if (it != cg.bt_map.end()) { account_details_t *ad=it->second; @@ -306,7 +306,7 @@ void MySQL_Authentication::decrease_frontend_user_connections(char *username) { #else spin_wrlock(&cg.lock); #endif - std::unordered_map::iterator it; + std::map::iterator it; it = cg.bt_map.find(hash1); if (it != cg.bt_map.end()) { account_details_t *ad=it->second; @@ -338,7 +338,7 @@ bool MySQL_Authentication::del(char * username, enum cred_username_type usertype #else spin_wrlock(&cg.lock); #endif - std::unordered_map::iterator lookup; + std::map::iterator lookup; lookup = cg.bt_map.find(hash1); if (lookup != cg.bt_map.end()) { account_details_t *ad=lookup->second; @@ -376,7 +376,7 @@ bool MySQL_Authentication::set_SHA1(char * username, enum cred_username_type use #else spin_wrlock(&cg.lock); #endif - std::unordered_map::iterator lookup; + std::map::iterator lookup; lookup = cg.bt_map.find(hash1); if (lookup != cg.bt_map.end()) { account_details_t *ad=lookup->second; @@ -410,7 +410,7 @@ char * MySQL_Authentication::lookup(char * username, enum cred_username_type use #else spin_rdlock(&cg.lock); #endif - std::unordered_map::iterator lookup; + std::map::iterator lookup; lookup = cg.bt_map.find(hash1); if (lookup != cg.bt_map.end()) { account_details_t *ad=lookup->second; @@ -446,7 +446,7 @@ bool MySQL_Authentication::_reset(enum cred_username_type usertype) { #else spin_wrlock(&cg.lock); #endif - std::unordered_map::iterator lookup; + std::map::iterator lookup; while (cg.bt_map.size()) { lookup = cg.bt_map.begin(); @@ -476,3 +476,35 @@ bool MySQL_Authentication::reset() { _reset(USERNAME_FRONTEND); return true; } + + +uint64_t MySQL_Authentication::_get_runtime_checksum(enum cred_username_type usertype) { + creds_group_t &cg=(usertype==USERNAME_BACKEND ? creds_backends : creds_frontends); + std::map::iterator it; + if (cg.bt_map.size() == 0) { + return 0; + } + SpookyHash myhash; + myhash.Init(13,4); + for (it = cg.bt_map.begin(); it != cg.bt_map.end(); ) { + account_details_t *ad=it->second; + myhash.Update(&ad->use_ssl,sizeof(ad->use_ssl)); + myhash.Update(&ad->default_hostgroup,sizeof(ad->default_hostgroup)); + myhash.Update(&ad->schema_locked,sizeof(ad->schema_locked)); + myhash.Update(&ad->transaction_persistent,sizeof(ad->transaction_persistent)); + myhash.Update(&ad->fast_forward,sizeof(ad->fast_forward)); + myhash.Update(&ad->max_connections,sizeof(ad->max_connections)); + myhash.Update(ad->username,strlen(ad->username)); + myhash.Update(ad->password,strlen(ad->password)); + it++; + } + uint64_t hash1, hash2; + myhash.Final(&hash1, &hash2); + return hash1; +} + +uint64_t MySQL_Authentication::get_runtime_checksum() { + uint64_t hashB = _get_runtime_checksum(USERNAME_BACKEND); + uint64_t hashF = _get_runtime_checksum(USERNAME_FRONTEND); + return hashB+hashF; +} diff --git a/lib/MySQL_HostGroups_Manager.cpp b/lib/MySQL_HostGroups_Manager.cpp index 79014e5e5..a1ccef31e 100644 --- a/lib/MySQL_HostGroups_Manager.cpp +++ b/lib/MySQL_HostGroups_Manager.cpp @@ -702,14 +702,19 @@ bool MySQL_HostGroups_Manager::commit() { if ( GloAdmin && GloAdmin->checksum_variables.checksum_mysql_servers ) { - uint64_t hash1, hash2; + uint64_t hash1=0, hash2=0; SpookyHash myhash; char buf[80]; - myhash.Init(19,3); + bool init = false; +/* removing all this code, because we need them ordered MySrvC *mysrvc=NULL; for (unsigned int i=0; ilen; i++) { MyHGC *myhgc=(MyHGC *)MyHostGroups->index(i); for (unsigned int j=0; jmysrvs->servers->len; j++) { + if (init == false) { + init = true; + myhash.Init(19,3); + } mysrvc=myhgc->mysrvs->idx(j); // hostgroup sprintf(buf,"%u",mysrvc->myhgc->hid); @@ -747,7 +752,69 @@ bool MySQL_HostGroups_Manager::commit() { } else { myhash.Update("",0); } } } - myhash.Final(&hash1, &hash2); +*/ + { + mydb->execute("DELETE FROM mysql_servers"); + generate_mysql_servers_table(); + char *error=NULL; + int cols=0; + int affected_rows=0; + SQLite3_result *resultset=NULL; + char *query=(char *)"SELECT * FROM mysql_servers WHERE ORDER BY hostgroup_id, hostname, port"; + mydb->execute_statement(query, &error , &cols , &affected_rows , &resultset); + if (resultset) { + if (resultset->rows_count) { + if (init == false) { + init = true; + myhash.Init(19,3); + } + uint64_t hash1_ = resultset->raw_checksum(); + myhash.Update(&hash1_, sizeof(hash1_)); + } + delete resultset; + } + } + { + char *error=NULL; + int cols=0; + int affected_rows=0; + SQLite3_result *resultset=NULL; + char *query=(char *)"SELECT * FROM mysql_replication_hostgroups ORDER BY writer_hostgroup"; + mydb->execute_statement(query, &error , &cols , &affected_rows , &resultset); + if (resultset) { + if (resultset->rows_count) { + if (init == false) { + init = true; + myhash.Init(19,3); + } + uint64_t hash1_ = resultset->raw_checksum(); + myhash.Update(&hash1_, sizeof(hash1_)); + } + delete resultset; + } + } + { + char *error=NULL; + int cols=0; + int affected_rows=0; + SQLite3_result *resultset=NULL; + char *query=(char *)"SELECT * FROM mysql_group_replication_hostgroups ORDER BY writer_hostgroup"; + mydb->execute_statement(query, &error , &cols , &affected_rows , &resultset); + if (resultset) { + if (resultset->rows_count) { + if (init == false) { + init = true; + myhash.Init(19,3); + } + uint64_t hash1_ = resultset->raw_checksum(); + myhash.Update(&hash1_, sizeof(hash1_)); + } + delete resultset; + } + } + if (init == true) { + myhash.Final(&hash1, &hash2); + } uint32_t d32[2]; memcpy(&d32,&hash1,sizeof(hash1)); sprintf(buf,"0x%0X%0X", d32[0], d32[1]); diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index 2d27c442a..01f5b8081 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -282,6 +282,14 @@ static char * admin_variables_names[]= { (char *)"cluster_password", (char *)"cluster_check_interval_ms", (char *)"cluster_check_status_frequency", + (char *)"cluster_mysql_query_rules_diffs_before_sync", + (char *)"cluster_mysql_servers_diffs_before_sync", + (char *)"cluster_mysql_users_diffs_before_sync", + (char *)"cluster_proxysql_servers_diffs_before_sync", + (char *)"cluster_mysql_query_rules_save_to_disk", + (char *)"cluster_mysql_servers_save_to_disk", + (char *)"cluster_mysql_users_save_to_disk", + (char *)"cluster_proxysql_servers_save_to_disk", (char *)"checksum_mysql_query_rules", (char *)"checksum_mysql_servers", (char *)"checksum_mysql_users", @@ -1275,6 +1283,34 @@ bool admin_handler_command_load_or_save(char *query_no_space, unsigned int query return false; } + if ( + (query_no_space_length==strlen("LOAD PROXYSQL SERVERS FROM CONFIG") && !strncasecmp("LOAD PROXYSQL SERVERS FROM CONFIG",query_no_space, query_no_space_length)) + ) { + proxy_info("Received %s command\n", query_no_space); + if (GloVars.configfile_open) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loading from file %s\n", GloVars.config_file); + if (GloVars.confFile->OpenFile(NULL)==true) { + ProxySQL_Admin *SPA=(ProxySQL_Admin *)pa; + int rows=0; + rows=SPA->Read_ProxySQL_Servers_from_configfile(); + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Loaded ProxySQL servers from CONFIG\n"); + SPA->send_MySQL_OK(&sess->client_myds->myprot, NULL, rows); + GloVars.confFile->CloseFile(); + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unable to open or parse config file %s\n", GloVars.config_file); + char *s=(char *)"Unable to open or parse config file %s"; + char *m=(char *)malloc(strlen(s)+strlen(GloVars.config_file)+1); + sprintf(m,s,GloVars.config_file); + SPA->send_MySQL_ERR(&sess->client_myds->myprot, m); + free(m); + } + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Unknown config file\n"); + SPA->send_MySQL_ERR(&sess->client_myds->myprot, (char *)"Config file unknown"); + } + return false; + } + } if ((query_no_space_length>23) && ( (!strncasecmp("SAVE MYSQL QUERY RULES ", query_no_space, 23)) || (!strncasecmp("LOAD MYSQL QUERY RULES ", query_no_space, 23))) ) { @@ -2791,9 +2827,17 @@ ProxySQL_Admin::ProxySQL_Admin() { variables.cluster_password=strdup((char *)""); variables.cluster_check_interval_ms=1000; variables.cluster_check_status_frequency=10; + variables.cluster_mysql_query_rules_diffs_before_sync = 3; + variables.cluster_mysql_servers_diffs_before_sync = 3; + variables.cluster_mysql_users_diffs_before_sync = 3; + variables.cluster_proxysql_servers_diffs_before_sync = 3; checksum_variables.checksum_mysql_query_rules = true; checksum_variables.checksum_mysql_servers = true; checksum_variables.checksum_mysql_users = true; + variables.cluster_mysql_query_rules_save_to_disk = true; + variables.cluster_mysql_servers_save_to_disk = true; + variables.cluster_mysql_users_save_to_disk = true; + variables.cluster_proxysql_servers_save_to_disk = true; #ifdef DEBUG variables.debug=GloVars.global.gdbg; #endif /* DEBUG */ @@ -3010,6 +3054,7 @@ bool ProxySQL_Admin::init() { Read_MySQL_Users_from_configfile(); Read_MySQL_Query_Rules_from_configfile(); Read_Scheduler_from_configfile(); + Read_ProxySQL_Servers_from_configfile(); __insert_or_replace_disktable_select_maintable(); } else { if (GloVars.confFile->OpenFile(GloVars.config_file)==true) { @@ -3019,6 +3064,7 @@ bool ProxySQL_Admin::init() { Read_Global_Variables_from_configfile("admin"); Read_Global_Variables_from_configfile("mysql"); Read_Scheduler_from_configfile(); + Read_ProxySQL_Servers_from_configfile(); __insert_or_replace_disktable_select_maintable(); } } @@ -3378,6 +3424,34 @@ char * ProxySQL_Admin::get_variable(char *name) { sprintf(intbuf,"%d",variables.cluster_check_status_frequency); return strdup(intbuf); } + if (!strcasecmp(name,"cluster_mysql_query_rules_diffs_before_sync")) { + sprintf(intbuf,"%d",variables.cluster_mysql_query_rules_diffs_before_sync); + return strdup(intbuf); + } + if (!strcasecmp(name,"cluster_mysql_servers_diffs_before_sync")) { + sprintf(intbuf,"%d",variables.cluster_mysql_servers_diffs_before_sync); + return strdup(intbuf); + } + if (!strcasecmp(name,"cluster_mysql_users_diffs_before_sync")) { + sprintf(intbuf,"%d",variables.cluster_mysql_users_diffs_before_sync); + return strdup(intbuf); + } + if (!strcasecmp(name,"cluster_proxysql_servers_diffs_before_sync")) { + sprintf(intbuf,"%d",variables.cluster_proxysql_servers_diffs_before_sync); + return strdup(intbuf); + } + if (!strcasecmp(name,"cluster_mysql_query_rules_save_to_disk")) { + return strdup((variables.cluster_mysql_query_rules_save_to_disk ? "true" : "false")); + } + if (!strcasecmp(name,"cluster_mysql_servers_save_to_disk")) { + return strdup((variables.cluster_mysql_servers_save_to_disk ? "true" : "false")); + } + if (!strcasecmp(name,"cluster_mysql_users_save_to_disk")) { + return strdup((variables.cluster_mysql_users_save_to_disk ? "true" : "false")); + } + if (!strcasecmp(name,"cluster_proxysql_servers_save_to_disk")) { + return strdup((variables.cluster_proxysql_servers_save_to_disk ? "true" : "false")); + } if (!strcasecmp(name,"refresh_interval")) { sprintf(intbuf,"%d",variables.refresh_interval); return strdup(intbuf); @@ -3598,6 +3672,46 @@ bool ProxySQL_Admin::set_variable(char *name, char *value) { // this is the pub return false; } } + if (!strcasecmp(name,"cluster_mysql_query_rules_diffs_before_sync")) { + int intv=atoi(value); + if (intv >= 0 && intv <= 1000) { + variables.cluster_mysql_query_rules_diffs_before_sync=intv; + __sync_lock_test_and_set(&GloProxyCluster->cluster_mysql_query_rules_diffs_before_sync, intv); + return true; + } else { + return false; + } + } + if (!strcasecmp(name,"cluster_mysql_servers_diffs_before_sync")) { + int intv=atoi(value); + if (intv >= 0 && intv <= 1000) { + variables.cluster_mysql_servers_diffs_before_sync=intv; + __sync_lock_test_and_set(&GloProxyCluster->cluster_mysql_servers_diffs_before_sync, intv); + return true; + } else { + return false; + } + } + if (!strcasecmp(name,"cluster_mysql_users_diffs_before_sync")) { + int intv=atoi(value); + if (intv >= 0 && intv <= 1000) { + variables.cluster_mysql_users_diffs_before_sync=intv; + __sync_lock_test_and_set(&GloProxyCluster->cluster_mysql_users_diffs_before_sync, intv); + return true; + } else { + return false; + } + } + if (!strcasecmp(name,"cluster_proxysql_servers_diffs_before_sync")) { + int intv=atoi(value); + if (intv >= 0 && intv <= 1000) { + variables.cluster_proxysql_servers_diffs_before_sync=intv; + __sync_lock_test_and_set(&GloProxyCluster->cluster_proxysql_servers_diffs_before_sync, intv); + return true; + } else { + return false; + } + } if (!strcasecmp(name,"version")) { if (strcasecmp(value,(char *)PROXYSQL_VERSION)==0) { return true; @@ -3616,6 +3730,58 @@ bool ProxySQL_Admin::set_variable(char *name, char *value) { // this is the pub } return false; } + if (!strcasecmp(name,"cluster_mysql_query_rules_save_to_disk")) { + if (strcasecmp(value,"true")==0 || strcasecmp(value,"1")==0) { + variables.cluster_mysql_query_rules_save_to_disk=true; + __sync_lock_test_and_set(&GloProxyCluster->cluster_mysql_query_rules_save_to_disk, true); + return true; + } + if (strcasecmp(value,"false")==0 || strcasecmp(value,"0")==0) { + variables.cluster_mysql_query_rules_save_to_disk=false; + __sync_lock_test_and_set(&GloProxyCluster->cluster_mysql_query_rules_save_to_disk, false); + return true; + } + return false; + } + if (!strcasecmp(name,"cluster_mysql_servers_save_to_disk")) { + if (strcasecmp(value,"true")==0 || strcasecmp(value,"1")==0) { + variables.cluster_mysql_servers_save_to_disk=true; + __sync_lock_test_and_set(&GloProxyCluster->cluster_mysql_servers_save_to_disk, true); + return true; + } + if (strcasecmp(value,"false")==0 || strcasecmp(value,"0")==0) { + variables.cluster_mysql_servers_save_to_disk=false; + __sync_lock_test_and_set(&GloProxyCluster->cluster_mysql_servers_save_to_disk, false); + return true; + } + return false; + } + if (!strcasecmp(name,"cluster_mysql_users_save_to_disk")) { + if (strcasecmp(value,"true")==0 || strcasecmp(value,"1")==0) { + variables.cluster_mysql_users_save_to_disk=true; + __sync_lock_test_and_set(&GloProxyCluster->cluster_mysql_users_save_to_disk, true); + return true; + } + if (strcasecmp(value,"false")==0 || strcasecmp(value,"0")==0) { + variables.cluster_mysql_users_save_to_disk=false; + __sync_lock_test_and_set(&GloProxyCluster->cluster_mysql_users_save_to_disk, false); + return true; + } + return false; + } + if (!strcasecmp(name,"cluster_proxysql_servers_save_to_disk")) { + if (strcasecmp(value,"true")==0 || strcasecmp(value,"1")==0) { + variables.cluster_proxysql_servers_save_to_disk=true; + __sync_lock_test_and_set(&GloProxyCluster->cluster_proxysql_servers_save_to_disk, true); + return true; + } + if (strcasecmp(value,"false")==0 || strcasecmp(value,"0")==0) { + variables.cluster_proxysql_servers_save_to_disk=false; + __sync_lock_test_and_set(&GloProxyCluster->cluster_proxysql_servers_save_to_disk, false); + return true; + } + return false; + } if (!strcasecmp(name,"checksum_mysql_query_rules")) { if (strcasecmp(value,"true")==0 || strcasecmp(value,"1")==0) { checksum_variables.checksum_mysql_query_rules=true; @@ -4443,58 +4609,62 @@ int ProxySQL_Admin::flush_debug_levels_database_to_runtime(SQLite3DB *db) { void ProxySQL_Admin::__insert_or_ignore_maintable_select_disktable() { - admindb->execute("PRAGMA foreign_keys = OFF"); - admindb->execute("INSERT OR IGNORE INTO main.mysql_servers SELECT * FROM disk.mysql_servers"); - admindb->execute("INSERT OR IGNORE INTO main.mysql_replication_hostgroups SELECT * FROM disk.mysql_replication_hostgroups"); - admindb->execute("INSERT OR IGNORE INTO main.mysql_group_replication_hostgroups SELECT * FROM disk.mysql_group_replication_hostgroups"); - admindb->execute("INSERT OR IGNORE INTO main.mysql_users SELECT * FROM disk.mysql_users"); + admindb->execute("PRAGMA foreign_keys = OFF"); + admindb->execute("INSERT OR IGNORE INTO main.mysql_servers SELECT * FROM disk.mysql_servers"); + admindb->execute("INSERT OR IGNORE INTO main.mysql_replication_hostgroups SELECT * FROM disk.mysql_replication_hostgroups"); + admindb->execute("INSERT OR IGNORE INTO main.mysql_group_replication_hostgroups SELECT * FROM disk.mysql_group_replication_hostgroups"); + admindb->execute("INSERT OR IGNORE INTO main.mysql_users SELECT * FROM disk.mysql_users"); admindb->execute("INSERT OR IGNORE INTO main.mysql_query_rules SELECT * FROM disk.mysql_query_rules"); admindb->execute("INSERT OR IGNORE INTO main.global_variables SELECT * FROM disk.global_variables"); admindb->execute("INSERT OR IGNORE INTO main.scheduler SELECT * FROM disk.scheduler"); + admindb->execute("INSERT OR IGNORE INTO main.proxysql_servers SELECT * FROM disk.proxysql_servers"); #ifdef DEBUG - admindb->execute("INSERT OR IGNORE INTO main.debug_levels SELECT * FROM disk.debug_levels"); + admindb->execute("INSERT OR IGNORE INTO main.debug_levels SELECT * FROM disk.debug_levels"); #endif /* DEBUG */ - admindb->execute("PRAGMA foreign_keys = ON"); + admindb->execute("PRAGMA foreign_keys = ON"); } void ProxySQL_Admin::__insert_or_replace_maintable_select_disktable() { - admindb->execute("PRAGMA foreign_keys = OFF"); - admindb->execute("INSERT OR REPLACE INTO main.mysql_servers SELECT * FROM disk.mysql_servers"); - admindb->execute("INSERT OR REPLACE INTO main.mysql_replication_hostgroups SELECT * FROM disk.mysql_replication_hostgroups"); - admindb->execute("INSERT OR REPLACE INTO main.mysql_group_replication_hostgroups SELECT * FROM disk.mysql_group_replication_hostgroups"); - admindb->execute("INSERT OR REPLACE INTO main.mysql_users SELECT * FROM disk.mysql_users"); + admindb->execute("PRAGMA foreign_keys = OFF"); + admindb->execute("INSERT OR REPLACE INTO main.mysql_servers SELECT * FROM disk.mysql_servers"); + admindb->execute("INSERT OR REPLACE INTO main.mysql_replication_hostgroups SELECT * FROM disk.mysql_replication_hostgroups"); + admindb->execute("INSERT OR REPLACE INTO main.mysql_group_replication_hostgroups SELECT * FROM disk.mysql_group_replication_hostgroups"); + admindb->execute("INSERT OR REPLACE INTO main.mysql_users SELECT * FROM disk.mysql_users"); admindb->execute("INSERT OR REPLACE INTO main.mysql_query_rules SELECT * FROM disk.mysql_query_rules"); admindb->execute("INSERT OR REPLACE INTO main.global_variables SELECT * FROM disk.global_variables"); admindb->execute("INSERT OR REPLACE INTO main.scheduler SELECT * FROM disk.scheduler"); + admindb->execute("INSERT OR REPLACE INTO main.proxysql_servers SELECT * FROM disk.proxysql_servers"); #ifdef DEBUG - admindb->execute("INSERT OR IGNORE INTO main.debug_levels SELECT * FROM disk.debug_levels"); + admindb->execute("INSERT OR IGNORE INTO main.debug_levels SELECT * FROM disk.debug_levels"); #endif /* DEBUG */ - admindb->execute("PRAGMA foreign_keys = ON"); + admindb->execute("PRAGMA foreign_keys = ON"); } void ProxySQL_Admin::__delete_disktable() { - admindb->execute("DELETE FROM disk.mysql_servers"); - admindb->execute("DELETE FROM disk.mysql_replication_hostgroups"); - admindb->execute("DELETE FROM disk.mysql_users"); + admindb->execute("DELETE FROM disk.mysql_servers"); + admindb->execute("DELETE FROM disk.mysql_replication_hostgroups"); + admindb->execute("DELETE FROM disk.mysql_users"); admindb->execute("DELETE FROM disk.mysql_query_rules"); admindb->execute("DELETE FROM disk.global_variables"); admindb->execute("DELETE FROM disk.scheduler"); + admindb->execute("DELETE FROM disk.proxysql_servers"); #ifdef DEBUG - admindb->execute("DELETE FROM disk.debug_levels"); + admindb->execute("DELETE FROM disk.debug_levels"); #endif /* DEBUG */ } void ProxySQL_Admin::__insert_or_replace_disktable_select_maintable() { - admindb->execute("INSERT OR REPLACE INTO disk.mysql_servers SELECT * FROM main.mysql_servers"); - admindb->execute("INSERT OR REPLACE INTO disk.mysql_replication_hostgroups SELECT * FROM main.mysql_replication_hostgroups"); - admindb->execute("INSERT OR REPLACE INTO disk.mysql_group_replication_hostgroups SELECT * FROM main.mysql_group_replication_hostgroups"); - admindb->execute("INSERT OR REPLACE INTO disk.mysql_query_rules SELECT * FROM main.mysql_query_rules"); - admindb->execute("INSERT OR REPLACE INTO disk.mysql_users SELECT * FROM main.mysql_users"); + admindb->execute("INSERT OR REPLACE INTO disk.mysql_servers SELECT * FROM main.mysql_servers"); + admindb->execute("INSERT OR REPLACE INTO disk.mysql_replication_hostgroups SELECT * FROM main.mysql_replication_hostgroups"); + admindb->execute("INSERT OR REPLACE INTO disk.mysql_group_replication_hostgroups SELECT * FROM main.mysql_group_replication_hostgroups"); + admindb->execute("INSERT OR REPLACE INTO disk.mysql_query_rules SELECT * FROM main.mysql_query_rules"); + admindb->execute("INSERT OR REPLACE INTO disk.mysql_users SELECT * FROM main.mysql_users"); admindb->execute("INSERT OR REPLACE INTO disk.mysql_query_rules SELECT * FROM main.mysql_query_rules"); admindb->execute("INSERT OR REPLACE INTO disk.global_variables SELECT * FROM main.global_variables"); admindb->execute("INSERT OR REPLACE INTO disk.scheduler SELECT * FROM main.scheduler"); + admindb->execute("INSERT OR REPLACE INTO disk.proxysql_servers SELECT * FROM main.proxysql_servers"); #ifdef DEBUG - admindb->execute("INSERT OR REPLACE INTO disk.debug_levels SELECT * FROM main.debug_levels"); + admindb->execute("INSERT OR REPLACE INTO disk.debug_levels SELECT * FROM main.debug_levels"); #endif /* DEBUG */ } @@ -4597,6 +4767,10 @@ void ProxySQL_Admin::init_mysql_servers() { mysql_servers_wrunlock(); } +void ProxySQL_Admin::init_proxysql_servers() { + load_proxysql_servers_to_runtime(); +} + void ProxySQL_Admin::init_mysql_query_rules() { load_mysql_query_rules_to_runtime(); } @@ -4623,19 +4797,24 @@ void ProxySQL_Admin::__refresh_users() { GloMyAuth->set_all_inactive(USERNAME_BACKEND); GloMyAuth->set_all_inactive(USERNAME_FRONTEND); add_admin_users(); - uint64_t hashB, hashF; - if (calculate_checksum) { - __add_active_users(USERNAME_BACKEND, NULL, &hashB); - __add_active_users(USERNAME_FRONTEND, NULL, &hashF); - } else { + +// uint64_t hashB, hashF; +// if (calculate_checksum) { +// __add_active_users(USERNAME_BACKEND, NULL, &hashB); +// __add_active_users(USERNAME_FRONTEND, NULL, &hashF); +// } else { __add_active_users(USERNAME_BACKEND); __add_active_users(USERNAME_FRONTEND); - } +// } GloMyAuth->remove_inactives(USERNAME_BACKEND); GloMyAuth->remove_inactives(USERNAME_FRONTEND); + uint64_t hash1 = 0; + if (calculate_checksum) { + } set_variable((char *)"admin_credentials",(char *)""); if (calculate_checksum) { - uint64_t hash1 = hashB + hashF; // overflow allowed + hash1 = GloMyAuth->get_runtime_checksum(); + //uint64_t hash1 = hashB + hashF; // overflow allowed uint32_t d32[2]; char buf[20]; memcpy(&d32, &hash1, sizeof(hash1)); @@ -4693,6 +4872,7 @@ void ProxySQL_Admin::__add_active_users(enum cred_username_type usertype, char * char *error=NULL; int cols=0; int affected_rows=0; + bool empty = true; SpookyHash myhash; if (hash1) { myhash.Init(19,3); @@ -4731,6 +4911,7 @@ void ProxySQL_Admin::__add_active_users(enum cred_username_type usertype, char * SQLite3_row *r=new SQLite3_row(cols); r->add_fields(statement); if (hash1) { + empty = false; for (int i=0; ifields[i]) { myhash.Update(r->fields[i],r->sizes[i]); @@ -4800,6 +4981,9 @@ void ProxySQL_Admin::__add_active_users(enum cred_username_type usertype, char * uint64_t h1, h2; myhash.Final(&h1, &h2); *hash1 = h1; + if (empty) { + *hash1 = 0; + } } #else if (resultset) delete resultset; @@ -5366,7 +5550,7 @@ char * ProxySQL_Admin::load_mysql_query_rules_to_runtime() { int affected_rows=0; if (GloQPro==NULL) return (char *)"Global Query Processor not started: command impossible to run"; SQLite3_result *resultset=NULL; - char *query=(char *)"SELECT rule_id, username, schemaname, flagIN, client_addr, proxy_addr, proxy_port, digest, match_digest, match_pattern, negate_match_pattern, re_modifiers, flagOUT, replace_pattern, destination_hostgroup, cache_ttl, reconnect, timeout, retries, delay, next_query_flagIN, mirror_flagOUT, mirror_hostgroup, error_msg, ok_msg, sticky_conn, multiplex, log, apply, comment FROM main.mysql_query_rules WHERE active=1"; + char *query=(char *)"SELECT rule_id, username, schemaname, flagIN, client_addr, proxy_addr, proxy_port, digest, match_digest, match_pattern, negate_match_pattern, re_modifiers, flagOUT, replace_pattern, destination_hostgroup, cache_ttl, reconnect, timeout, retries, delay, next_query_flagIN, mirror_flagOUT, mirror_hostgroup, error_msg, ok_msg, sticky_conn, multiplex, log, apply, comment FROM main.mysql_query_rules WHERE active=1 ORDER BY rule_id"; admindb->execute_statement(query, &error , &cols , &affected_rows , &resultset); if (error) { proxy_error("Error on %s : %s\n", query, error); @@ -5886,7 +6070,11 @@ int ProxySQL_Admin::Read_MySQL_Servers_from_configfile() { int use_ssl=0; int max_latency_ms=0; std::string comment=""; - if (server.lookupValue("address", address)==false) continue; + if (server.lookupValue("address", address)==false) { + if (server.lookupValue("hostname", address)==false) { + continue; + } + } if (server.lookupValue("port", port)==false) continue; if (server.lookupValue("hostgroup", hostgroup)==false) continue; server.lookupValue("status", status); @@ -5945,6 +6133,47 @@ int ProxySQL_Admin::Read_MySQL_Servers_from_configfile() { return rows; } +int ProxySQL_Admin::Read_ProxySQL_Servers_from_configfile() { + const Setting& root = GloVars.confFile->cfg->getRoot(); + int i; + int rows=0; + admindb->execute("PRAGMA foreign_keys = OFF"); + if (root.exists("proxysql_servers")==true) { + const Setting &mysql_servers = root["proxysql_servers"]; + int count = mysql_servers.getLength(); + //fprintf(stderr, "Found %d servers\n",count); + char *q=(char *)"INSERT OR REPLACE INTO proxysql_servers (hostname, port, weight, comment) VALUES (\"%s\", %d, %d, '%s')"; + for (i=0; i< count; i++) { + const Setting &server = mysql_servers[i]; + std::string address; + int port; + int weight=0; + std::string comment=""; + if (server.lookupValue("address", address)==false) { + if (server.lookupValue("hostname", address)==false) { + continue; + } + } + if (server.lookupValue("port", port)==false) continue; + server.lookupValue("weight", weight); + server.lookupValue("comment", comment); + char *o1=strdup(comment.c_str()); + char *o=escape_string_single_quotes(o1, false); + char *query=(char *)malloc(strlen(q)+strlen(address.c_str())+strlen(o)+128); + sprintf(query, q, address.c_str(), port, weight, o); + proxy_info("Cluster: Adding ProxySQL Servers %s:%d from config file\n", address.c_str(), port); + //fprintf(stderr, "%s\n", query); + admindb->execute(query); + if (o!=o1) free(o); + free(o1); + free(query); + rows++; + } + } + admindb->execute("PRAGMA foreign_keys = ON"); + return rows; +} + extern "C" ProxySQL_Admin * create_ProxySQL_Admin_func() { return new ProxySQL_Admin(); } @@ -6422,19 +6651,19 @@ unsigned long long ProxySQL_External_Scheduler::run_once() { return next_run; } -void ProxySQL_Admin::load_proxysql_servers_to_runtime() { +void ProxySQL_Admin::load_proxysql_servers_to_runtime(bool _lock) { // make sure that the caller has called mysql_servers_wrlock() char *error=NULL; int cols=0; int affected_rows=0; SQLite3_result *resultset=NULL; - char *query=(char *)"SELECT hostname,port,weight,comment FROM proxysql_servers"; + char *query=(char *)"SELECT hostname, port, weight, comment FROM proxysql_servers ORDER BY hostname, port"; proxy_debug(PROXY_DEBUG_ADMIN, 4, "%s\n", query); admindb->execute_statement(query, &error , &cols , &affected_rows , &resultset); if (error) { proxy_error("Error on %s : %s\n", query, error); } else { - GloProxyCluster->load_servers_list(resultset); + GloProxyCluster->load_servers_list(resultset, _lock); // if (checksum_variables.checksum_mysql_query_rules) { pthread_mutex_lock(&GloVars.checksum_mutex); uint64_t hash1 = resultset->raw_checksum(); diff --git a/lib/ProxySQL_Cluster.cpp b/lib/ProxySQL_Cluster.cpp index 572890fbf..1577c58ba 100644 --- a/lib/ProxySQL_Cluster.cpp +++ b/lib/ProxySQL_Cluster.cpp @@ -9,10 +9,25 @@ #endif /* DEBUG */ #define PROXYSQL_CLUSTER_VERSION "0.1.0702" DEB + +#define SAFE_SQLITE3_STEP(_stmt) do {\ + do {\ + rc=sqlite3_step(_stmt);\ + if (rc!=SQLITE_DONE) {\ + assert(rc==SQLITE_LOCKED);\ + usleep(10);\ + }\ + } while (rc!=SQLITE_DONE);\ +} while (0) + + + static char *NODE_COMPUTE_DELIMITER=(char *)"-gtyw23a-"; // a random string used for hashing extern ProxySQL_Cluster * GloProxyCluster; +extern ProxySQL_Admin *GloAdmin; + typedef struct _proxy_node_address_t { pthread_t thrid; uint64_t hash; // unused for now @@ -35,6 +50,7 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { mysql_thread_init(); pthread_detach(pthread_self()); + proxy_info("Cluster: starting thread for peer %s:%d\n", node->hostname, node->port); char *query1 = (char *)"SELECT GLOBAL_CHECKSUM()"; // in future this will be used for "light check" char *query2 = (char *)"SELECT * FROM stats_mysql_global ORDER BY Variable_Name"; char *query3 = (char *)"SELECT * FROM runtime_checksums_values ORDER BY name"; @@ -153,6 +169,7 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { mysql_close(conn); } } else { + proxy_warning("Cluster: unable to connect to peer %s:%d . Error: %s\n", node->hostname, node->port, mysql_error(conn)); mysql_close(conn); conn = mysql_init(NULL); int ci = __sync_fetch_and_add(&GloProxyCluster->cluster_check_interval_ms,0); @@ -167,6 +184,7 @@ __exit_monitor_thread: if (conn->net.vio) { mysql_close(conn); } + proxy_info("Cluster: closing thread for peer %s:%d\n", node->hostname, node->port); free(node->hostname); free(node); //pthread_exit(0); @@ -217,9 +235,11 @@ ProxySQL_Node_Entry::ProxySQL_Node_Entry(char *_hostname, uint16_t _port, uint64 for (int i = 0; i < PROXYSQL_NODE_METRICS_LEN ; i++) { metrics[i] = new ProxySQL_Node_Metrics(); } + proxy_info("Created new Cluster Node Entry for host %s:%d\n",hostname,port); } ProxySQL_Node_Entry::~ProxySQL_Node_Entry() { + proxy_info("Destroyed Cluster Node Entry for host %s:%d\n",hostname,port); if (hostname) { free(hostname); hostname = NULL; @@ -302,6 +322,10 @@ void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { strcpy(checksums_values.mysql_query_rules.checksum, row[3]); checksums_values.mysql_query_rules.last_changed = now; checksums_values.mysql_query_rules.diff_check = 1; + proxy_info("Cluster: detected a new checksum for mysql_query_rules from peer %s:%d, version %llu, epoch %llu, checksum %s . Not syncing yet ...\n", hostname, port, checksums_values.mysql_query_rules.version, checksums_values.mysql_query_rules.epoch, checksums_values.mysql_query_rules.checksum); + if (strcmp(checksums_values.mysql_query_rules.checksum, GloVars.checksums_values.mysql_query_rules.checksum) == 0) { + proxy_info("Cluster: checksum for mysql_query_rules from peer %s:%d matches with local checksum %s , we won't sync.\n", hostname, port, GloVars.checksums_values.mysql_query_rules.checksum); + } } else { checksums_values.mysql_query_rules.diff_check++; } @@ -318,6 +342,10 @@ void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { strcpy(checksums_values.mysql_servers.checksum, row[3]); checksums_values.mysql_servers.last_changed = now; checksums_values.mysql_servers.diff_check = 1; + proxy_info("Cluster: detected a new checksum for mysql_servers from peer %s:%d, version %llu, epoch %llu, checksum %s . Not syncing yet ...\n", hostname, port, checksums_values.mysql_servers.version, checksums_values.mysql_servers.epoch, checksums_values.mysql_servers.checksum); + if (strcmp(checksums_values.mysql_servers.checksum, GloVars.checksums_values.mysql_servers.checksum) == 0) { + proxy_info("Cluster: checksum for mysql_servers from peer %s:%d matches with local checksum %s , we won't sync.\n", hostname, port, GloVars.checksums_values.mysql_servers.checksum); + } } else { checksums_values.mysql_servers.diff_check++; } @@ -334,6 +362,10 @@ void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { strcpy(checksums_values.mysql_users.checksum, row[3]); checksums_values.mysql_users.last_changed = now; checksums_values.mysql_users.diff_check = 1; + proxy_info("Cluster: detected a new checksum for mysql_users from peer %s:%d, version %llu, epoch %llu, checksum %s . Not syncing yet ...\n", hostname, port, checksums_values.mysql_users.version, checksums_values.mysql_users.epoch, checksums_values.mysql_users.checksum); + if (strcmp(checksums_values.mysql_users.checksum, GloVars.checksums_values.mysql_users.checksum) == 0) { + proxy_info("Cluster: checksum for mysql_users from peer %s:%d matches with local checksum %s , we won't sync.\n", hostname, port, GloVars.checksums_values.mysql_users.checksum); + } } else { checksums_values.mysql_users.diff_check++; } @@ -366,6 +398,10 @@ void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { strcpy(checksums_values.proxysql_servers.checksum, row[3]); checksums_values.proxysql_servers.last_changed = now; checksums_values.proxysql_servers.diff_check = 1; + proxy_info("Cluster: detected a new checksum for proxysql_servers from peer %s:%d, version %llu, epoch %llu, checksum %s . Not syncing yet ...\n", hostname, port, checksums_values.proxysql_servers.version, checksums_values.proxysql_servers.epoch, checksums_values.proxysql_servers.checksum); + if (strcmp(checksums_values.proxysql_servers.checksum, GloVars.checksums_values.proxysql_servers.checksum) == 0) { + proxy_info("Cluster: checksum for proxysql_servers from peer %s:%d matches with local checksum %s , we won't sync.\n", hostname, port, GloVars.checksums_values.proxysql_servers.checksum); + } } else { checksums_values.proxysql_servers.diff_check++; } @@ -383,14 +419,23 @@ void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { v->diff_check++; v = &checksums_values.mysql_query_rules; v->last_updated = now; + if (strcmp(v->checksum, GloVars.checksums_values.mysql_query_rules.checksum) == 0) { + v->diff_check = 0; + } if (v->diff_check) v->diff_check++; v = &checksums_values.mysql_servers; v->last_updated = now; + if (strcmp(v->checksum, GloVars.checksums_values.mysql_servers.checksum) == 0) { + v->diff_check = 0; + } if (v->diff_check) v->diff_check++; v = &checksums_values.mysql_users; v->last_updated = now; + if (strcmp(v->checksum, GloVars.checksums_values.mysql_users.checksum) == 0) { + v->diff_check = 0; + } if (v->diff_check) v->diff_check++; v = &checksums_values.mysql_variables; @@ -399,10 +444,460 @@ void ProxySQL_Node_Entry::set_checksums(MYSQL_RES *_r) { v->diff_check++; v = &checksums_values.proxysql_servers; v->last_updated = now; + if (strcmp(v->checksum, GloVars.checksums_values.proxysql_servers.checksum) == 0) { + v->diff_check = 0; + } if (v->diff_check) v->diff_check++; } pthread_mutex_unlock(&GloVars.checksum_mutex); + // we now do a series of checks, and we take action + // note that this is done outside the critical section + // as mutex on GloVars.checksum_mutex is already released + unsigned int diff_mqr = (unsigned int)__sync_fetch_and_add(&GloProxyCluster->cluster_mysql_query_rules_diffs_before_sync,0); + unsigned int diff_ms = (unsigned int)__sync_fetch_and_add(&GloProxyCluster->cluster_mysql_servers_diffs_before_sync,0); + unsigned int diff_mu = (unsigned int)__sync_fetch_and_add(&GloProxyCluster->cluster_mysql_users_diffs_before_sync,0); + unsigned int diff_ps = (unsigned int)__sync_fetch_and_add(&GloProxyCluster->cluster_proxysql_servers_diffs_before_sync,0); + ProxySQL_Checksum_Value_2 *v = NULL; + if (diff_mqr) { + unsigned long long own_version = __sync_fetch_and_add(&GloVars.checksums_values.mysql_query_rules.version,0); + unsigned long long own_epoch = __sync_fetch_and_add(&GloVars.checksums_values.mysql_query_rules.epoch,0); + v = &checksums_values.mysql_query_rules; + if (v->version > 1) { + if ( + (own_version == 1) // we just booted + || + (v->epoch > own_version) // epoch is newer + ) { + if (v->diff_check > diff_mqr) { + proxy_info("Cluster: detected a peer %s:%d with mysql_query_rules version %llu, epoch %llu, diff_check %llu. Own version: %llu, epoch: %llu. Proceeding with remote sync\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch); + GloProxyCluster->pull_mysql_query_rules_from_peer(); + } + } + } else { + if (v->diff_check && (v->diff_check % (diff_mqr*10)) == 0) { + proxy_warning("Cluster: detected a peer %s:%d with mysql_query_rules version %llu, epoch %llu, diff_check %llu. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %llu checks until LOAD MYSQL QUERY RULES TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_mqr*10)); + } + } + } + if (diff_ms) { + v = &checksums_values.mysql_servers; + unsigned long long own_version = __sync_fetch_and_add(&GloVars.checksums_values.mysql_servers.version,0); + unsigned long long own_epoch = __sync_fetch_and_add(&GloVars.checksums_values.mysql_servers.epoch,0); + if (v->version > 1) { + if ( + (own_version == 1) // we just booted + || + (v->epoch > own_version) // epoch is newer + ) { + if (v->diff_check > diff_ms) { + proxy_info("Cluster: detected a peer %s:%d with mysql_servers version %llu, epoch %llu, diff_check %llu. Own version: %llu, epoch: %llu. Proceeding with remote sync\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch); + GloProxyCluster->pull_mysql_servers_from_peer(); + } + } + } else { + if (v->diff_check && (v->diff_check % (diff_ms*10)) == 0) { + proxy_warning("Cluster: detected a peer %s:%d with mysql_servers version %llu, epoch %llu, diff_check %llu. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %llu checks until LOAD MYSQL SERVERS TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_ms*10)); + } + } + } + if (diff_mu) { + v = &checksums_values.mysql_users; + unsigned long long own_version = __sync_fetch_and_add(&GloVars.checksums_values.mysql_users.version,0); + unsigned long long own_epoch = __sync_fetch_and_add(&GloVars.checksums_values.mysql_users.epoch,0); + if (v->version > 1) { + if ( + (own_version == 1) // we just booted + || + (v->epoch > own_version) // epoch is newer + ) { + if (v->diff_check > diff_mu) { + proxy_info("Cluster: detected a peer %s:%d with mysql_users version %llu, epoch %llu, diff_check %llu. Own version: %llu, epoch: %llu. Proceeding with remote sync\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch); + GloProxyCluster->pull_mysql_users_from_peer(); + } + } + } else { + if (v->diff_check && (v->diff_check % (diff_mu*10)) == 0) { + proxy_warning("Cluster: detected a peer %s:%d with mysql_users version %llu, epoch %llu, diff_check %llu. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %llu checks until LOAD MYSQL SERVERS TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_mu*10)); + } + } + } + if (diff_ps) { + v = &checksums_values.proxysql_servers; + unsigned long long own_version = __sync_fetch_and_add(&GloVars.checksums_values.proxysql_servers.version,0); + unsigned long long own_epoch = __sync_fetch_and_add(&GloVars.checksums_values.proxysql_servers.epoch,0); + if (v->version > 1) { + if ( + (own_version == 1) // we just booted + || + (v->epoch > own_version) // epoch is newer + ) { + if (v->diff_check > diff_ps) { + proxy_info("Cluster: detected a peer %s:%d with proxysql_servers version %llu, epoch %llu, diff_check %llu. Own version: %llu, epoch: %llu. Proceeding with remote sync\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch); + GloProxyCluster->pull_proxysql_servers_from_peer(); + } + } + } else { + if (v->diff_check && (v->diff_check % (diff_ms*10)) == 0) { + proxy_warning("Cluster: detected a peer %s:%d with proxysql_servers version %llu, epoch %llu, diff_check %llu. Own version: %llu, epoch: %llu. diff_check is increasing, but version 1 doesn't allow sync. This message will be repeated every %llu checks until LOAD PROXYSQL SERVERS TO RUNTIME is executed on candidate master.\n", hostname, port, v->version, v->epoch, v->diff_check, own_version, own_epoch, (diff_ps*10)); + } + } + } +} + +void ProxySQL_Cluster::pull_mysql_query_rules_from_peer() { + char * hostname = NULL; + uint16_t port = 0; + pthread_mutex_lock(&GloProxyCluster->update_mysql_query_rules_mutex); + nodes.get_peer_to_sync_mysql_query_rules(&hostname, &port); + if (hostname) { + char *username = NULL; + char *password = NULL; + bool rc_bool = true; + MYSQL *rc_conn; + int rc_query; + int rc; + MYSQL *conn = mysql_init(NULL); + if (conn==NULL) { + proxy_error("Unable to run mysql_init()\n"); + goto __exit_pull_mysql_query_rules_from_peer; + } + GloProxyCluster->get_credentials(&username, &password); + if (strlen(username)) { // do not monitor if the username is empty + unsigned int timeout = 1; + unsigned int timeout_long = 60; + mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); + mysql_options(conn, MYSQL_OPT_READ_TIMEOUT, &timeout_long); + mysql_options(conn, MYSQL_OPT_WRITE_TIMEOUT, &timeout); + proxy_info("Cluster: Fetching MySQL Query Rules from peer %s:%d started\n", hostname, port); + rc_conn = mysql_real_connect(conn, hostname, username, password, NULL, port, NULL, 0); + if (rc_conn) { + rc_query = mysql_query(conn,"SELECT rule_id, username, schemaname, flagIN, client_addr, proxy_addr, proxy_port, digest, match_digest, match_pattern, negate_match_pattern, re_modifiers, flagOUT, replace_pattern, destination_hostgroup, cache_ttl, reconnect, timeout, retries, delay, next_query_flagIN, mirror_flagOUT, mirror_hostgroup, error_msg, ok_msg, sticky_conn, multiplex, log, apply, comment FROM runtime_mysql_query_rules"); + if ( rc_query == 0 ) { + MYSQL_RES *result = mysql_store_result(conn); + GloAdmin->admindb->execute("DELETE FROM mysql_query_rules"); + MYSQL_ROW row; + char *q = (char *)"INSERT INTO mysql_query_rules (rule_id, username, schemaname, flagIN, client_addr, proxy_addr, proxy_port, digest, match_digest, match_pattern, negate_match_pattern, re_modifiers, flagOUT, replace_pattern, destination_hostgroup, cache_ttl, reconnect, timeout, retries, delay, next_query_flagIN, mirror_flagOUT, mirror_hostgroup, error_msg, ok_msg, sticky_conn, multiplex, log, apply, comment) VALUES (?1 , ?2 , ?3 , ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23, ?24, ?25, ?26, ?27, ?28, ?29, ?30)"; + sqlite3_stmt *statement1 = NULL; + sqlite3 *mydb3 = GloAdmin->admindb->get_db(); + rc=sqlite3_prepare_v2(mydb3, q, -1, &statement1, 0); + assert(rc==SQLITE_OK); + while ((row = mysql_fetch_row(result))) { + rc=sqlite3_bind_int64(statement1, 1, atoll(row[0])); assert(rc==SQLITE_OK); // rule_id + rc=sqlite3_bind_text(statement1, 2, row[1], -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); // username + rc=sqlite3_bind_text(statement1, 3, row[2], -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); // schemaname + rc=sqlite3_bind_int64(statement1, 4, atoll(row[3])); assert(rc==SQLITE_OK); // flagIN + rc=sqlite3_bind_text(statement1, 5, row[4], -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); // client_addr + rc=sqlite3_bind_text(statement1, 6, row[5], -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); // proxy_addr + rc=sqlite3_bind_int64(statement1, 7, atoll(row[6])); assert(rc==SQLITE_OK); // proxy_port + rc=sqlite3_bind_text(statement1, 8, row[7], -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); // digest + rc=sqlite3_bind_text(statement1, 9, row[8], -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); // match_digest + rc=sqlite3_bind_text(statement1, 10, row[9], -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); // match_pattern + rc=sqlite3_bind_int64(statement1, 11, atoll(row[10])); assert(rc==SQLITE_OK); // negate_match_pattern + rc=sqlite3_bind_text(statement1, 12, row[11], -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); // re_modifiers + rc=sqlite3_bind_int64(statement1, 13, atoll(row[12])); assert(rc==SQLITE_OK); // flagOUT + rc=sqlite3_bind_text(statement1, 14, row[13], -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); // replace_pattern + rc=sqlite3_bind_int64(statement1, 15, atoll(row[14])); assert(rc==SQLITE_OK); // destination_hostgroup + rc=sqlite3_bind_int64(statement1, 16, atoll(row[15])); assert(rc==SQLITE_OK); // cache_ttl + rc=sqlite3_bind_int64(statement1, 17, atoll(row[16])); assert(rc==SQLITE_OK); // reconnect + rc=sqlite3_bind_int64(statement1, 18, atoll(row[17])); assert(rc==SQLITE_OK); // timeout + rc=sqlite3_bind_int64(statement1, 19, atoll(row[18])); assert(rc==SQLITE_OK); // retries + rc=sqlite3_bind_int64(statement1, 20, atoll(row[19])); assert(rc==SQLITE_OK); // delay + rc=sqlite3_bind_int64(statement1, 21, atoll(row[20])); assert(rc==SQLITE_OK); // next_query_flagIN + rc=sqlite3_bind_int64(statement1, 22, atoll(row[21])); assert(rc==SQLITE_OK); // mirror_flagOUT + rc=sqlite3_bind_int64(statement1, 23, atoll(row[22])); assert(rc==SQLITE_OK); // mirror_hostgroup + rc=sqlite3_bind_text(statement1, 24, row[23], -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); // error_msg + rc=sqlite3_bind_text(statement1, 25, row[24], -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); // OK_msg + rc=sqlite3_bind_int64(statement1, 26, atoll(row[25])); assert(rc==SQLITE_OK); // sticky_conn + rc=sqlite3_bind_int64(statement1, 27, atoll(row[26])); assert(rc==SQLITE_OK); // multiplex + rc=sqlite3_bind_int64(statement1, 28, atoll(row[27])); assert(rc==SQLITE_OK); // log + rc=sqlite3_bind_int64(statement1, 29, atoll(row[28])); assert(rc==SQLITE_OK); // apply + rc=sqlite3_bind_text(statement1, 30, row[29], -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); // comment + + SAFE_SQLITE3_STEP(statement1); + rc=sqlite3_clear_bindings(statement1); assert(rc==SQLITE_OK); + rc=sqlite3_reset(statement1); assert(rc==SQLITE_OK); + } + mysql_free_result(result); + proxy_info("Cluster: Fetching MySQL Query Rules from peer %s:%d completed\n", hostname, port); + proxy_info("Cluster: Loading to runtime MySQL Servers from peer %s:%d\n", hostname, port); + GloAdmin->load_mysql_query_rules_to_runtime(); + if (__sync_fetch_and_add(&GloProxyCluster->cluster_mysql_query_rules_save_to_disk,0) == true) { + proxy_info("Cluster: Saving to disk MySQL Query Rules from peer %s:%d\n", hostname, port); + GloAdmin->flush_mysql_query_rules__from_memory_to_disk(); + } + } else { + proxy_info("Cluster: Fetching MySQL Query Rules from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); + } + } else { + proxy_info("Cluster: Fetching MySQL Query Rules from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); + } + } +__exit_pull_mysql_query_rules_from_peer: + if (conn) { + if (conn->net.vio) { + mysql_close(conn); + } + } + free(hostname); + } + pthread_mutex_unlock(&GloProxyCluster->update_mysql_query_rules_mutex); +} + +void ProxySQL_Cluster::pull_mysql_users_from_peer() { + char * hostname = NULL; + uint16_t port = 0; + pthread_mutex_lock(&GloProxyCluster->update_mysql_users_mutex); + nodes.get_peer_to_sync_mysql_users(&hostname, &port); + if (hostname) { + char *username = NULL; + char *password = NULL; + bool rc_bool = true; + MYSQL *rc_conn; + int rc_query; + int rc; + MYSQL *conn = mysql_init(NULL); + if (conn==NULL) { + proxy_error("Unable to run mysql_init()\n"); + goto __exit_pull_mysql_users_from_peer; + } + GloProxyCluster->get_credentials(&username, &password); + if (strlen(username)) { // do not monitor if the username is empty + unsigned int timeout = 1; + unsigned int timeout_long = 60; + mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); + mysql_options(conn, MYSQL_OPT_READ_TIMEOUT, &timeout_long); + mysql_options(conn, MYSQL_OPT_WRITE_TIMEOUT, &timeout); + proxy_info("Cluster: Fetching MySQL Users from peer %s:%d started\n", hostname, port); + rc_conn = mysql_real_connect(conn, hostname, username, password, NULL, port, NULL, 0); + if (rc_conn) { + rc_query = mysql_query(conn, "SELECT username, password, active, use_ssl, default_hostgroup, default_schema, schema_locked, transaction_persistent, fast_forward, backend, frontend, max_connections FROM runtime_mysql_users"); + if ( rc_query == 0 ) { + MYSQL_RES *result = mysql_store_result(conn); + GloAdmin->admindb->execute("DELETE FROM mysql_users"); + MYSQL_ROW row; + char *q = (char *)"INSERT INTO mysql_users (username, password, active, use_ssl, default_hostgroup, default_schema, schema_locked, transaction_persistent, fast_forward, backend, frontend, max_connections) VALUES (?1 , ?2 , ?3 , ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)"; + sqlite3_stmt *statement1 = NULL; + sqlite3 *mydb3 = GloAdmin->admindb->get_db(); + rc=sqlite3_prepare_v2(mydb3, q, -1, &statement1, 0); + assert(rc==SQLITE_OK); + while ((row = mysql_fetch_row(result))) { + rc=sqlite3_bind_text(statement1, 1, row[0], -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); // username + rc=sqlite3_bind_text(statement1, 2, row[1], -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); // password + rc=sqlite3_bind_int64(statement1, 3, atoll(row[2])); assert(rc==SQLITE_OK); // active + rc=sqlite3_bind_int64(statement1, 4, atoll(row[3])); assert(rc==SQLITE_OK); // use_ssl + rc=sqlite3_bind_int64(statement1, 5, atoll(row[4])); assert(rc==SQLITE_OK); // default_hostgroup + rc=sqlite3_bind_text(statement1, 6, row[5], -1, SQLITE_TRANSIENT); assert(rc==SQLITE_OK); // default_schema + rc=sqlite3_bind_int64(statement1, 7, atoll(row[6])); assert(rc==SQLITE_OK); // schema_locked + rc=sqlite3_bind_int64(statement1, 8, atoll(row[7])); assert(rc==SQLITE_OK); // transaction_persistent + rc=sqlite3_bind_int64(statement1, 9, atoll(row[8])); assert(rc==SQLITE_OK); // fast_forward + rc=sqlite3_bind_int64(statement1, 10, atoll(row[9])); assert(rc==SQLITE_OK); // backend + rc=sqlite3_bind_int64(statement1, 11, atoll(row[10])); assert(rc==SQLITE_OK); // frontend + rc=sqlite3_bind_int64(statement1, 12, atoll(row[11])); assert(rc==SQLITE_OK); // max_connection + + SAFE_SQLITE3_STEP(statement1); + rc=sqlite3_clear_bindings(statement1); assert(rc==SQLITE_OK); + rc=sqlite3_reset(statement1); assert(rc==SQLITE_OK); + } + mysql_free_result(result); + proxy_info("Cluster: Fetching MySQL Users from peer %s:%d completed\n", hostname, port); + proxy_info("Cluster: Loading to runtime MySQL Users from peer %s:%d\n", hostname, port); + GloAdmin->init_users(); + if (__sync_fetch_and_add(&GloProxyCluster->cluster_mysql_query_rules_save_to_disk,0) == true) { + proxy_info("Cluster: Saving to disk MySQL Query Rules from peer %s:%d\n", hostname, port); + GloAdmin->flush_mysql_users__from_memory_to_disk(); + } + } else { + proxy_info("Cluster: Fetching MySQL Users from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); + } + } else { + proxy_info("Cluster: Fetching MySQL Users from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); + } + } +__exit_pull_mysql_users_from_peer: + if (conn) { + if (conn->net.vio) { + mysql_close(conn); + } + } + free(hostname); + } + pthread_mutex_unlock(&GloProxyCluster->update_mysql_users_mutex); +} + +void ProxySQL_Cluster::pull_mysql_servers_from_peer() { + char * hostname = NULL; + uint16_t port = 0; + pthread_mutex_lock(&GloProxyCluster->update_mysql_servers_mutex); + nodes.get_peer_to_sync_mysql_servers(&hostname, &port); + if (hostname) { + char *username = NULL; + char *password = NULL; + bool rc_bool = true; + MYSQL *rc_conn; + int rc_query; + MYSQL *conn = mysql_init(NULL); + if (conn==NULL) { + proxy_error("Unable to run mysql_init()\n"); + goto __exit_pull_mysql_servers_from_peer; + } + GloProxyCluster->get_credentials(&username, &password); + if (strlen(username)) { // do not monitor if the username is empty + unsigned int timeout = 1; + unsigned int timeout_long = 60; + mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); + mysql_options(conn, MYSQL_OPT_READ_TIMEOUT, &timeout_long); + mysql_options(conn, MYSQL_OPT_WRITE_TIMEOUT, &timeout); + proxy_info("Cluster: Fetching MySQL Servers from peer %s:%d started\n", hostname, port); + rc_conn = mysql_real_connect(conn, hostname, username, password, NULL, port, NULL, 0); + if (rc_conn) { + rc_query = mysql_query(conn,"SELECT hostgroup_id, hostname, port, status, weight, compression, max_connections, max_replication_lag, use_ssl, max_latency_ms, comment FROM runtime_mysql_servers"); + if ( rc_query == 0 ) { + MYSQL_RES *result = mysql_store_result(conn); + GloAdmin->admindb->execute("DELETE FROM mysql_servers"); + MYSQL_ROW row; + char *q=(char *)"INSERT INTO mysql_servers (hostgroup_id, hostname, port, status, weight, compression, max_connections, max_replication_lag, use_ssl, max_latency_ms, comment) VALUES (%s, \"%s\", %s, \"%s\", %s, %s, %s, %s, %s, %s, '%s')"; + while ((row = mysql_fetch_row(result))) { + int i; + int l=0; + for (i=0; i<10; i++) { + l+=strlen(row[i]); + } + char *o=escape_string_single_quotes(row[10],false); + char *query = (char *)malloc(strlen(q)+i+strlen(o)+64); + + sprintf(query,q,row[0],row[1],row[2],row[3],row[4],row[5],row[6],row[7],row[8],row[9],o); + if (o!=row[10]) { // there was a copy + free(o); + } + GloAdmin->admindb->execute(query); + free(query); + } + mysql_free_result(result); + + rc_query = mysql_query(conn,"SELECT writer_hostgroup, reader_hostgroup, comment FROM mysql_replication_hostgroups"); + if ( rc_query == 0 ) { + MYSQL_RES *result = mysql_store_result(conn); + GloAdmin->admindb->execute("DELETE FROM mysql_replication_hostgroups"); + MYSQL_ROW row; + char *q=(char *)"INSERT INTO mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup, comment) VALUES (%s, %s, '%s')"; + while ((row = mysql_fetch_row(result))) { + int i; + int l=0; + for (i=0; i<2; i++) { + l+=strlen(row[i]); + } + char *o=escape_string_single_quotes(row[2],false); + char *query = (char *)malloc(strlen(q)+i+strlen(o)+64); + + sprintf(query,q,row[0],row[1],o); + if (o!=row[2]) { // there was a copy + free(o); + } + GloAdmin->admindb->execute(query); + free(query); + } + mysql_free_result(result); + proxy_info("Cluster: Fetching MySQL Servers from peer %s:%d completed\n", hostname, port); + proxy_info("Cluster: Loading to runtime MySQL Servers from peer %s:%d\n", hostname, port); + GloAdmin->load_mysql_servers_to_runtime(); + if (__sync_fetch_and_add(&GloProxyCluster->cluster_mysql_servers_save_to_disk,0) == true) { + proxy_info("Cluster: Saving to disk MySQL Servers from peer %s:%d\n", hostname, port); + GloAdmin->flush_mysql_servers__from_memory_to_disk(); + } + } else { + proxy_info("Cluster: Fetching MySQL Servers from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); + } + } else { + proxy_info("Cluster: Fetching MySQL Servers from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); + } + } else { + proxy_info("Cluster: Fetching MySQL Servers from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); + } + } +__exit_pull_mysql_servers_from_peer: + if (conn) { + if (conn->net.vio) { + mysql_close(conn); + } + } + free(hostname); + } + pthread_mutex_unlock(&GloProxyCluster->update_mysql_servers_mutex); +} + +void ProxySQL_Cluster::pull_proxysql_servers_from_peer() { + char * hostname = NULL; + uint16_t port = 0; + pthread_mutex_lock(&GloProxyCluster->update_proxysql_servers_mutex); + nodes.get_peer_to_sync_proxysql_servers(&hostname, &port); + if (hostname) { + char *username = NULL; + char *password = NULL; + bool rc_bool = true; + MYSQL *rc_conn; + int rc_query; + MYSQL *conn = mysql_init(NULL); + if (conn==NULL) { + proxy_error("Unable to run mysql_init()\n"); + goto __exit_pull_proxysql_servers_from_peer; + } + GloProxyCluster->get_credentials(&username, &password); + if (strlen(username)) { // do not monitor if the username is empty + unsigned int timeout = 1; + unsigned int timeout_long = 60; + mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); + mysql_options(conn, MYSQL_OPT_READ_TIMEOUT, &timeout_long); + mysql_options(conn, MYSQL_OPT_WRITE_TIMEOUT, &timeout); + proxy_info("Cluster: Fetching ProxySQL Servers from peer %s:%d started\n", hostname, port); + rc_conn = mysql_real_connect(conn, hostname, username, password, NULL, port, NULL, 0); + if (rc_conn) { + rc_query = mysql_query(conn,"SELECT hostname, port, weight, comment FROM runtime_proxysql_servers"); + if ( rc_query == 0 ) { + MYSQL_RES *result = mysql_store_result(conn); + GloAdmin->admindb->execute("DELETE FROM proxysql_servers"); + MYSQL_ROW row; + char *q=(char *)"INSERT INTO proxysql_servers (hostname, port, weight, comment) VALUES (\"%s\", %s, %s, '%s')"; + while ((row = mysql_fetch_row(result))) { + int i; + int l=0; + for (i=0; i<3; i++) { + l+=strlen(row[i]); + } + char *o=escape_string_single_quotes(row[3],false); + char *query = (char *)malloc(strlen(q)+i+strlen(o)+64); + + sprintf(query,q,row[0],row[1],row[2],o); + if (o!=row[3]) { // there was a copy + free(o); + } + GloAdmin->admindb->execute(query); + free(query); + } + mysql_free_result(result); + proxy_info("Cluster: Fetching ProxySQL Servers from peer %s:%d completed\n", hostname, port); + proxy_info("Cluster: Loading to runtime ProxySQL Servers from peer %s:%d\n", hostname, port); + GloAdmin->load_proxysql_servers_to_runtime(false); + if (__sync_fetch_and_add(&GloProxyCluster->cluster_proxysql_servers_save_to_disk,0) == true) { + proxy_info("Cluster: Saving to disk ProxySQL Servers from peer %s:%d\n", hostname, port); + GloAdmin->flush_proxysql_servers__from_memory_to_disk(); + } + } else { + proxy_info("Cluster: Fetching ProxySQL Servers from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); + } + } else { + proxy_info("Cluster: Fetching ProxySQL Servers from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); + } + } +__exit_pull_proxysql_servers_from_peer: + if (conn) { + if (conn->net.vio) { + mysql_close(conn); + } + } + free(hostname); + } + pthread_mutex_unlock(&GloProxyCluster->update_proxysql_servers_mutex); } void ProxySQL_Node_Entry::set_metrics(MYSQL_RES *_r, unsigned long long _response_time) { @@ -487,8 +982,9 @@ uint64_t ProxySQL_Cluster_Nodes::generate_hash(char *_hostname, uint16_t _port) return hash_; } -void ProxySQL_Cluster_Nodes::load_servers_list(SQLite3_result *resultset) { - pthread_mutex_lock(&mutex); +void ProxySQL_Cluster_Nodes::load_servers_list(SQLite3_result *resultset, bool _lock) { + if (_lock) + pthread_mutex_lock(&mutex); set_all_inactive(); for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { SQLite3_row *r=*it; @@ -521,7 +1017,8 @@ void ProxySQL_Cluster_Nodes::load_servers_list(SQLite3_result *resultset) { } } remove_inactives(); - pthread_mutex_unlock(&mutex); + if (_lock) + pthread_mutex_unlock(&mutex); } // if it returns false , the node doesn't exist anymore and the monitor should stop @@ -578,6 +1075,130 @@ bool ProxySQL_Cluster_Nodes::Update_Node_Metrics(char * _h, uint16_t _p, MYSQL_R return ret; } +void ProxySQL_Cluster_Nodes::get_peer_to_sync_mysql_query_rules(char **host, uint16_t *port) { + unsigned long long version = 0; + unsigned long long epoch = 0; + char *hostname = NULL; + uint16_t p = 0; +// pthread_mutex_lock(&mutex); + //unsigned long long curtime = monotonic_time(); + for( std::unordered_map::iterator it = umap_proxy_nodes.begin(); it != umap_proxy_nodes.end(); ) { + ProxySQL_Node_Entry * node = it->second; + ProxySQL_Checksum_Value_2 * v = &node->checksums_values.mysql_query_rules; + if (v->version > 1) { + if ( v->epoch > epoch && v->diff_check > 3) { + epoch = v->epoch; + version = v->version; + if (hostname) { + free(hostname); + } + hostname=strdup(node->get_hostname()); + p = node->get_port(); + } + } + it++; + } +// pthread_mutex_unlock(&mutex); + if (hostname) { + *host = hostname; + *port = p; + } + proxy_info("Cluster: detected peer %s:%d with mysql_query_rules version %llu, epoch %llu\n", hostname, p, version, epoch); +} + +void ProxySQL_Cluster_Nodes::get_peer_to_sync_mysql_servers(char **host, uint16_t *port) { + unsigned long long version = 0; + unsigned long long epoch = 0; + char *hostname = NULL; + uint16_t p = 0; +// pthread_mutex_lock(&mutex); + //unsigned long long curtime = monotonic_time(); + for( std::unordered_map::iterator it = umap_proxy_nodes.begin(); it != umap_proxy_nodes.end(); ) { + ProxySQL_Node_Entry * node = it->second; + ProxySQL_Checksum_Value_2 * v = &node->checksums_values.mysql_servers; + if (v->version > 1) { + if ( v->epoch > epoch && v->diff_check > 3) { + epoch = v->epoch; + version = v->version; + if (hostname) { + free(hostname); + } + hostname=strdup(node->get_hostname()); + p = node->get_port(); + } + } + it++; + } +// pthread_mutex_unlock(&mutex); + if (hostname) { + *host = hostname; + *port = p; + } + proxy_info("Cluster: detected peer %s:%d with mysql_servers version %llu, epoch %llu\n", hostname, p, version, epoch); +} + +void ProxySQL_Cluster_Nodes::get_peer_to_sync_mysql_users(char **host, uint16_t *port) { + unsigned long long version = 0; + unsigned long long epoch = 0; + char *hostname = NULL; + uint16_t p = 0; +// pthread_mutex_lock(&mutex); + //unsigned long long curtime = monotonic_time(); + for( std::unordered_map::iterator it = umap_proxy_nodes.begin(); it != umap_proxy_nodes.end(); ) { + ProxySQL_Node_Entry * node = it->second; + ProxySQL_Checksum_Value_2 * v = &node->checksums_values.mysql_users; + if (v->version > 1) { + if ( v->epoch > epoch && v->diff_check > 3) { + epoch = v->epoch; + version = v->version; + if (hostname) { + free(hostname); + } + hostname=strdup(node->get_hostname()); + p = node->get_port(); + } + } + it++; + } +// pthread_mutex_unlock(&mutex); + if (hostname) { + *host = hostname; + *port = p; + } + proxy_info("Cluster: detected peer %s:%d with mysql_users version %llu, epoch %llu\n", hostname, p, version, epoch); +} + +void ProxySQL_Cluster_Nodes::get_peer_to_sync_proxysql_servers(char **host, uint16_t *port) { + unsigned long long version = 0; + unsigned long long epoch = 0; + char *hostname = NULL; + uint16_t p = 0; +// pthread_mutex_lock(&mutex); + //unsigned long long curtime = monotonic_time(); + for( std::unordered_map::iterator it = umap_proxy_nodes.begin(); it != umap_proxy_nodes.end(); ) { + ProxySQL_Node_Entry * node = it->second; + ProxySQL_Checksum_Value_2 * v = &node->checksums_values.proxysql_servers; + if (v->version > 1) { + if ( v->epoch > epoch && v->diff_check > 3) { + epoch = v->epoch; + version = v->version; + if (hostname) { + free(hostname); + } + hostname=strdup(node->get_hostname()); + p = node->get_port(); + } + } + it++; + } +// pthread_mutex_unlock(&mutex); + if (hostname) { + *host = hostname; + *port = p; + } + proxy_info("Cluster: detected peer %s:%d with proxysql_servers version %llu, epoch %llu\n", hostname, p, version, epoch); +} + SQLite3_result * ProxySQL_Cluster_Nodes::stats_proxysql_servers_checksums() { const int colnum=9; SQLite3_result *result=new SQLite3_result(colnum); @@ -746,10 +1367,22 @@ SQLite3_result * ProxySQL_Cluster_Nodes::dump_table_proxysql_servers() { ProxySQL_Cluster::ProxySQL_Cluster() { pthread_mutex_init(&mutex,NULL); + pthread_mutex_init(&update_mysql_query_rules_mutex,NULL); + pthread_mutex_init(&update_mysql_servers_mutex,NULL); + pthread_mutex_init(&update_mysql_users_mutex,NULL); + pthread_mutex_init(&update_proxysql_servers_mutex,NULL); cluster_username = strdup((char *)""); cluster_password = strdup((char *)""); cluster_check_interval_ms = 1000; cluster_check_status_frequency = 10; + cluster_mysql_query_rules_diffs_before_sync = 3; + cluster_mysql_servers_diffs_before_sync = 3; + cluster_mysql_users_diffs_before_sync = 3; + cluster_proxysql_servers_diffs_before_sync = 3; + cluster_mysql_query_rules_save_to_disk = true; + cluster_mysql_servers_save_to_disk = true; + cluster_mysql_users_save_to_disk = true; + cluster_proxysql_servers_save_to_disk = true; } ProxySQL_Cluster::~ProxySQL_Cluster() { diff --git a/src/main.cpp b/src/main.cpp index 300d5d712..9f8852402 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -569,6 +569,7 @@ void ProxySQL_Main_init_phase3___start_all() { { cpu_timer t; GloAdmin->init_mysql_servers(); + GloAdmin->init_proxysql_servers(); GloAdmin->load_scheduler_to_runtime(); #ifdef DEBUG std::cerr << "Main phase3 : GloAdmin initialized in ";