From 8f53bed48e80ce55a66a5790a21ef909a6d5a775 Mon Sep 17 00:00:00 2001 From: Miro Stauder Date: Wed, 3 Apr 2024 10:13:58 +0000 Subject: [PATCH 01/79] use 'docker compose' instead of 'docker-compose' --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 78859c20cb..a067414c08 100644 --- a/Makefile +++ b/Makefile @@ -341,9 +341,9 @@ build-%: .NOTPARALLEL: binaries/proxysql% binaries/proxysql%: - @docker-compose -p proxysql down -v --remove-orphans - @docker-compose -p proxysql up $(IMG_NAME)$(IMG_TYPE)$(IMG_COMP)_build - @docker-compose -p proxysql down -v --remove-orphans + @docker compose -p proxysql down -v --remove-orphans + @docker compose -p proxysql up $(IMG_NAME)$(IMG_TYPE)$(IMG_COMP)_build + @docker compose -p proxysql down -v --remove-orphans ### clean targets From ecff8d0cc2232761c279973a1cd1e8e31a2bb765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Wed, 10 Apr 2024 12:01:58 +0200 Subject: [PATCH 02/79] Fix two memory leaks on SQLite3 prepared statements The leaks are exercised during table regeneration at 'MySQL_HostGroups_Manager::commit'. --- lib/MySQL_HostGroups_Manager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/MySQL_HostGroups_Manager.cpp b/lib/MySQL_HostGroups_Manager.cpp index b185416af8..d1f6188ec4 100644 --- a/lib/MySQL_HostGroups_Manager.cpp +++ b/lib/MySQL_HostGroups_Manager.cpp @@ -7663,6 +7663,7 @@ void MySQL_HostGroups_Manager::generate_mysql_hostgroup_attributes_table() { } } + (*proxy_sqlite3_finalize)(statement); delete incoming_hostgroup_attributes; incoming_hostgroup_attributes=NULL; } @@ -7717,6 +7718,7 @@ void MySQL_HostGroups_Manager::generate_mysql_servers_ssl_params_table() { string MapKey = MSSP.getMapKey(rand_del); Servers_SSL_Params_map.emplace(MapKey, MSSP); } + (*proxy_sqlite3_finalize)(statement); delete incoming_mysql_servers_ssl_params; incoming_mysql_servers_ssl_params=NULL; } From 288eee140541e4c053489b6ca0b23ee3472a98a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Wed, 10 Apr 2024 12:19:17 +0200 Subject: [PATCH 03/79] Add 'prof_accum' to jemalloc defaults Activate cumulative object/byte counts in profile dumps. This mode increments memory consumption, but it also increases the chances of detection of small and consistent leaks. It also enables extra accumulative memory metrics, e.g: jeprof 'alloc_space'. --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index aaac09822c..700272f29a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -409,7 +409,7 @@ static volatile int load_; //#else //const char *malloc_conf = "xmalloc:true,lg_tcache_max:16,purge:decay"; #ifndef __FreeBSD__ -const char *malloc_conf = "xmalloc:true,lg_tcache_max:16,prof:true,prof_leak:true,lg_prof_sample:20,lg_prof_interval:30,prof_active:false"; +const char *malloc_conf = "xmalloc:true,lg_tcache_max:16,prof:true,prof_accum:true,prof_leak:true,lg_prof_sample:20,lg_prof_interval:30,prof_active:false"; #endif //#endif /* DEBUG */ //const char *malloc_conf = "prof_leak:true,lg_prof_sample:0,prof_final:true,xmalloc:true,lg_tcache_max:16"; From 0a38bd435d5e3ba27b9492200d5fc066d83e9490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Wed, 10 Apr 2024 15:55:39 +0200 Subject: [PATCH 04/79] Print jemalloc default config on startup Since jemalloc config can be changed via 'MALLOC_CONF' env variable, printing on startup ensures that we can check in the error log if the default config has been overridden. --- src/Makefile | 9 ++++++++- src/main.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 0d2e577cf2..40480140b9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -149,11 +149,18 @@ ifeq ($(TEST_WITHASAN),1) WASAN += -DTEST_WITHASAN endif +NOJEMALLOC := $(shell echo $(NOJEMALLOC)) +ifeq ($(NOJEMALLOC),1) +NOJEM=-DNOJEM +else +NOJEM= +endif + MYCXXFLAGS := $(STDCPP) ifeq ($(CXX),clang++) MYCXXFLAGS += -fuse-ld=lld endif -MYCXXFLAGS += $(IDIRS) $(OPTZ) $(DEBUG) $(PSQLCH) -DGITVERSION=\"$(GIT_VERSION)\" $(WGCOV) $(WASAN) +MYCXXFLAGS += $(IDIRS) $(OPTZ) $(DEBUG) $(PSQLCH) -DGITVERSION=\"$(GIT_VERSION)\" $(NOJEM) $(WGCOV) $(WASAN) STATICMYLIBS := -Wl,-Bstatic -lconfig -lproxysql -ldaemon -lconfig++ -lre2 -lpcrecpp -lpcre -lmariadbclient -lhttpserver -lmicrohttpd -linjection -lcurl -lssl -lcrypto -lev diff --git a/src/main.cpp b/src/main.cpp index 700272f29a..34f6f12e79 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1902,7 +1902,61 @@ void handleProcessRestart() { } while (pid > 0); } +#ifndef NOJEM +int print_jemalloc_conf() { + int rc = 0; + + bool xmalloc = 0; + bool prof_accum = 0; + bool prof_leak = 0; + + size_t lg_cache_max = 0; + size_t lg_prof_sample = 0; + size_t lg_prof_interval = 0; + + size_t bool_sz = sizeof(bool); + size_t size_sz = sizeof(size_t); + size_t ssize_sz = sizeof(ssize_t); + + rc = mallctl("config.xmalloc", &xmalloc, &bool_sz, NULL, 0); + if (rc) { proxy_error("Failed to fetch 'config.xmalloc' with error %d", rc); return rc; } + + rc = mallctl("opt.lg_tcache_max", &lg_cache_max, &size_sz, NULL, 0); + if (rc) { proxy_error("Failed to fetch 'opt.lg_tcache_max' with error %d", rc); return rc; } + + rc = mallctl("opt.prof_accum", &prof_accum, &bool_sz, NULL, 0); + if (rc) { proxy_error("Failed to fetch 'opt.prof_accum' with error %d", rc); return rc; } + + rc = mallctl("opt.prof_leak", &prof_leak, &bool_sz, NULL, 0); + if (rc) { proxy_error("Failed to fetch 'opt.prof_leak' with error %d", rc); return rc; } + + rc = mallctl("opt.lg_prof_sample", &lg_prof_sample, &size_sz, NULL, 0); + if (rc) { proxy_error("Failed to fetch 'opt.lg_prof_sample' with error %d", rc); return rc; } + + rc = mallctl("opt.lg_prof_interval", &lg_prof_interval, &ssize_sz, NULL, 0); + if (rc) { proxy_error("Failed to fetch 'opt.lg_prof_interval' with error %d", rc); return rc; } + + proxy_info( + "Using jemalloc with MALLOC_CONF:" + " config.xmalloc:%d, lg_tcache_max:%lu, opt.prof_accum:%d, opt.prof_leak:%d," + " opt.lg_prof_sample:%lu, opt.lg_prof_interval:%lu, rc:%d\n", + xmalloc, lg_cache_max, prof_accum, prof_leak, lg_prof_sample, lg_prof_interval, rc + ); + + return 0; +} +#else +int print_jemalloc_conf() { + return 0; +} +#endif + int main(int argc, const char * argv[]) { + // Output current jemalloc conf; no action taken when disabled + { + int rc = print_jemalloc_conf(); + if (rc) { exit(EXIT_FAILURE); } + } { MYSQL *my = mysql_init(NULL); From 9894621db1938f462490bf1480b83a9c2b3bfd75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Wed, 10 Apr 2024 16:00:40 +0200 Subject: [PATCH 05/79] Fix GCC type mismatch warning on 'proxy_info' --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 34f6f12e79..0e1335d04d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1859,7 +1859,7 @@ void handleProcessRestart() { // Calculate wait time using exponential backoff int waitTime = 1 << restartAttempts; parent_open_error_log(); - proxy_info("ProxySQL exited after only %d seconds , below the %d seconds threshold. Restarting attempt %d\n", elapsed_seconds, EXECUTION_THRESHOLD, restartAttempts); + proxy_info("ProxySQL exited after only %ld seconds , below the %d seconds threshold. Restarting attempt %d\n", elapsed_seconds, EXECUTION_THRESHOLD, restartAttempts); proxy_info("Angel process is waiting %d seconds before starting a new ProxySQL process\n", waitTime); parent_close_error_log(); From 0a2cd03672fbed028d29438b86de17cc3ce3d89b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Sun, 21 Apr 2024 11:37:37 +0000 Subject: [PATCH 06/79] Refactoring to split Query_Processor.cpp into multiple files --- include/QP_rule_text.h | 22 +++ include/proxysql_admin.h | 2 + include/proxysql_typedefs.h | 5 + include/query_processor.h | 4 +- lib/Makefile | 1 + lib/QP_query_digest_stats.cpp | 189 +++++++++++++++++++++++ lib/QP_rule_text.cpp | 99 ++++++++++++ lib/Query_Processor.cpp | 278 +--------------------------------- 8 files changed, 322 insertions(+), 278 deletions(-) create mode 100644 include/QP_rule_text.h create mode 100644 include/proxysql_typedefs.h create mode 100644 lib/QP_query_digest_stats.cpp create mode 100644 lib/QP_rule_text.cpp diff --git a/include/QP_rule_text.h b/include/QP_rule_text.h new file mode 100644 index 0000000000..9cbd7e7368 --- /dev/null +++ b/include/QP_rule_text.h @@ -0,0 +1,22 @@ +#ifndef CLASS_QR_RULE_H +#define CLASS_QR_RULE_H + +#define QP_RE_MOD_CASELESS 1 +#define QP_RE_MOD_GLOBAL 2 + +class QP_rule_text_hitsonly { + public: + char **pta; + QP_rule_text_hitsonly(QP_rule_t *QPr); + ~QP_rule_text_hitsonly(); +}; + +class QP_rule_text { + public: + char **pta; + int num_fields; + QP_rule_text(QP_rule_t *QPr); + ~QP_rule_text(); +}; + +#endif // CLASS_QR_RULE_H diff --git a/include/proxysql_admin.h b/include/proxysql_admin.h index 6906091cc7..c56d68bbb6 100644 --- a/include/proxysql_admin.h +++ b/include/proxysql_admin.h @@ -15,6 +15,8 @@ #include "ProxySQL_RESTAPI_Server.hpp" +#include "proxysql_typedefs.h" + typedef struct { uint32_t hash; uint32_t key; } t_symstruct; class ProxySQL_Config; class ProxySQL_Restapi; diff --git a/include/proxysql_typedefs.h b/include/proxysql_typedefs.h new file mode 100644 index 0000000000..f88af0def8 --- /dev/null +++ b/include/proxysql_typedefs.h @@ -0,0 +1,5 @@ +#ifndef PROXYSQL_COMMON_TYPEDEF +#define PROXYSQL_COMMON_TYPEDEF +typedef std::unordered_map umap_query_digest; +typedef std::unordered_map umap_query_digest_text; +#endif // PROXYSQL_COMMON_TYPEDEF diff --git a/include/query_processor.h b/include/query_processor.h index c9dd826a3d..3d7d8d1561 100644 --- a/include/query_processor.h +++ b/include/query_processor.h @@ -16,9 +16,7 @@ #include "khash.h" KHASH_MAP_INIT_STR(khStrInt, int) -typedef std::unordered_map umap_query_digest; -typedef std::unordered_map umap_query_digest_text; - +#include "proxysql_typedefs.h" #define WUS_NOT_FOUND 0 // couldn't find any filter #define WUS_OFF 1 // allow the query diff --git a/lib/Makefile b/lib/Makefile index b94dd2fc65..6cf7396ec5 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -127,6 +127,7 @@ default: libproxysql.a _OBJ_CXX := ProxySQL_GloVars.oo network.oo debug.oo configfile.oo Query_Cache.oo SpookyV2.oo MySQL_Authentication.oo gen_utils.oo sqlite3db.oo mysql_connection.oo MySQL_HostGroups_Manager.oo mysql_data_stream.oo MySQL_Thread.oo MySQL_Session.oo MySQL_Protocol.oo mysql_backend.oo Query_Processor.oo ProxySQL_Admin.oo ProxySQL_Config.oo ProxySQL_Restapi.oo MySQL_Monitor.oo MySQL_Logger.oo thread.oo MySQL_PreparedStatement.oo ProxySQL_Cluster.oo ClickHouse_Authentication.oo ClickHouse_Server.oo ProxySQL_Statistics.oo Chart_bundle_js.oo ProxySQL_HTTP_Server.oo ProxySQL_RESTAPI_Server.oo font-awesome.min.css.oo main-bundle.min.css.oo set_parser.oo MySQL_Variables.oo c_tokenizer.oo proxysql_utils.oo proxysql_coredump.oo proxysql_sslkeylog.oo \ sha256crypt.oo \ + QP_rule_text.oo QP_query_digest_stats.oo \ proxysql_find_charset.oo ProxySQL_Poll.oo OBJ_CXX := $(patsubst %,$(ODIR)/%,$(_OBJ_CXX)) HEADERS := ../include/*.h ../include/*.hpp diff --git a/lib/QP_query_digest_stats.cpp b/lib/QP_query_digest_stats.cpp new file mode 100644 index 0000000000..509951b506 --- /dev/null +++ b/lib/QP_query_digest_stats.cpp @@ -0,0 +1,189 @@ +#include "query_processor.h" + +// reverse: reverse string s in place +static void reverse(char s[]) { + int i, j; + char c; + int l = strlen(s); + for (i = 0, j = l-1; i 0); /* delete it */ + s[i] = '\0'; + reverse(s); +} + + +QP_query_digest_stats::QP_query_digest_stats(char *u, char *s, uint64_t d, char *dt, int h, char *ca) { + digest=d; + digest_text=NULL; + if (dt) { + digest_text=strndup(dt, mysql_thread___query_digests_max_digest_length); + } + if (strlen(u) < sizeof(username_buf)) { + strcpy(username_buf,u); + username = username_buf; + } else { + username=strdup(u); + } + if (strlen(s) < sizeof(schemaname_buf)) { + strcpy(schemaname_buf,s); + schemaname = schemaname_buf; + } else { + schemaname=strdup(s); + } + if (strlen(ca) < sizeof(client_address_buf)) { + strcpy(client_address_buf,ca); + client_address = client_address_buf; + } else { + client_address=strdup(ca); + } + count_star=0; + first_seen=0; + last_seen=0; + sum_time=0; + min_time=0; + max_time=0; + rows_affected=0; + rows_sent=0; + hid=h; +} +void QP_query_digest_stats::add_time( + unsigned long long t, unsigned long long n, unsigned long long ra, unsigned long long rs, + unsigned long long cnt +) { + count_star += cnt; + sum_time+=t; + rows_affected+=ra; + rows_sent+=rs; + if (t < min_time || min_time==0) { + if (t) min_time = t; + } + if (t > max_time) { + max_time = t; + } + if (first_seen==0) { + first_seen=n; + } + last_seen=n; +} +QP_query_digest_stats::~QP_query_digest_stats() { + if (digest_text) { + free(digest_text); + digest_text=NULL; + } + if (username) { + if (username == username_buf) { + } else { + free(username); + } + username=NULL; + } + if (schemaname) { + if (schemaname == schemaname_buf) { + } else { + free(schemaname); + } + schemaname=NULL; + } + if (client_address) { + if (client_address == client_address_buf) { + } else { + free(client_address); + } + client_address=NULL; + } +} + +// Funtion to get the digest text associated to a QP_query_digest_stats. +// QP_query_digest_stats member type "char *digest_text" may by NULL, so we +// have to get the digest text from "digest_text_umap". +char *QP_query_digest_stats::get_digest_text(const umap_query_digest_text *digest_text_umap) { + char *digest_text_str = NULL; + + if (digest_text) { + digest_text_str = digest_text; + } else { + std::unordered_map::const_iterator it; + it = digest_text_umap->find(digest); + if (it != digest_text_umap->end()) { + digest_text_str = it->second; + } else { + // LCOV_EXCL_START + assert(0); + // LCOV_EXCL_STOP + } + } + + return digest_text_str; +} + +char **QP_query_digest_stats::get_row(umap_query_digest_text *digest_text_umap, query_digest_stats_pointers_t *qdsp) { + char **pta=qdsp->pta; + + assert(schemaname); + pta[0]=schemaname; + assert(username); + pta[1]=username; + assert(client_address); + pta[2]=client_address; + + assert(qdsp != NULL); + assert(qdsp->digest); + sprintf(qdsp->digest,"0x%016llX", (long long unsigned int)digest); + pta[3]=qdsp->digest; + + pta[4] = get_digest_text(digest_text_umap); + + //sprintf(qdsp->count_star,"%u",count_star); + my_itoa(qdsp->count_star, count_star); + pta[5]=qdsp->count_star; + + time_t __now; + time(&__now); + unsigned long long curtime=monotonic_time(); + time_t seen_time; + seen_time= __now - curtime/1000000 + first_seen/1000000; + //sprintf(qdsp->first_seen,"%ld", seen_time); + my_itoa(qdsp->first_seen, seen_time); + pta[6]=qdsp->first_seen; + + seen_time= __now - curtime/1000000 + last_seen/1000000; + //sprintf(qdsp->last_seen,"%ld", seen_time); + my_itoa(qdsp->last_seen, seen_time); + pta[7]=qdsp->last_seen; + //sprintf(qdsp->sum_time,"%llu",sum_time); + my_itoa(qdsp->sum_time,sum_time); + pta[8]=qdsp->sum_time; + //sprintf(qdsp->min_time,"%llu",min_time); + my_itoa(qdsp->min_time,min_time); + pta[9]=qdsp->min_time; + //sprintf(qdsp->max_time,"%llu",max_time); + my_itoa(qdsp->max_time,max_time); + pta[10]=qdsp->max_time; + // we are reverting this back to the use of sprintf instead of my_itoa + // because with my_itoa we are losing the sign + // see issue #2285 + sprintf(qdsp->hid,"%d",hid); + //my_itoa(qdsp->hid,hid); + pta[11]=qdsp->hid; + //sprintf(qdsp->rows_affected,"%llu",rows_affected); + my_itoa(qdsp->rows_affected,rows_affected); + pta[12]=qdsp->rows_affected; + //sprintf(qdsp->rows_sent,"%llu",rows_sent); + my_itoa(qdsp->rows_sent,rows_sent); + pta[13]=qdsp->rows_sent; + return pta; +} + diff --git a/lib/QP_rule_text.cpp b/lib/QP_rule_text.cpp new file mode 100644 index 0000000000..cd1a340425 --- /dev/null +++ b/lib/QP_rule_text.cpp @@ -0,0 +1,99 @@ +/* +#include // std::cout +#include // std::sort +#include // std::vector +#include "re2/re2.h" +#include "re2/regexp.h" +#include "proxysql.h" +#include "cpp.h" + +#include "MySQL_PreparedStatement.h" +#include "MySQL_Data_Stream.h" +*/ +#include "query_processor.h" +#include +#include "proxysql_macros.h" + +#include "QP_rule_text.h" + + +QP_rule_text_hitsonly::QP_rule_text_hitsonly(QP_rule_t *QPr) { + pta=NULL; + pta=(char **)malloc(sizeof(char *)*2); + itostr(pta[0], (long long)QPr->rule_id); + itostr(pta[1], (long long)QPr->hits); +} + +QP_rule_text_hitsonly::~QP_rule_text_hitsonly() { + for(int i=0; i<2; i++) { + free_null(pta[i]); + } + free(pta); +} + +QP_rule_text::QP_rule_text(QP_rule_t *QPr) { + num_fields=36; // this count the number of fields + pta=NULL; + pta=(char **)malloc(sizeof(char *)*num_fields); + itostr(pta[0], (long long)QPr->rule_id); + itostr(pta[1], (long long)QPr->active); + pta[2]=strdup_null(QPr->username); + pta[3]=strdup_null(QPr->schemaname); + itostr(pta[4], (long long)QPr->flagIN); + + pta[5]=strdup_null(QPr->client_addr); + pta[6]=strdup_null(QPr->proxy_addr); + itostr(pta[7], (long long)QPr->proxy_port); + + char buf[20]; + if (QPr->digest) { + sprintf(buf,"0x%016llX", (long long unsigned int)QPr->digest); + pta[8]=strdup(buf); + } else { + pta[8]=NULL; + } + + pta[9]=strdup_null(QPr->match_digest); + pta[10]=strdup_null(QPr->match_pattern); + itostr(pta[11], (long long)QPr->negate_match_pattern); + std::string re_mod; + re_mod=""; + if ((QPr->re_modifiers & QP_RE_MOD_CASELESS) == QP_RE_MOD_CASELESS) re_mod = "CASELESS"; + if ((QPr->re_modifiers & QP_RE_MOD_GLOBAL) == QP_RE_MOD_GLOBAL) { + if (re_mod.length()) { + re_mod = re_mod + ","; + } + re_mod = re_mod + "GLOBAL"; + } + pta[12]=strdup_null((char *)re_mod.c_str()); // re_modifiers + itostr(pta[13], (long long)QPr->flagOUT); + pta[14]=strdup_null(QPr->replace_pattern); + itostr(pta[15], (long long)QPr->destination_hostgroup); + itostr(pta[16], (long long)QPr->cache_ttl); + itostr(pta[17], (long long)QPr->cache_empty_result); + itostr(pta[18], (long long)QPr->cache_timeout); + itostr(pta[19], (long long)QPr->reconnect); + itostr(pta[20], (long long)QPr->timeout); + itostr(pta[21], (long long)QPr->retries); + itostr(pta[22], (long long)QPr->delay); + itostr(pta[23], (long long)QPr->next_query_flagIN); + itostr(pta[24], (long long)QPr->mirror_flagOUT); + itostr(pta[25], (long long)QPr->mirror_hostgroup); + pta[26]=strdup_null(QPr->error_msg); + pta[27]=strdup_null(QPr->OK_msg); + itostr(pta[28], (long long)QPr->sticky_conn); + itostr(pta[29], (long long)QPr->multiplex); + itostr(pta[30], (long long)QPr->gtid_from_hostgroup); + itostr(pta[31], (long long)QPr->log); + itostr(pta[32], (long long)QPr->apply); + pta[33]=strdup_null(QPr->attributes); + pta[34]=strdup_null(QPr->comment); // issue #643 + itostr(pta[35], (long long)QPr->hits); +} + +QP_rule_text::~QP_rule_text() { + for(int i=0; i #include + +#include "QP_rule_text.h" + extern MySQL_Threads_Handler *GloMTH; extern ProxySQL_Admin *GloAdmin; @@ -41,279 +42,6 @@ static int int_cmp(const void *a, const void *b) { return 0; } -class QP_rule_text_hitsonly { - public: - char **pta; - QP_rule_text_hitsonly(QP_rule_t *QPr) { - pta=NULL; - pta=(char **)malloc(sizeof(char *)*2); - itostr(pta[0], (long long)QPr->rule_id); - itostr(pta[1], (long long)QPr->hits); - } - ~QP_rule_text_hitsonly() { - for(int i=0; i<2; i++) { - free_null(pta[i]); - } - free(pta); - } -}; - -class QP_rule_text { - public: - char **pta; - int num_fields; - QP_rule_text(QP_rule_t *QPr) { - num_fields=36; // this count the number of fields - pta=NULL; - pta=(char **)malloc(sizeof(char *)*num_fields); - itostr(pta[0], (long long)QPr->rule_id); - itostr(pta[1], (long long)QPr->active); - pta[2]=strdup_null(QPr->username); - pta[3]=strdup_null(QPr->schemaname); - itostr(pta[4], (long long)QPr->flagIN); - - pta[5]=strdup_null(QPr->client_addr); - pta[6]=strdup_null(QPr->proxy_addr); - itostr(pta[7], (long long)QPr->proxy_port); - - char buf[20]; - if (QPr->digest) { - sprintf(buf,"0x%016llX", (long long unsigned int)QPr->digest); - pta[8]=strdup(buf); - } else { - pta[8]=NULL; - } - - pta[9]=strdup_null(QPr->match_digest); - pta[10]=strdup_null(QPr->match_pattern); - itostr(pta[11], (long long)QPr->negate_match_pattern); - std::string re_mod; - re_mod=""; - if ((QPr->re_modifiers & QP_RE_MOD_CASELESS) == QP_RE_MOD_CASELESS) re_mod = "CASELESS"; - if ((QPr->re_modifiers & QP_RE_MOD_GLOBAL) == QP_RE_MOD_GLOBAL) { - if (re_mod.length()) { - re_mod = re_mod + ","; - } - re_mod = re_mod + "GLOBAL"; - } - pta[12]=strdup_null((char *)re_mod.c_str()); // re_modifiers - itostr(pta[13], (long long)QPr->flagOUT); - pta[14]=strdup_null(QPr->replace_pattern); - itostr(pta[15], (long long)QPr->destination_hostgroup); - itostr(pta[16], (long long)QPr->cache_ttl); - itostr(pta[17], (long long)QPr->cache_empty_result); - itostr(pta[18], (long long)QPr->cache_timeout); - itostr(pta[19], (long long)QPr->reconnect); - itostr(pta[20], (long long)QPr->timeout); - itostr(pta[21], (long long)QPr->retries); - itostr(pta[22], (long long)QPr->delay); - itostr(pta[23], (long long)QPr->next_query_flagIN); - itostr(pta[24], (long long)QPr->mirror_flagOUT); - itostr(pta[25], (long long)QPr->mirror_hostgroup); - pta[26]=strdup_null(QPr->error_msg); - pta[27]=strdup_null(QPr->OK_msg); - itostr(pta[28], (long long)QPr->sticky_conn); - itostr(pta[29], (long long)QPr->multiplex); - itostr(pta[30], (long long)QPr->gtid_from_hostgroup); - itostr(pta[31], (long long)QPr->log); - itostr(pta[32], (long long)QPr->apply); - pta[33]=strdup_null(QPr->attributes); - pta[34]=strdup_null(QPr->comment); // issue #643 - itostr(pta[35], (long long)QPr->hits); - } - ~QP_rule_text() { - for(int i=0; i 0); /* delete it */ - s[i] = '\0'; - reverse(s); -} - -QP_query_digest_stats::QP_query_digest_stats(char *u, char *s, uint64_t d, char *dt, int h, char *ca) { - digest=d; - digest_text=NULL; - if (dt) { - digest_text=strndup(dt, mysql_thread___query_digests_max_digest_length); - } - if (strlen(u) < sizeof(username_buf)) { - strcpy(username_buf,u); - username = username_buf; - } else { - username=strdup(u); - } - if (strlen(s) < sizeof(schemaname_buf)) { - strcpy(schemaname_buf,s); - schemaname = schemaname_buf; - } else { - schemaname=strdup(s); - } - if (strlen(ca) < sizeof(client_address_buf)) { - strcpy(client_address_buf,ca); - client_address = client_address_buf; - } else { - client_address=strdup(ca); - } - count_star=0; - first_seen=0; - last_seen=0; - sum_time=0; - min_time=0; - max_time=0; - rows_affected=0; - rows_sent=0; - hid=h; -} -void QP_query_digest_stats::add_time( - unsigned long long t, unsigned long long n, unsigned long long ra, unsigned long long rs, - unsigned long long cnt -) { - count_star += cnt; - sum_time+=t; - rows_affected+=ra; - rows_sent+=rs; - if (t < min_time || min_time==0) { - if (t) min_time = t; - } - if (t > max_time) { - max_time = t; - } - if (first_seen==0) { - first_seen=n; - } - last_seen=n; -} -QP_query_digest_stats::~QP_query_digest_stats() { - if (digest_text) { - free(digest_text); - digest_text=NULL; - } - if (username) { - if (username == username_buf) { - } else { - free(username); - } - username=NULL; - } - if (schemaname) { - if (schemaname == schemaname_buf) { - } else { - free(schemaname); - } - schemaname=NULL; - } - if (client_address) { - if (client_address == client_address_buf) { - } else { - free(client_address); - } - client_address=NULL; - } -} - -// Funtion to get the digest text associated to a QP_query_digest_stats. -// QP_query_digest_stats member type "char *digest_text" may by NULL, so we -// have to get the digest text from "digest_text_umap". -char *QP_query_digest_stats::get_digest_text(const umap_query_digest_text *digest_text_umap) { - char *digest_text_str = NULL; - - if (digest_text) { - digest_text_str = digest_text; - } else { - std::unordered_map::const_iterator it; - it = digest_text_umap->find(digest); - if (it != digest_text_umap->end()) { - digest_text_str = it->second; - } else { - // LCOV_EXCL_START - assert(0); - // LCOV_EXCL_STOP - } - } - - return digest_text_str; -} - -char **QP_query_digest_stats::get_row(umap_query_digest_text *digest_text_umap, query_digest_stats_pointers_t *qdsp) { - char **pta=qdsp->pta; - - assert(schemaname); - pta[0]=schemaname; - assert(username); - pta[1]=username; - assert(client_address); - pta[2]=client_address; - - assert(qdsp != NULL); - assert(qdsp->digest); - sprintf(qdsp->digest,"0x%016llX", (long long unsigned int)digest); - pta[3]=qdsp->digest; - - pta[4] = get_digest_text(digest_text_umap); - - //sprintf(qdsp->count_star,"%u",count_star); - my_itoa(qdsp->count_star, count_star); - pta[5]=qdsp->count_star; - - time_t __now; - time(&__now); - unsigned long long curtime=monotonic_time(); - time_t seen_time; - seen_time= __now - curtime/1000000 + first_seen/1000000; - //sprintf(qdsp->first_seen,"%ld", seen_time); - my_itoa(qdsp->first_seen, seen_time); - pta[6]=qdsp->first_seen; - - seen_time= __now - curtime/1000000 + last_seen/1000000; - //sprintf(qdsp->last_seen,"%ld", seen_time); - my_itoa(qdsp->last_seen, seen_time); - pta[7]=qdsp->last_seen; - //sprintf(qdsp->sum_time,"%llu",sum_time); - my_itoa(qdsp->sum_time,sum_time); - pta[8]=qdsp->sum_time; - //sprintf(qdsp->min_time,"%llu",min_time); - my_itoa(qdsp->min_time,min_time); - pta[9]=qdsp->min_time; - //sprintf(qdsp->max_time,"%llu",max_time); - my_itoa(qdsp->max_time,max_time); - pta[10]=qdsp->max_time; - // we are reverting this back to the use of sprintf instead of my_itoa - // because with my_itoa we are losing the sign - // see issue #2285 - sprintf(qdsp->hid,"%d",hid); - //my_itoa(qdsp->hid,hid); - pta[11]=qdsp->hid; - //sprintf(qdsp->rows_affected,"%llu",rows_affected); - my_itoa(qdsp->rows_affected,rows_affected); - pta[12]=qdsp->rows_affected; - //sprintf(qdsp->rows_sent,"%llu",rows_sent); - my_itoa(qdsp->rows_sent,rows_sent); - pta[13]=qdsp->rows_sent; - return pta; -} struct __RE2_objects_t { pcrecpp::RE_Options *opt1; From e7aa5ff4c5392e93327e963e91a3fcae516384bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Sun, 21 Apr 2024 13:31:06 +0000 Subject: [PATCH 07/79] Split of MySQL_HostGroups_Manager.cpp into multiple files First commit to split MySQL_HostGroups_Manager.cpp into multiple files. More to split. --- include/GTID_Server_Data.h | 27 + include/MySQL_HostGroups_Manager.h | 5 +- lib/GTID_Server_Data.cpp | 469 ++++++++++ lib/Makefile | 1 + lib/MyHGC.cpp | 384 +++++++++ lib/MySQL_HostGroups_Manager.cpp | 1272 +--------------------------- lib/MySrvC.cpp | 198 +++++ lib/MySrvConnList.cpp | 256 ++++++ lib/MySrvList.cpp | 44 + 9 files changed, 1385 insertions(+), 1271 deletions(-) create mode 100644 include/GTID_Server_Data.h create mode 100644 lib/GTID_Server_Data.cpp create mode 100644 lib/MyHGC.cpp create mode 100644 lib/MySrvC.cpp create mode 100644 lib/MySrvConnList.cpp create mode 100644 lib/MySrvList.cpp diff --git a/include/GTID_Server_Data.h b/include/GTID_Server_Data.h new file mode 100644 index 0000000000..9bc9219fda --- /dev/null +++ b/include/GTID_Server_Data.h @@ -0,0 +1,27 @@ +#ifndef CLASS_GTID_Server_Data_H +#define CLASS_GTID_Server_Data_H +class GTID_Server_Data { + public: + char *address; + uint16_t port; + uint16_t mysql_port; + char *data; + size_t len; + size_t size; + size_t pos; + struct ev_io *w; + char uuid_server[64]; + unsigned long long events_read; + gtid_set_t gtid_executed; + bool active; + GTID_Server_Data(struct ev_io *_w, char *_address, uint16_t _port, uint16_t _mysql_port); + void resize(size_t _s); + ~GTID_Server_Data(); + bool readall(); + bool writeout(); + bool read_next_gtid(); + bool gtid_exists(char *gtid_uuid, uint64_t gtid_trxid); + void read_all_gtids(); + void dump(); +}; +#endif // CLASS_GTID_Server_Data_H diff --git a/include/MySQL_HostGroups_Manager.h b/include/MySQL_HostGroups_Manager.h index f01a030ee2..eaa71a293c 100644 --- a/include/MySQL_HostGroups_Manager.h +++ b/include/MySQL_HostGroups_Manager.h @@ -130,6 +130,9 @@ class MyHGC; std::string gtid_executed_to_string(gtid_set_t& gtid_executed); void addGtid(const gtid_t& gtid, gtid_set_t& gtid_executed); +#include "GTID_Server_Data.h" + +/* class GTID_Server_Data { public: char *address; @@ -154,7 +157,7 @@ class GTID_Server_Data { void read_all_gtids(); void dump(); }; - +*/ class MySrvConnList { diff --git a/lib/GTID_Server_Data.cpp b/lib/GTID_Server_Data.cpp new file mode 100644 index 0000000000..6dbf572354 --- /dev/null +++ b/lib/GTID_Server_Data.cpp @@ -0,0 +1,469 @@ +#include "MySQL_HostGroups_Manager.h" + +#include "ev.h" +#include + + +extern ProxySQL_Admin *GloAdmin; + +extern MySQL_Threads_Handler *GloMTH; + +extern MySQL_Monitor *GloMyMon; + +static pthread_mutex_t ev_loop_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void gtid_async_cb(struct ev_loop *loop, struct ev_async *watcher, int revents) { + if (glovars.shutdown) { + ev_break(loop); + } + pthread_mutex_lock(&ev_loop_mutex); + MyHGM->gtid_missing_nodes = false; + MyHGM->generate_mysql_gtid_executed_tables(); + pthread_mutex_unlock(&ev_loop_mutex); + return; +} + +static void gtid_timer_cb (struct ev_loop *loop, struct ev_timer *timer, int revents) { + if (GloMTH == nullptr) { return; } + ev_timer_stop(loop, timer); + ev_timer_set(timer, __sync_add_and_fetch(&GloMTH->variables.binlog_reader_connect_retry_msec,0)/1000, 0); + if (glovars.shutdown) { + ev_break(loop); + } + if (MyHGM->gtid_missing_nodes) { + pthread_mutex_lock(&ev_loop_mutex); + MyHGM->gtid_missing_nodes = false; + MyHGM->generate_mysql_gtid_executed_tables(); + pthread_mutex_unlock(&ev_loop_mutex); + } + ev_timer_start(loop, timer); + return; +} + +void reader_cb(struct ev_loop *loop, struct ev_io *w, int revents) { + pthread_mutex_lock(&ev_loop_mutex); + if (revents & EV_READ) { + GTID_Server_Data *sd = (GTID_Server_Data *)w->data; + bool rc = true; + rc = sd->readall(); + if (rc == false) { + //delete sd; + std::string s1 = sd->address; + s1.append(":"); + s1.append(std::to_string(sd->mysql_port)); + MyHGM->gtid_missing_nodes = true; + proxy_warning("GTID: failed to connect to ProxySQL binlog reader on port %d for server %s:%d\n", sd->port, sd->address, sd->mysql_port); + std::unordered_map ::iterator it2; + it2 = MyHGM->gtid_map.find(s1); + if (it2 != MyHGM->gtid_map.end()) { + //MyHGM->gtid_map.erase(it2); + it2->second = NULL; + delete sd; + } + ev_io_stop(MyHGM->gtid_ev_loop, w); + free(w); + } else { + sd->dump(); + } + } + pthread_mutex_unlock(&ev_loop_mutex); +} + +void connect_cb(EV_P_ ev_io *w, int revents) { + pthread_mutex_lock(&ev_loop_mutex); + struct ev_io * c = w; + if (revents & EV_WRITE) { + int optval = 0; + socklen_t optlen = sizeof(optval); + if ((getsockopt(w->fd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1) || + (optval != 0)) { + /* Connection failed; try the next address in the list. */ + //int errnum = optval ? optval : errno; + ev_io_stop(MyHGM->gtid_ev_loop, w); + close(w->fd); + MyHGM->gtid_missing_nodes = true; + GTID_Server_Data * custom_data = (GTID_Server_Data *)w->data; + GTID_Server_Data *sd = custom_data; + std::string s1 = sd->address; + s1.append(":"); + s1.append(std::to_string(sd->mysql_port)); + proxy_warning("GTID: failed to connect to ProxySQL binlog reader on port %d for server %s:%d\n", sd->port, sd->address, sd->mysql_port); + std::unordered_map ::iterator it2; + it2 = MyHGM->gtid_map.find(s1); + if (it2 != MyHGM->gtid_map.end()) { + //MyHGM->gtid_map.erase(it2); + it2->second = NULL; + delete sd; + } + //delete custom_data; + free(c); + } else { + ev_io_stop(MyHGM->gtid_ev_loop, w); + int fd=w->fd; + struct ev_io * new_w = (struct ev_io*) malloc(sizeof(struct ev_io)); + new_w->data = w->data; + GTID_Server_Data * custom_data = (GTID_Server_Data *)new_w->data; + custom_data->w = new_w; + free(w); + ev_io_init(new_w, reader_cb, fd, EV_READ); + ev_io_start(MyHGM->gtid_ev_loop, new_w); + } + } + pthread_mutex_unlock(&ev_loop_mutex); +} + +struct ev_io * new_connector(char *address, uint16_t gtid_port, uint16_t mysql_port) { + //struct sockaddr_in a; + int s; + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + perror("socket"); + close(s); + return NULL; + } +/* + memset(&a, 0, sizeof(a)); + a.sin_port = htons(gtid_port); + a.sin_family = AF_INET; + if (!inet_aton(address, (struct in_addr *) &a.sin_addr.s_addr)) { + perror("bad IP address format"); + close(s); + return NULL; + } +*/ + ioctl_FIONBIO(s,1); + + struct addrinfo hints; + struct addrinfo *res = NULL; + memset(&hints, 0, sizeof(hints)); + hints.ai_protocol= IPPROTO_TCP; + hints.ai_family= AF_UNSPEC; + hints.ai_socktype= SOCK_STREAM; + + char str_port[NI_MAXSERV+1]; + sprintf(str_port,"%d", gtid_port); + int gai_rc = getaddrinfo(address, str_port, &hints, &res); + if (gai_rc) { + freeaddrinfo(res); + //exit here + return NULL; + } + + //int status = connect(s, (struct sockaddr *) &a, sizeof(a)); + int status = connect(s, res->ai_addr, res->ai_addrlen); + if ((status == 0) || ((status == -1) && (errno == EINPROGRESS))) { + struct ev_io *c = (struct ev_io *)malloc(sizeof(struct ev_io)); + if (c) { + ev_io_init(c, connect_cb, s, EV_WRITE); + GTID_Server_Data * custom_data = new GTID_Server_Data(c, address, gtid_port, mysql_port); + c->data = (void *)custom_data; + return c; + } + /* else error */ + } + return NULL; +} + + + +GTID_Server_Data::GTID_Server_Data(struct ev_io *_w, char *_address, uint16_t _port, uint16_t _mysql_port) { + active = true; + w = _w; + size = 1024; // 1KB buffer + data = (char *)malloc(size); + memset(uuid_server, 0, sizeof(uuid_server)); + pos = 0; + len = 0; + address = strdup(_address); + port = _port; + mysql_port = _mysql_port; + events_read = 0; +} + +void GTID_Server_Data::resize(size_t _s) { + char *data_ = (char *)malloc(_s); + memcpy(data_, data, (_s > size ? size : _s)); + size = _s; + free(data); + data = data_; +} + +GTID_Server_Data::~GTID_Server_Data() { + free(address); + free(data); +} + +bool GTID_Server_Data::readall() { + bool ret = true; + if (size == len) { + // buffer is full, expand + resize(len*2); + } + int rc = 0; + rc = read(w->fd,data+len,size-len); + if (rc > 0) { + len += rc; + } else { + int myerr = errno; + proxy_error("Read returned %d bytes, error %d\n", rc, myerr); + if ( + (rc == 0) || + (rc==-1 && myerr != EINTR && myerr != EAGAIN) + ) { + ret = false; + } + } + return ret; +} + + +bool GTID_Server_Data::gtid_exists(char *gtid_uuid, uint64_t gtid_trxid) { + std::string s = gtid_uuid; + auto it = gtid_executed.find(s); +// fprintf(stderr,"Checking if server %s:%d has GTID %s:%lu ... ", address, port, gtid_uuid, gtid_trxid); + if (it == gtid_executed.end()) { +// fprintf(stderr,"NO\n"); + return false; + } + for (auto itr = it->second.begin(); itr != it->second.end(); ++itr) { + if ((int64_t)gtid_trxid >= itr->first && (int64_t)gtid_trxid <= itr->second) { +// fprintf(stderr,"YES\n"); + return true; + } + } +// fprintf(stderr,"NO\n"); + return false; +} + +void GTID_Server_Data::read_all_gtids() { + while (read_next_gtid()) { + } + } + +void GTID_Server_Data::dump() { + if (len==0) { + return; + } + read_all_gtids(); + //int rc = write(1,data+pos,len-pos); + fflush(stdout); + ///pos += rc; + if (pos >= len/2) { + memmove(data,data+pos,len-pos); + len = len-pos; + pos = 0; + } +} + +bool GTID_Server_Data::writeout() { + bool ret = true; + if (len==0) { + return ret; + } + int rc = 0; + rc = write(w->fd,data+pos,len-pos); + if (rc > 0) { + pos += rc; + if (pos >= len/2) { + memmove(data,data+pos,len-pos); + len = len-pos; + pos = 0; + } + } + return ret; +} + +bool GTID_Server_Data::read_next_gtid() { + if (len==0) { + return false; + } + void *nlp = NULL; + nlp = memchr(data+pos,'\n',len-pos); + if (nlp == NULL) { + return false; + } + int l = (char *)nlp - (data+pos); + char rec_msg[80]; + if (strncmp(data+pos,(char *)"ST=",3)==0) { + // we are reading the bootstrap + char *bs = (char *)malloc(l+1-3); // length + 1 (null byte) - 3 (header) + memcpy(bs, data+pos+3, l-3); + bs[l-3] = '\0'; + char *saveptr1=NULL; + char *saveptr2=NULL; + //char *saveptr3=NULL; + char *token = NULL; + char *subtoken = NULL; + //char *subtoken2 = NULL; + char *str1 = NULL; + char *str2 = NULL; + //char *str3 = NULL; + for (str1 = bs; ; str1 = NULL) { + token = strtok_r(str1, ",", &saveptr1); + if (token == NULL) { + break; + } + int j = 0; + for (str2 = token; ; str2 = NULL) { + subtoken = strtok_r(str2, ":", &saveptr2); + if (subtoken == NULL) { + break; + } + j++; + if (j%2 == 1) { // we are reading the uuid + char *p = uuid_server; + for (unsigned int k=0; kfirst; + s.insert(8,"-"); + s.insert(13,"-"); + s.insert(18,"-"); + s.insert(23,"-"); + s = s + ":"; + for (auto itr = it->second.begin(); itr != it->second.end(); ++itr) { + std::string s2 = s; + s2 = s2 + std::to_string(itr->first); + s2 = s2 + "-"; + s2 = s2 + std::to_string(itr->second); + s2 = s2 + ","; + gtid_set = gtid_set + s2; + } + } + // Extract latest comma only in case 'gtid_executed' isn't empty + if (gtid_set.empty() == false) { + gtid_set.pop_back(); + } + return gtid_set; +} + + + +void addGtid(const gtid_t& gtid, gtid_set_t& gtid_executed) { + auto it = gtid_executed.find(gtid.first); + if (it == gtid_executed.end()) + { + gtid_executed[gtid.first].emplace_back(gtid.second, gtid.second); + return; + } + + bool flag = true; + for (auto itr = it->second.begin(); itr != it->second.end(); ++itr) + { + if (gtid.second >= itr->first && gtid.second <= itr->second) + return; + if (gtid.second + 1 == itr->first) + { + --itr->first; + flag = false; + break; + } + else if (gtid.second == itr->second + 1) + { + ++itr->second; + flag = false; + break; + } + else if (gtid.second < itr->first) + { + it->second.emplace(itr, gtid.second, gtid.second); + return; + } + } + + if (flag) + it->second.emplace_back(gtid.second, gtid.second); + + for (auto itr = it->second.begin(); itr != it->second.end(); ++itr) + { + auto next_itr = std::next(itr); + if (next_itr != it->second.end() && itr->second + 1 == next_itr->first) + { + itr->second = next_itr->second; + it->second.erase(next_itr); + break; + } + } +} + +void * GTID_syncer_run() { + //struct ev_loop * gtid_ev_loop; + //gtid_ev_loop = NULL; + MyHGM->gtid_ev_loop = ev_loop_new (EVBACKEND_POLL | EVFLAG_NOENV); + if (MyHGM->gtid_ev_loop == NULL) { + proxy_error("could not initialise GTID sync loop\n"); + exit(EXIT_FAILURE); + } + //ev_async_init(gtid_ev_async, gtid_async_cb); + //ev_async_start(gtid_ev_loop, gtid_ev_async); + MyHGM->gtid_ev_timer = (struct ev_timer *)malloc(sizeof(struct ev_timer)); + ev_async_init(MyHGM->gtid_ev_async, gtid_async_cb); + ev_async_start(MyHGM->gtid_ev_loop, MyHGM->gtid_ev_async); + //ev_timer_init(MyHGM->gtid_ev_timer, gtid_timer_cb, __sync_add_and_fetch(&GloMTH->variables.binlog_reader_connect_retry_msec,0)/1000, 0); + ev_timer_init(MyHGM->gtid_ev_timer, gtid_timer_cb, 3, 0); + ev_timer_start(MyHGM->gtid_ev_loop, MyHGM->gtid_ev_timer); + //ev_ref(gtid_ev_loop); + ev_run(MyHGM->gtid_ev_loop, 0); + //sleep(1000); + return NULL; +} + diff --git a/lib/Makefile b/lib/Makefile index 6cf7396ec5..8ab3daea5f 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -128,6 +128,7 @@ default: libproxysql.a _OBJ_CXX := ProxySQL_GloVars.oo network.oo debug.oo configfile.oo Query_Cache.oo SpookyV2.oo MySQL_Authentication.oo gen_utils.oo sqlite3db.oo mysql_connection.oo MySQL_HostGroups_Manager.oo mysql_data_stream.oo MySQL_Thread.oo MySQL_Session.oo MySQL_Protocol.oo mysql_backend.oo Query_Processor.oo ProxySQL_Admin.oo ProxySQL_Config.oo ProxySQL_Restapi.oo MySQL_Monitor.oo MySQL_Logger.oo thread.oo MySQL_PreparedStatement.oo ProxySQL_Cluster.oo ClickHouse_Authentication.oo ClickHouse_Server.oo ProxySQL_Statistics.oo Chart_bundle_js.oo ProxySQL_HTTP_Server.oo ProxySQL_RESTAPI_Server.oo font-awesome.min.css.oo main-bundle.min.css.oo set_parser.oo MySQL_Variables.oo c_tokenizer.oo proxysql_utils.oo proxysql_coredump.oo proxysql_sslkeylog.oo \ sha256crypt.oo \ QP_rule_text.oo QP_query_digest_stats.oo \ + GTID_Server_Data.oo MyHGC.oo MySrvConnList.oo MySrvList.oo MySrvC.oo \ proxysql_find_charset.oo ProxySQL_Poll.oo OBJ_CXX := $(patsubst %,$(ODIR)/%,$(_OBJ_CXX)) HEADERS := ../include/*.h ../include/*.hpp diff --git a/lib/MyHGC.cpp b/lib/MyHGC.cpp new file mode 100644 index 0000000000..6daa6be295 --- /dev/null +++ b/lib/MyHGC.cpp @@ -0,0 +1,384 @@ +#include "MySQL_HostGroups_Manager.h" + + +extern MySQL_Threads_Handler *GloMTH; + +MyHGC::MyHGC(int _hid) { + hid=_hid; + mysrvs=new MySrvList(this); + current_time_now = 0; + new_connections_now = 0; + attributes.initialized = false; + reset_attributes(); + // Uninitialized server defaults. Should later be initialized via 'mysql_hostgroup_attributes'. + servers_defaults.weight = -1; + servers_defaults.max_connections = -1; + servers_defaults.use_ssl = -1; +} + +void MyHGC::reset_attributes() { + if (attributes.initialized == false) { + attributes.init_connect = NULL; + attributes.comment = NULL; + attributes.ignore_session_variables_text = NULL; + } + attributes.initialized = true; + attributes.configured = false; + attributes.max_num_online_servers = 1000000; + attributes.throttle_connections_per_sec = 1000000; + attributes.autocommit = -1; + attributes.free_connections_pct = 10; + attributes.handle_warnings = -1; + attributes.multiplex = true; + attributes.connection_warming = false; + free(attributes.init_connect); + attributes.init_connect = NULL; + free(attributes.comment); + attributes.comment = NULL; + free(attributes.ignore_session_variables_text); + attributes.ignore_session_variables_text = NULL; + attributes.ignore_session_variables_json = json(); +} + +MyHGC::~MyHGC() { + reset_attributes(); // free all memory + delete mysrvs; +} + +MySrvC *MyHGC::get_random_MySrvC(char * gtid_uuid, uint64_t gtid_trxid, int max_lag_ms, MySQL_Session *sess) { + MySrvC *mysrvc=NULL; + unsigned int j; + unsigned int sum=0; + unsigned int TotalUsedConn=0; + unsigned int l=mysrvs->cnt(); + static time_t last_hg_log = 0; +#ifdef TEST_AURORA + unsigned long long a1 = array_mysrvc_total/10000; + array_mysrvc_total += l; + unsigned long long a2 = array_mysrvc_total/10000; + if (a2 > a1) { + fprintf(stderr, "Total: %llu, Candidates: %llu\n", array_mysrvc_total-l, array_mysrvc_cands); + } +#endif // TEST_AURORA + MySrvC *mysrvcCandidates_static[32]; + MySrvC **mysrvcCandidates = mysrvcCandidates_static; + unsigned int num_candidates = 0; + bool max_connections_reached = false; + if (l>32) { + mysrvcCandidates = (MySrvC **)malloc(sizeof(MySrvC *)*l); + } + if (l) { + //int j=0; + for (j=0; jidx(j); + if (mysrvc->status==MYSQL_SERVER_STATUS_ONLINE) { // consider this server only if ONLINE + if (mysrvc->ConnectionsUsed->conns_length() < mysrvc->max_connections) { // consider this server only if didn't reach max_connections + if ( mysrvc->current_latency_us < ( mysrvc->max_latency_us ? mysrvc->max_latency_us : mysql_thread___default_max_latency_ms*1000 ) ) { // consider the host only if not too far + if (gtid_trxid) { + if (MyHGM->gtid_exists(mysrvc, gtid_uuid, gtid_trxid)) { + sum+=mysrvc->weight; + TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); + mysrvcCandidates[num_candidates]=mysrvc; + num_candidates++; + } + } else { + if (max_lag_ms >= 0) { + if ((unsigned int)max_lag_ms >= mysrvc->aws_aurora_current_lag_us/1000) { + sum+=mysrvc->weight; + TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); + mysrvcCandidates[num_candidates]=mysrvc; + num_candidates++; + } else { + sess->thread->status_variables.stvar[st_var_aws_aurora_replicas_skipped_during_query]++; + } + } else { + sum+=mysrvc->weight; + TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); + mysrvcCandidates[num_candidates]=mysrvc; + num_candidates++; + } + } + } + } else { + max_connections_reached = true; + } + } else { + if (mysrvc->status==MYSQL_SERVER_STATUS_SHUNNED) { + // try to recover shunned servers + if (mysrvc->shunned_automatic && mysql_thread___shun_recovery_time_sec) { + time_t t; + t=time(NULL); + // we do all these changes without locking . We assume the server is not used from long + // even if the server is still in used and any of the follow command fails it is not critical + // because this is only an attempt to recover a server that is probably dead anyway + + // the next few lines of code try to solve issue #530 + int max_wait_sec = ( mysql_thread___shun_recovery_time_sec * 1000 >= mysql_thread___connect_timeout_server_max ? mysql_thread___connect_timeout_server_max/1000 - 1 : mysql_thread___shun_recovery_time_sec ); + if (max_wait_sec < 1) { // min wait time should be at least 1 second + max_wait_sec = 1; + } + if (t > mysrvc->time_last_detected_error && (t - mysrvc->time_last_detected_error) > max_wait_sec) { + if ( + (mysrvc->shunned_and_kill_all_connections==false) // it is safe to bring it back online + || + (mysrvc->shunned_and_kill_all_connections==true && mysrvc->ConnectionsUsed->conns_length()==0 && mysrvc->ConnectionsFree->conns_length()==0) // if shunned_and_kill_all_connections is set, ensure all connections are already dropped + ) { +#ifdef DEBUG + if (GloMTH->variables.hostgroup_manager_verbose >= 3) { + proxy_info("Unshunning server %s:%d.\n", mysrvc->address, mysrvc->port); + } +#endif + mysrvc->status=MYSQL_SERVER_STATUS_ONLINE; + mysrvc->shunned_automatic=false; + mysrvc->shunned_and_kill_all_connections=false; + mysrvc->connect_ERR_at_time_last_detected_error=0; + mysrvc->time_last_detected_error=0; + // note: the following function scans all the hostgroups. + // This is ok for now because we only have a global mutex. + // If one day we implement a mutex per hostgroup (unlikely, + // but possible), this must be taken into consideration + if (mysql_thread___unshun_algorithm == 1) { + MyHGM->unshun_server_all_hostgroups(mysrvc->address, mysrvc->port, t, max_wait_sec, &mysrvc->myhgc->hid); + } + // if a server is taken back online, consider it immediately + if ( mysrvc->current_latency_us < ( mysrvc->max_latency_us ? mysrvc->max_latency_us : mysql_thread___default_max_latency_ms*1000 ) ) { // consider the host only if not too far + if (gtid_trxid) { + if (MyHGM->gtid_exists(mysrvc, gtid_uuid, gtid_trxid)) { + sum+=mysrvc->weight; + TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); + mysrvcCandidates[num_candidates]=mysrvc; + num_candidates++; + } + } else { + if (max_lag_ms >= 0) { + if ((unsigned int)max_lag_ms >= mysrvc->aws_aurora_current_lag_us/1000) { + sum+=mysrvc->weight; + TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); + mysrvcCandidates[num_candidates]=mysrvc; + num_candidates++; + } + } else { + sum+=mysrvc->weight; + TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); + mysrvcCandidates[num_candidates]=mysrvc; + num_candidates++; + } + } + } + } + } + } + } + } + } + if (max_lag_ms > 0) { // we are using AWS Aurora, as this logic is implemented only here + unsigned int min_num_replicas = sess->thread->variables.aurora_max_lag_ms_only_read_from_replicas; + if (min_num_replicas) { + if (num_candidates >= min_num_replicas) { // there are at least N replicas + // we try to remove the writer + unsigned int total_aws_aurora_current_lag_us=0; + for (j=0; jaws_aurora_current_lag_us; + } + if (total_aws_aurora_current_lag_us) { // we are just double checking that we don't have all servers with aws_aurora_current_lag_us==0 + for (j=0; jaws_aurora_current_lag_us==0) { + sum-=mysrvc->weight; + TotalUsedConn-=mysrvc->ConnectionsUsed->conns_length(); + if (j < num_candidates-1) { + mysrvcCandidates[j]=mysrvcCandidates[num_candidates-1]; + } + num_candidates--; + } + } + } + } + } + } + if (sum==0) { + // per issue #531 , we try a desperate attempt to bring back online any shunned server + // we do this lowering the maximum wait time to 10% + // most of the follow code is copied from few lines above + time_t t; + t=time(NULL); + int max_wait_sec = ( mysql_thread___shun_recovery_time_sec * 1000 >= mysql_thread___connect_timeout_server_max ? mysql_thread___connect_timeout_server_max/10000 - 1 : mysql_thread___shun_recovery_time_sec/10 ); + if (max_wait_sec < 1) { // min wait time should be at least 1 second + max_wait_sec = 1; + } + if (t - last_hg_log > 1) { // log this at most once per second to avoid spamming the logs + last_hg_log = time(NULL); + + if (gtid_trxid) { + proxy_error("Hostgroup %u has no servers ready for GTID '%s:%ld'. Waiting for replication...\n", hid, gtid_uuid, gtid_trxid); + } else { + proxy_error("Hostgroup %u has no servers available%s! Checking servers shunned for more than %u second%s\n", hid, + (max_connections_reached ? " or max_connections reached for all servers" : ""), max_wait_sec, max_wait_sec == 1 ? "" : "s"); + } + } + for (j=0; jidx(j); + if (mysrvc->status==MYSQL_SERVER_STATUS_SHUNNED && mysrvc->shunned_automatic==true) { + if ((t - mysrvc->time_last_detected_error) > max_wait_sec) { + mysrvc->status=MYSQL_SERVER_STATUS_ONLINE; + mysrvc->shunned_automatic=false; + mysrvc->connect_ERR_at_time_last_detected_error=0; + mysrvc->time_last_detected_error=0; + // if a server is taken back online, consider it immediately + if ( mysrvc->current_latency_us < ( mysrvc->max_latency_us ? mysrvc->max_latency_us : mysql_thread___default_max_latency_ms*1000 ) ) { // consider the host only if not too far + if (gtid_trxid) { + if (MyHGM->gtid_exists(mysrvc, gtid_uuid, gtid_trxid)) { + sum+=mysrvc->weight; + TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); + mysrvcCandidates[num_candidates]=mysrvc; + num_candidates++; + } + } else { + if (max_lag_ms >= 0) { + if ((unsigned int)max_lag_ms >= mysrvc->aws_aurora_current_lag_us/1000) { + sum+=mysrvc->weight; + TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); + mysrvcCandidates[num_candidates]=mysrvc; + num_candidates++; + } + } else { + sum+=mysrvc->weight; + TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); + mysrvcCandidates[num_candidates]=mysrvc; + num_candidates++; + } + } + } + } + } + } + } + if (sum==0) { + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySrvC NULL because no backend ONLINE or with weight\n"); + if (l>32) { + free(mysrvcCandidates); + } +#ifdef TEST_AURORA + array_mysrvc_cands += num_candidates; +#endif // TEST_AURORA + return NULL; // if we reach here, we couldn't find any target + } + +/* + unsigned int New_sum=0; + unsigned int New_TotalUsedConn=0; + // we will now scan again to ignore overloaded servers + for (j=0; jConnectionsUsed->conns_length(); + if ((len * sum) <= (TotalUsedConn * mysrvc->weight * 1.5 + 1)) { + + New_sum+=mysrvc->weight; + New_TotalUsedConn+=len; + } else { + // remove the candidate + if (j+1 < num_candidates) { + mysrvcCandidates[j] = mysrvcCandidates[num_candidates-1]; + } + j--; + num_candidates--; + } + } +*/ + + unsigned int New_sum=sum; + + if (New_sum==0) { + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySrvC NULL because no backend ONLINE or with weight\n"); + if (l>32) { + free(mysrvcCandidates); + } +#ifdef TEST_AURORA + array_mysrvc_cands += num_candidates; +#endif // TEST_AURORA + return NULL; // if we reach here, we couldn't find any target + } + + // latency awareness algorithm is enabled only when compiled with USE_MYSRVC_ARRAY + if (sess && sess->thread->variables.min_num_servers_lantency_awareness) { + if ((int) num_candidates >= sess->thread->variables.min_num_servers_lantency_awareness) { + unsigned int servers_with_latency = 0; + unsigned int total_latency_us = 0; + // scan and verify that all servers have some latency + for (j=0; jcurrent_latency_us) { + servers_with_latency++; + total_latency_us += mysrvc->current_latency_us; + } + } + if (servers_with_latency == num_candidates) { + // all servers have some latency. + // That is good. If any server have no latency, something is wrong + // and we will skip this algorithm + sess->thread->status_variables.stvar[st_var_ConnPool_get_conn_latency_awareness]++; + unsigned int avg_latency_us = 0; + avg_latency_us = total_latency_us/num_candidates; + for (j=0; jcurrent_latency_us > avg_latency_us) { + // remove the candidate + if (j+1 < num_candidates) { + mysrvcCandidates[j] = mysrvcCandidates[num_candidates-1]; + } + j--; + num_candidates--; + } + } + // we scan again to adjust weight + New_sum = 0; + for (j=0; jweight; + } + } + } + } + + + unsigned int k; + if (New_sum > 32768) { + k=rand()%New_sum; + } else { + k=fastrand()%New_sum; + } + k++; + New_sum=0; + + for (j=0; jweight; + if (k<=New_sum) { + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySrvC %p, server %s:%d\n", mysrvc, mysrvc->address, mysrvc->port); + if (l>32) { + free(mysrvcCandidates); + } +#ifdef TEST_AURORA + array_mysrvc_cands += num_candidates; +#endif // TEST_AURORA + return mysrvc; + } + } + } else { + time_t t = time(NULL); + + if (t - last_hg_log > 1) { + last_hg_log = time(NULL); + proxy_error("Hostgroup %u has no servers available!\n", hid); + } + } + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySrvC NULL\n"); + if (l>32) { + free(mysrvcCandidates); + } +#ifdef TEST_AURORA + array_mysrvc_cands += num_candidates; +#endif // TEST_AURORA + return NULL; // if we reach here, we couldn't find any target +} diff --git a/lib/MySQL_HostGroups_Manager.cpp b/lib/MySQL_HostGroups_Manager.cpp index d1f6188ec4..90bc415367 100644 --- a/lib/MySQL_HostGroups_Manager.cpp +++ b/lib/MySQL_HostGroups_Manager.cpp @@ -57,39 +57,9 @@ class MySrvC; class MySrvList; class MyHGC; -//static struct ev_async * gtid_ev_async; +struct ev_io * new_connector(char *address, uint16_t gtid_port, uint16_t mysql_port); +void * GTID_syncer_run(); -static pthread_mutex_t ev_loop_mutex; - -//static std::unordered_map gtid_map; - -static void gtid_async_cb(struct ev_loop *loop, struct ev_async *watcher, int revents) { - if (glovars.shutdown) { - ev_break(loop); - } - pthread_mutex_lock(&ev_loop_mutex); - MyHGM->gtid_missing_nodes = false; - MyHGM->generate_mysql_gtid_executed_tables(); - pthread_mutex_unlock(&ev_loop_mutex); - return; -} - -static void gtid_timer_cb (struct ev_loop *loop, struct ev_timer *timer, int revents) { - if (GloMTH == nullptr) { return; } - ev_timer_stop(loop, timer); - ev_timer_set(timer, __sync_add_and_fetch(&GloMTH->variables.binlog_reader_connect_retry_msec,0)/1000, 0); - if (glovars.shutdown) { - ev_break(loop); - } - if (MyHGM->gtid_missing_nodes) { - pthread_mutex_lock(&ev_loop_mutex); - MyHGM->gtid_missing_nodes = false; - MyHGM->generate_mysql_gtid_executed_tables(); - pthread_mutex_unlock(&ev_loop_mutex); - } - ev_timer_start(loop, timer); - return; -} static int wait_for_mysql(MYSQL *mysql, int status) { struct pollfd pfd; @@ -115,432 +85,6 @@ static int wait_for_mysql(MYSQL *mysql, int status) { } } -void reader_cb(struct ev_loop *loop, struct ev_io *w, int revents) { - pthread_mutex_lock(&ev_loop_mutex); - if (revents & EV_READ) { - GTID_Server_Data *sd = (GTID_Server_Data *)w->data; - bool rc = true; - rc = sd->readall(); - if (rc == false) { - //delete sd; - std::string s1 = sd->address; - s1.append(":"); - s1.append(std::to_string(sd->mysql_port)); - MyHGM->gtid_missing_nodes = true; - proxy_warning("GTID: failed to connect to ProxySQL binlog reader on port %d for server %s:%d\n", sd->port, sd->address, sd->mysql_port); - std::unordered_map ::iterator it2; - it2 = MyHGM->gtid_map.find(s1); - if (it2 != MyHGM->gtid_map.end()) { - //MyHGM->gtid_map.erase(it2); - it2->second = NULL; - delete sd; - } - ev_io_stop(MyHGM->gtid_ev_loop, w); - free(w); - } else { - sd->dump(); - } - } - pthread_mutex_unlock(&ev_loop_mutex); -} - -void connect_cb(EV_P_ ev_io *w, int revents) { - pthread_mutex_lock(&ev_loop_mutex); - struct ev_io * c = w; - if (revents & EV_WRITE) { - int optval = 0; - socklen_t optlen = sizeof(optval); - if ((getsockopt(w->fd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1) || - (optval != 0)) { - /* Connection failed; try the next address in the list. */ - //int errnum = optval ? optval : errno; - ev_io_stop(MyHGM->gtid_ev_loop, w); - close(w->fd); - MyHGM->gtid_missing_nodes = true; - GTID_Server_Data * custom_data = (GTID_Server_Data *)w->data; - GTID_Server_Data *sd = custom_data; - std::string s1 = sd->address; - s1.append(":"); - s1.append(std::to_string(sd->mysql_port)); - proxy_warning("GTID: failed to connect to ProxySQL binlog reader on port %d for server %s:%d\n", sd->port, sd->address, sd->mysql_port); - std::unordered_map ::iterator it2; - it2 = MyHGM->gtid_map.find(s1); - if (it2 != MyHGM->gtid_map.end()) { - //MyHGM->gtid_map.erase(it2); - it2->second = NULL; - delete sd; - } - //delete custom_data; - free(c); - } else { - ev_io_stop(MyHGM->gtid_ev_loop, w); - int fd=w->fd; - struct ev_io * new_w = (struct ev_io*) malloc(sizeof(struct ev_io)); - new_w->data = w->data; - GTID_Server_Data * custom_data = (GTID_Server_Data *)new_w->data; - custom_data->w = new_w; - free(w); - ev_io_init(new_w, reader_cb, fd, EV_READ); - ev_io_start(MyHGM->gtid_ev_loop, new_w); - } - } - pthread_mutex_unlock(&ev_loop_mutex); -} - -struct ev_io * new_connector(char *address, uint16_t gtid_port, uint16_t mysql_port) { - //struct sockaddr_in a; - int s; - - if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) { - perror("socket"); - close(s); - return NULL; - } -/* - memset(&a, 0, sizeof(a)); - a.sin_port = htons(gtid_port); - a.sin_family = AF_INET; - if (!inet_aton(address, (struct in_addr *) &a.sin_addr.s_addr)) { - perror("bad IP address format"); - close(s); - return NULL; - } -*/ - ioctl_FIONBIO(s,1); - - struct addrinfo hints; - struct addrinfo *res = NULL; - memset(&hints, 0, sizeof(hints)); - hints.ai_protocol= IPPROTO_TCP; - hints.ai_family= AF_UNSPEC; - hints.ai_socktype= SOCK_STREAM; - - char str_port[NI_MAXSERV+1]; - sprintf(str_port,"%d", gtid_port); - int gai_rc = getaddrinfo(address, str_port, &hints, &res); - if (gai_rc) { - freeaddrinfo(res); - //exit here - return NULL; - } - - //int status = connect(s, (struct sockaddr *) &a, sizeof(a)); - int status = connect(s, res->ai_addr, res->ai_addrlen); - if ((status == 0) || ((status == -1) && (errno == EINPROGRESS))) { - struct ev_io *c = (struct ev_io *)malloc(sizeof(struct ev_io)); - if (c) { - ev_io_init(c, connect_cb, s, EV_WRITE); - GTID_Server_Data * custom_data = new GTID_Server_Data(c, address, gtid_port, mysql_port); - c->data = (void *)custom_data; - return c; - } - /* else error */ - } - return NULL; -} - - - -GTID_Server_Data::GTID_Server_Data(struct ev_io *_w, char *_address, uint16_t _port, uint16_t _mysql_port) { - active = true; - w = _w; - size = 1024; // 1KB buffer - data = (char *)malloc(size); - memset(uuid_server, 0, sizeof(uuid_server)); - pos = 0; - len = 0; - address = strdup(_address); - port = _port; - mysql_port = _mysql_port; - events_read = 0; -} - -void GTID_Server_Data::resize(size_t _s) { - char *data_ = (char *)malloc(_s); - memcpy(data_, data, (_s > size ? size : _s)); - size = _s; - free(data); - data = data_; -} - -GTID_Server_Data::~GTID_Server_Data() { - free(address); - free(data); -} - -bool GTID_Server_Data::readall() { - bool ret = true; - if (size == len) { - // buffer is full, expand - resize(len*2); - } - int rc = 0; - rc = read(w->fd,data+len,size-len); - if (rc > 0) { - len += rc; - } else { - int myerr = errno; - proxy_error("Read returned %d bytes, error %d\n", rc, myerr); - if ( - (rc == 0) || - (rc==-1 && myerr != EINTR && myerr != EAGAIN) - ) { - ret = false; - } - } - return ret; -} - - -bool GTID_Server_Data::gtid_exists(char *gtid_uuid, uint64_t gtid_trxid) { - std::string s = gtid_uuid; - auto it = gtid_executed.find(s); -// fprintf(stderr,"Checking if server %s:%d has GTID %s:%lu ... ", address, port, gtid_uuid, gtid_trxid); - if (it == gtid_executed.end()) { -// fprintf(stderr,"NO\n"); - return false; - } - for (auto itr = it->second.begin(); itr != it->second.end(); ++itr) { - if ((int64_t)gtid_trxid >= itr->first && (int64_t)gtid_trxid <= itr->second) { -// fprintf(stderr,"YES\n"); - return true; - } - } -// fprintf(stderr,"NO\n"); - return false; -} - -void GTID_Server_Data::read_all_gtids() { - while (read_next_gtid()) { - } - } - -void GTID_Server_Data::dump() { - if (len==0) { - return; - } - read_all_gtids(); - //int rc = write(1,data+pos,len-pos); - fflush(stdout); - ///pos += rc; - if (pos >= len/2) { - memmove(data,data+pos,len-pos); - len = len-pos; - pos = 0; - } -} - -bool GTID_Server_Data::writeout() { - bool ret = true; - if (len==0) { - return ret; - } - int rc = 0; - rc = write(w->fd,data+pos,len-pos); - if (rc > 0) { - pos += rc; - if (pos >= len/2) { - memmove(data,data+pos,len-pos); - len = len-pos; - pos = 0; - } - } - return ret; -} - -bool GTID_Server_Data::read_next_gtid() { - if (len==0) { - return false; - } - void *nlp = NULL; - nlp = memchr(data+pos,'\n',len-pos); - if (nlp == NULL) { - return false; - } - int l = (char *)nlp - (data+pos); - char rec_msg[80]; - if (strncmp(data+pos,(char *)"ST=",3)==0) { - // we are reading the bootstrap - char *bs = (char *)malloc(l+1-3); // length + 1 (null byte) - 3 (header) - memcpy(bs, data+pos+3, l-3); - bs[l-3] = '\0'; - char *saveptr1=NULL; - char *saveptr2=NULL; - //char *saveptr3=NULL; - char *token = NULL; - char *subtoken = NULL; - //char *subtoken2 = NULL; - char *str1 = NULL; - char *str2 = NULL; - //char *str3 = NULL; - for (str1 = bs; ; str1 = NULL) { - token = strtok_r(str1, ",", &saveptr1); - if (token == NULL) { - break; - } - int j = 0; - for (str2 = token; ; str2 = NULL) { - subtoken = strtok_r(str2, ":", &saveptr2); - if (subtoken == NULL) { - break; - } - j++; - if (j%2 == 1) { // we are reading the uuid - char *p = uuid_server; - for (unsigned int k=0; kfirst; - s.insert(8,"-"); - s.insert(13,"-"); - s.insert(18,"-"); - s.insert(23,"-"); - s = s + ":"; - for (auto itr = it->second.begin(); itr != it->second.end(); ++itr) { - std::string s2 = s; - s2 = s2 + std::to_string(itr->first); - s2 = s2 + "-"; - s2 = s2 + std::to_string(itr->second); - s2 = s2 + ","; - gtid_set = gtid_set + s2; - } - } - // Extract latest comma only in case 'gtid_executed' isn't empty - if (gtid_set.empty() == false) { - gtid_set.pop_back(); - } - return gtid_set; -} - - - -void addGtid(const gtid_t& gtid, gtid_set_t& gtid_executed) { - auto it = gtid_executed.find(gtid.first); - if (it == gtid_executed.end()) - { - gtid_executed[gtid.first].emplace_back(gtid.second, gtid.second); - return; - } - - bool flag = true; - for (auto itr = it->second.begin(); itr != it->second.end(); ++itr) - { - if (gtid.second >= itr->first && gtid.second <= itr->second) - return; - if (gtid.second + 1 == itr->first) - { - --itr->first; - flag = false; - break; - } - else if (gtid.second == itr->second + 1) - { - ++itr->second; - flag = false; - break; - } - else if (gtid.second < itr->first) - { - it->second.emplace(itr, gtid.second, gtid.second); - return; - } - } - - if (flag) - it->second.emplace_back(gtid.second, gtid.second); - - for (auto itr = it->second.begin(); itr != it->second.end(); ++itr) - { - auto next_itr = std::next(itr); - if (next_itr != it->second.end() && itr->second + 1 == next_itr->first) - { - itr->second = next_itr->second; - it->second.erase(next_itr); - break; - } - } -} - -static void * GTID_syncer_run() { - //struct ev_loop * gtid_ev_loop; - //gtid_ev_loop = NULL; - MyHGM->gtid_ev_loop = ev_loop_new (EVBACKEND_POLL | EVFLAG_NOENV); - if (MyHGM->gtid_ev_loop == NULL) { - proxy_error("could not initialise GTID sync loop\n"); - exit(EXIT_FAILURE); - } - //ev_async_init(gtid_ev_async, gtid_async_cb); - //ev_async_start(gtid_ev_loop, gtid_ev_async); - MyHGM->gtid_ev_timer = (struct ev_timer *)malloc(sizeof(struct ev_timer)); - ev_async_init(MyHGM->gtid_ev_async, gtid_async_cb); - ev_async_start(MyHGM->gtid_ev_loop, MyHGM->gtid_ev_async); - //ev_timer_init(MyHGM->gtid_ev_timer, gtid_timer_cb, __sync_add_and_fetch(&GloMTH->variables.binlog_reader_connect_retry_msec,0)/1000, 0); - ev_timer_init(MyHGM->gtid_ev_timer, gtid_timer_cb, 3, 0); - ev_timer_start(MyHGM->gtid_ev_loop, MyHGM->gtid_ev_timer); - //ev_ref(gtid_ev_loop); - ev_run(MyHGM->gtid_ev_loop, 0); - //sleep(1000); - return NULL; -} //static void * HGCU_thread_run() { static void * HGCU_thread_run() { @@ -661,266 +205,6 @@ static void * HGCU_thread_run() { } -MySQL_Connection *MySrvConnList::index(unsigned int _k) { - return (MySQL_Connection *)conns->index(_k); -} - -MySQL_Connection * MySrvConnList::remove(int _k) { - return (MySQL_Connection *)conns->remove_index_fast(_k); -} - -/* -unsigned int MySrvConnList::conns_length() { - return conns->len; -} -*/ - -MySrvConnList::MySrvConnList(MySrvC *_mysrvc) { - mysrvc=_mysrvc; - conns=new PtrArray(); -} - -void MySrvConnList::add(MySQL_Connection *c) { - conns->add(c); -} - -MySrvConnList::~MySrvConnList() { - mysrvc=NULL; - while (conns_length()) { - MySQL_Connection *conn=(MySQL_Connection *)conns->remove_index_fast(0); - delete conn; - } - delete conns; -} - -MySrvList::MySrvList(MyHGC *_myhgc) { - myhgc=_myhgc; - servers=new PtrArray(); -} - -void MySrvList::add(MySrvC *s) { - if (s->myhgc==NULL) { - s->myhgc=myhgc; - } - servers->add(s); -} - - -int MySrvList::find_idx(MySrvC *s) { - for (unsigned int i=0; ilen; i++) { - MySrvC *mysrv=(MySrvC *)servers->index(i); - if (mysrv==s) { - return (unsigned int)i; - } - } - return -1; -} - -void MySrvList::remove(MySrvC *s) { - int i=find_idx(s); - assert(i>=0); - servers->remove_index_fast((unsigned int)i); -} - -void MySrvConnList::drop_all_connections() { - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Dropping all connections (%u total) on MySrvConnList %p for server %s:%d , hostgroup=%d , status=%d\n", conns_length(), this, mysrvc->address, mysrvc->port, mysrvc->myhgc->hid, mysrvc->status); - while (conns_length()) { - MySQL_Connection *conn=(MySQL_Connection *)conns->remove_index_fast(0); - delete conn; - } -} - - -MySrvC::MySrvC( - char* add, uint16_t p, uint16_t gp, int64_t _weight, enum MySerStatus _status, unsigned int _compression, - int64_t _max_connections, unsigned int _max_replication_lag, int32_t _use_ssl, unsigned int _max_latency_ms, - char* _comment -) { - address=strdup(add); - port=p; - gtid_port=gp; - weight=_weight; - status=_status; - compression=_compression; - max_connections=_max_connections; - max_replication_lag=_max_replication_lag; - use_ssl=_use_ssl; - cur_replication_lag=0; - cur_replication_lag_count=0; - max_latency_us=_max_latency_ms*1000; - current_latency_us=0; - aws_aurora_current_lag_us = 0; - connect_OK=0; - connect_ERR=0; - queries_sent=0; - bytes_sent=0; - bytes_recv=0; - max_connections_used=0; - queries_gtid_sync=0; - time_last_detected_error=0; - connect_ERR_at_time_last_detected_error=0; - shunned_automatic=false; - shunned_and_kill_all_connections=false; // false to default - //charset=_charset; - myhgc=NULL; - comment=strdup(_comment); - ConnectionsUsed=new MySrvConnList(this); - ConnectionsFree=new MySrvConnList(this); -} - -void MySrvC::connect_error(int err_num, bool get_mutex) { - // NOTE: this function operates without any mutex - // although, it is not extremely important if any counter is lost - // as a single connection failure won't make a significant difference - proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Connect failed with code '%d'\n", err_num); - __sync_fetch_and_add(&connect_ERR,1); - __sync_fetch_and_add(&MyHGM->status.server_connections_aborted,1); - if (err_num >= 1048 && err_num <= 1052) - return; - if (err_num >= 1054 && err_num <= 1075) - return; - if (err_num >= 1099 && err_num <= 1104) - return; - if (err_num >= 1106 && err_num <= 1113) - return; - if (err_num >= 1116 && err_num <= 1118) - return; - if (err_num == 1136 || (err_num >= 1138 && err_num <= 1149)) - return; - switch (err_num) { - case 1007: // Can't create database - case 1008: // Can't drop database - case 1044: // access denied - case 1045: // access denied -/* - case 1048: // Column cannot be null - case 1049: // Unknown database - case 1050: // Table already exists - case 1051: // Unknown table - case 1052: // Column is ambiguous -*/ - case 1120: - case 1203: // User %s already has more than 'max_user_connections' active connections - case 1226: // User '%s' has exceeded the '%s' resource (current value: %ld) - case 3118: // Access denied for user '%s'. Account is locked.. - return; - break; - default: - break; - } - time_t t=time(NULL); - if (t > time_last_detected_error) { - time_last_detected_error=t; - connect_ERR_at_time_last_detected_error=1; - } else { - if (t < time_last_detected_error) { - // time_last_detected_error is in the future - // this means that monitor has a ping interval too big and tuned that in the future - return; - } - // same time - /** - * @brief The expected configured retries set by 'mysql-connect_retries_on_failure' + '2' extra expected - * connection errors. - * @details This two extra connections errors are expected: - * 1. An initial connection error generated by the datastream and the connection when being created, - * this is, right after the session has requested a connection to the connection pool. This error takes - * places directly in the state machine from 'MySQL_Connection'. Because of this, we consider this - * additional error to be a consequence of the two states machines, and it's not considered for - * 'connect_retries'. - * 2. A second connection connection error, which is the initial connection error generated by 'MySQL_Session' - * when already in the 'CONNECTING_SERVER' state. This error is an 'extra error' to always consider, since - * it's not part of the retries specified by 'mysql_thread___connect_retries_on_failure', thus, we set the - * 'connect_retries' to be 'mysql_thread___connect_retries_on_failure + 1'. - */ - int connect_retries = mysql_thread___connect_retries_on_failure + 1; - int max_failures = mysql_thread___shun_on_failures > connect_retries ? connect_retries : mysql_thread___shun_on_failures; - - if (__sync_add_and_fetch(&connect_ERR_at_time_last_detected_error,1) >= (unsigned int)max_failures) { - bool _shu=false; - if (get_mutex==true) - MyHGM->wrlock(); // to prevent race conditions, lock here. See #627 - if (status==MYSQL_SERVER_STATUS_ONLINE) { - status=MYSQL_SERVER_STATUS_SHUNNED; - shunned_automatic=true; - _shu=true; - } else { - _shu=false; - } - if (get_mutex==true) - MyHGM->wrunlock(); - if (_shu) { - proxy_error("Shunning server %s:%d with %u errors/sec. Shunning for %u seconds\n", address, port, connect_ERR_at_time_last_detected_error , mysql_thread___shun_recovery_time_sec); - } - } - } -} - -void MySrvC::shun_and_killall() { - status=MYSQL_SERVER_STATUS_SHUNNED; - shunned_automatic=true; - shunned_and_kill_all_connections=true; -} - -MySrvC::~MySrvC() { - if (address) free(address); - if (comment) free(comment); - delete ConnectionsUsed; - delete ConnectionsFree; -} - -MySrvList::~MySrvList() { - myhgc=NULL; - while (servers->len) { - MySrvC *mysrvc=(MySrvC *)servers->remove_index_fast(0); - delete mysrvc; - } - delete servers; -} - - -MyHGC::MyHGC(int _hid) { - hid=_hid; - mysrvs=new MySrvList(this); - current_time_now = 0; - new_connections_now = 0; - attributes.initialized = false; - reset_attributes(); - // Uninitialized server defaults. Should later be initialized via 'mysql_hostgroup_attributes'. - servers_defaults.weight = -1; - servers_defaults.max_connections = -1; - servers_defaults.use_ssl = -1; -} - -void MyHGC::reset_attributes() { - if (attributes.initialized == false) { - attributes.init_connect = NULL; - attributes.comment = NULL; - attributes.ignore_session_variables_text = NULL; - } - attributes.initialized = true; - attributes.configured = false; - attributes.max_num_online_servers = 1000000; - attributes.throttle_connections_per_sec = 1000000; - attributes.autocommit = -1; - attributes.free_connections_pct = 10; - attributes.handle_warnings = -1; - attributes.multiplex = true; - attributes.connection_warming = false; - free(attributes.init_connect); - attributes.init_connect = NULL; - free(attributes.comment); - attributes.comment = NULL; - free(attributes.ignore_session_variables_text); - attributes.ignore_session_variables_text = NULL; - attributes.ignore_session_variables_json = json(); -} - -MyHGC::~MyHGC() { - reset_attributes(); // free all memory - delete mysrvs; -} - using metric_name = std::string; using metric_help = std::string; using metric_tags = std::map; @@ -1296,7 +580,6 @@ hg_metrics_map = std::make_tuple( ); MySQL_HostGroups_Manager::MySQL_HostGroups_Manager() { - pthread_mutex_init(&ev_loop_mutex, NULL); status.client_connections=0; status.client_connections_aborted=0; status.client_connections_created=0; @@ -3137,557 +2420,6 @@ void MySQL_HostGroups_Manager::push_MyConn_to_pool_array(MySQL_Connection **ca, wrunlock(); } -MySrvC *MyHGC::get_random_MySrvC(char * gtid_uuid, uint64_t gtid_trxid, int max_lag_ms, MySQL_Session *sess) { - MySrvC *mysrvc=NULL; - unsigned int j; - unsigned int sum=0; - unsigned int TotalUsedConn=0; - unsigned int l=mysrvs->cnt(); - static time_t last_hg_log = 0; -#ifdef TEST_AURORA - unsigned long long a1 = array_mysrvc_total/10000; - array_mysrvc_total += l; - unsigned long long a2 = array_mysrvc_total/10000; - if (a2 > a1) { - fprintf(stderr, "Total: %llu, Candidates: %llu\n", array_mysrvc_total-l, array_mysrvc_cands); - } -#endif // TEST_AURORA - MySrvC *mysrvcCandidates_static[32]; - MySrvC **mysrvcCandidates = mysrvcCandidates_static; - unsigned int num_candidates = 0; - bool max_connections_reached = false; - if (l>32) { - mysrvcCandidates = (MySrvC **)malloc(sizeof(MySrvC *)*l); - } - if (l) { - //int j=0; - for (j=0; jidx(j); - if (mysrvc->status==MYSQL_SERVER_STATUS_ONLINE) { // consider this server only if ONLINE - if (mysrvc->ConnectionsUsed->conns_length() < mysrvc->max_connections) { // consider this server only if didn't reach max_connections - if ( mysrvc->current_latency_us < ( mysrvc->max_latency_us ? mysrvc->max_latency_us : mysql_thread___default_max_latency_ms*1000 ) ) { // consider the host only if not too far - if (gtid_trxid) { - if (MyHGM->gtid_exists(mysrvc, gtid_uuid, gtid_trxid)) { - sum+=mysrvc->weight; - TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); - mysrvcCandidates[num_candidates]=mysrvc; - num_candidates++; - } - } else { - if (max_lag_ms >= 0) { - if ((unsigned int)max_lag_ms >= mysrvc->aws_aurora_current_lag_us/1000) { - sum+=mysrvc->weight; - TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); - mysrvcCandidates[num_candidates]=mysrvc; - num_candidates++; - } else { - sess->thread->status_variables.stvar[st_var_aws_aurora_replicas_skipped_during_query]++; - } - } else { - sum+=mysrvc->weight; - TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); - mysrvcCandidates[num_candidates]=mysrvc; - num_candidates++; - } - } - } - } else { - max_connections_reached = true; - } - } else { - if (mysrvc->status==MYSQL_SERVER_STATUS_SHUNNED) { - // try to recover shunned servers - if (mysrvc->shunned_automatic && mysql_thread___shun_recovery_time_sec) { - time_t t; - t=time(NULL); - // we do all these changes without locking . We assume the server is not used from long - // even if the server is still in used and any of the follow command fails it is not critical - // because this is only an attempt to recover a server that is probably dead anyway - - // the next few lines of code try to solve issue #530 - int max_wait_sec = ( mysql_thread___shun_recovery_time_sec * 1000 >= mysql_thread___connect_timeout_server_max ? mysql_thread___connect_timeout_server_max/1000 - 1 : mysql_thread___shun_recovery_time_sec ); - if (max_wait_sec < 1) { // min wait time should be at least 1 second - max_wait_sec = 1; - } - if (t > mysrvc->time_last_detected_error && (t - mysrvc->time_last_detected_error) > max_wait_sec) { - if ( - (mysrvc->shunned_and_kill_all_connections==false) // it is safe to bring it back online - || - (mysrvc->shunned_and_kill_all_connections==true && mysrvc->ConnectionsUsed->conns_length()==0 && mysrvc->ConnectionsFree->conns_length()==0) // if shunned_and_kill_all_connections is set, ensure all connections are already dropped - ) { -#ifdef DEBUG - if (GloMTH->variables.hostgroup_manager_verbose >= 3) { - proxy_info("Unshunning server %s:%d.\n", mysrvc->address, mysrvc->port); - } -#endif - mysrvc->status=MYSQL_SERVER_STATUS_ONLINE; - mysrvc->shunned_automatic=false; - mysrvc->shunned_and_kill_all_connections=false; - mysrvc->connect_ERR_at_time_last_detected_error=0; - mysrvc->time_last_detected_error=0; - // note: the following function scans all the hostgroups. - // This is ok for now because we only have a global mutex. - // If one day we implement a mutex per hostgroup (unlikely, - // but possible), this must be taken into consideration - if (mysql_thread___unshun_algorithm == 1) { - MyHGM->unshun_server_all_hostgroups(mysrvc->address, mysrvc->port, t, max_wait_sec, &mysrvc->myhgc->hid); - } - // if a server is taken back online, consider it immediately - if ( mysrvc->current_latency_us < ( mysrvc->max_latency_us ? mysrvc->max_latency_us : mysql_thread___default_max_latency_ms*1000 ) ) { // consider the host only if not too far - if (gtid_trxid) { - if (MyHGM->gtid_exists(mysrvc, gtid_uuid, gtid_trxid)) { - sum+=mysrvc->weight; - TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); - mysrvcCandidates[num_candidates]=mysrvc; - num_candidates++; - } - } else { - if (max_lag_ms >= 0) { - if ((unsigned int)max_lag_ms >= mysrvc->aws_aurora_current_lag_us/1000) { - sum+=mysrvc->weight; - TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); - mysrvcCandidates[num_candidates]=mysrvc; - num_candidates++; - } - } else { - sum+=mysrvc->weight; - TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); - mysrvcCandidates[num_candidates]=mysrvc; - num_candidates++; - } - } - } - } - } - } - } - } - } - if (max_lag_ms > 0) { // we are using AWS Aurora, as this logic is implemented only here - unsigned int min_num_replicas = sess->thread->variables.aurora_max_lag_ms_only_read_from_replicas; - if (min_num_replicas) { - if (num_candidates >= min_num_replicas) { // there are at least N replicas - // we try to remove the writer - unsigned int total_aws_aurora_current_lag_us=0; - for (j=0; jaws_aurora_current_lag_us; - } - if (total_aws_aurora_current_lag_us) { // we are just double checking that we don't have all servers with aws_aurora_current_lag_us==0 - for (j=0; jaws_aurora_current_lag_us==0) { - sum-=mysrvc->weight; - TotalUsedConn-=mysrvc->ConnectionsUsed->conns_length(); - if (j < num_candidates-1) { - mysrvcCandidates[j]=mysrvcCandidates[num_candidates-1]; - } - num_candidates--; - } - } - } - } - } - } - if (sum==0) { - // per issue #531 , we try a desperate attempt to bring back online any shunned server - // we do this lowering the maximum wait time to 10% - // most of the follow code is copied from few lines above - time_t t; - t=time(NULL); - int max_wait_sec = ( mysql_thread___shun_recovery_time_sec * 1000 >= mysql_thread___connect_timeout_server_max ? mysql_thread___connect_timeout_server_max/10000 - 1 : mysql_thread___shun_recovery_time_sec/10 ); - if (max_wait_sec < 1) { // min wait time should be at least 1 second - max_wait_sec = 1; - } - if (t - last_hg_log > 1) { // log this at most once per second to avoid spamming the logs - last_hg_log = time(NULL); - - if (gtid_trxid) { - proxy_error("Hostgroup %u has no servers ready for GTID '%s:%ld'. Waiting for replication...\n", hid, gtid_uuid, gtid_trxid); - } else { - proxy_error("Hostgroup %u has no servers available%s! Checking servers shunned for more than %u second%s\n", hid, - (max_connections_reached ? " or max_connections reached for all servers" : ""), max_wait_sec, max_wait_sec == 1 ? "" : "s"); - } - } - for (j=0; jidx(j); - if (mysrvc->status==MYSQL_SERVER_STATUS_SHUNNED && mysrvc->shunned_automatic==true) { - if ((t - mysrvc->time_last_detected_error) > max_wait_sec) { - mysrvc->status=MYSQL_SERVER_STATUS_ONLINE; - mysrvc->shunned_automatic=false; - mysrvc->connect_ERR_at_time_last_detected_error=0; - mysrvc->time_last_detected_error=0; - // if a server is taken back online, consider it immediately - if ( mysrvc->current_latency_us < ( mysrvc->max_latency_us ? mysrvc->max_latency_us : mysql_thread___default_max_latency_ms*1000 ) ) { // consider the host only if not too far - if (gtid_trxid) { - if (MyHGM->gtid_exists(mysrvc, gtid_uuid, gtid_trxid)) { - sum+=mysrvc->weight; - TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); - mysrvcCandidates[num_candidates]=mysrvc; - num_candidates++; - } - } else { - if (max_lag_ms >= 0) { - if ((unsigned int)max_lag_ms >= mysrvc->aws_aurora_current_lag_us/1000) { - sum+=mysrvc->weight; - TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); - mysrvcCandidates[num_candidates]=mysrvc; - num_candidates++; - } - } else { - sum+=mysrvc->weight; - TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); - mysrvcCandidates[num_candidates]=mysrvc; - num_candidates++; - } - } - } - } - } - } - } - if (sum==0) { - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySrvC NULL because no backend ONLINE or with weight\n"); - if (l>32) { - free(mysrvcCandidates); - } -#ifdef TEST_AURORA - array_mysrvc_cands += num_candidates; -#endif // TEST_AURORA - return NULL; // if we reach here, we couldn't find any target - } - -/* - unsigned int New_sum=0; - unsigned int New_TotalUsedConn=0; - // we will now scan again to ignore overloaded servers - for (j=0; jConnectionsUsed->conns_length(); - if ((len * sum) <= (TotalUsedConn * mysrvc->weight * 1.5 + 1)) { - - New_sum+=mysrvc->weight; - New_TotalUsedConn+=len; - } else { - // remove the candidate - if (j+1 < num_candidates) { - mysrvcCandidates[j] = mysrvcCandidates[num_candidates-1]; - } - j--; - num_candidates--; - } - } -*/ - - unsigned int New_sum=sum; - - if (New_sum==0) { - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySrvC NULL because no backend ONLINE or with weight\n"); - if (l>32) { - free(mysrvcCandidates); - } -#ifdef TEST_AURORA - array_mysrvc_cands += num_candidates; -#endif // TEST_AURORA - return NULL; // if we reach here, we couldn't find any target - } - - // latency awareness algorithm is enabled only when compiled with USE_MYSRVC_ARRAY - if (sess && sess->thread->variables.min_num_servers_lantency_awareness) { - if ((int) num_candidates >= sess->thread->variables.min_num_servers_lantency_awareness) { - unsigned int servers_with_latency = 0; - unsigned int total_latency_us = 0; - // scan and verify that all servers have some latency - for (j=0; jcurrent_latency_us) { - servers_with_latency++; - total_latency_us += mysrvc->current_latency_us; - } - } - if (servers_with_latency == num_candidates) { - // all servers have some latency. - // That is good. If any server have no latency, something is wrong - // and we will skip this algorithm - sess->thread->status_variables.stvar[st_var_ConnPool_get_conn_latency_awareness]++; - unsigned int avg_latency_us = 0; - avg_latency_us = total_latency_us/num_candidates; - for (j=0; jcurrent_latency_us > avg_latency_us) { - // remove the candidate - if (j+1 < num_candidates) { - mysrvcCandidates[j] = mysrvcCandidates[num_candidates-1]; - } - j--; - num_candidates--; - } - } - // we scan again to adjust weight - New_sum = 0; - for (j=0; jweight; - } - } - } - } - - - unsigned int k; - if (New_sum > 32768) { - k=rand()%New_sum; - } else { - k=fastrand()%New_sum; - } - k++; - New_sum=0; - - for (j=0; jweight; - if (k<=New_sum) { - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySrvC %p, server %s:%d\n", mysrvc, mysrvc->address, mysrvc->port); - if (l>32) { - free(mysrvcCandidates); - } -#ifdef TEST_AURORA - array_mysrvc_cands += num_candidates; -#endif // TEST_AURORA - return mysrvc; - } - } - } else { - time_t t = time(NULL); - - if (t - last_hg_log > 1) { - last_hg_log = time(NULL); - proxy_error("Hostgroup %u has no servers available!\n", hid); - } - } - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySrvC NULL\n"); - if (l>32) { - free(mysrvcCandidates); - } -#ifdef TEST_AURORA - array_mysrvc_cands += num_candidates; -#endif // TEST_AURORA - return NULL; // if we reach here, we couldn't find any target -} - -//unsigned int MySrvList::cnt() { -// return servers->len; -//} - -//MySrvC * MySrvList::idx(unsigned int i) { return (MySrvC *)servers->index(i); } - -void MySrvConnList::get_random_MyConn_inner_search(unsigned int start, unsigned int end, unsigned int& conn_found_idx, unsigned int& connection_quality_level, unsigned int& number_of_matching_session_variables, const MySQL_Connection * client_conn) { - char *schema = client_conn->userinfo->schemaname; - MySQL_Connection * conn=NULL; - unsigned int k; - for (k = start; k < end; k++) { - conn = (MySQL_Connection *)conns->index(k); - if (conn->match_tracked_options(client_conn)) { - if (connection_quality_level == 0) { - // this is our best candidate so far - connection_quality_level = 1; - conn_found_idx = k; - } - if (conn->requires_CHANGE_USER(client_conn)==false) { - if (connection_quality_level == 1) { - // this is our best candidate so far - connection_quality_level = 2; - conn_found_idx = k; - } - unsigned int cnt_match = 0; // number of matching session variables - unsigned int not_match = 0; // number of not matching session variables - cnt_match = conn->number_of_matching_session_variables(client_conn, not_match); - if (strcmp(conn->userinfo->schemaname,schema)==0) { - cnt_match++; - } else { - not_match++; - } - if (not_match==0) { - // it seems we found the perfect connection - number_of_matching_session_variables = cnt_match; - connection_quality_level = 3; - conn_found_idx = k; - return; // exit immediately, we found the perfect connection - } else { - // we didn't find the perfect connection - // but maybe is better than what we have so far? - if (cnt_match > number_of_matching_session_variables) { - // this is our best candidate so far - number_of_matching_session_variables = cnt_match; - conn_found_idx = k; - } - } - } else { - if (connection_quality_level == 1) { - int rca = mysql_thread___reset_connection_algorithm; - if (rca==1) { - int ql = GloMTH->variables.connpoll_reset_queue_length; - if (ql==0) { - // if: - // mysql-reset_connection_algorithm=1 and - // mysql-connpoll_reset_queue_length=0 - // we will not return a connection with connection_quality_level == 1 - // because we want to run COM_CHANGE_USER - // This change was introduced to work around Galera bug - // https://github.com/codership/galera/issues/613 - connection_quality_level = 0; - } - } - } - } - } - } -} - - - -MySQL_Connection * MySrvConnList::get_random_MyConn(MySQL_Session *sess, bool ff) { - MySQL_Connection * conn=NULL; - unsigned int i; - unsigned int conn_found_idx; - unsigned int l=conns_length(); - unsigned int connection_quality_level = 0; - bool needs_warming = false; - // connection_quality_level: - // 0 : not found any good connection, tracked options are not OK - // 1 : tracked options are OK , but CHANGE USER is required - // 2 : tracked options are OK , CHANGE USER is not required, but some SET statement or INIT_DB needs to be executed - // 3 : tracked options are OK , CHANGE USER is not required, and it seems that SET statements or INIT_DB ARE not required - unsigned int number_of_matching_session_variables = 0; // this includes session variables AND schema - bool connection_warming = mysql_thread___connection_warming; - int free_connections_pct = mysql_thread___free_connections_pct; - if (mysrvc->myhgc->attributes.configured == true) { - // mysql_hostgroup_attributes takes priority - connection_warming = mysrvc->myhgc->attributes.connection_warming; - free_connections_pct = mysrvc->myhgc->attributes.free_connections_pct; - } - if (connection_warming == true) { - unsigned int total_connections = mysrvc->ConnectionsFree->conns_length()+mysrvc->ConnectionsUsed->conns_length(); - unsigned int expected_warm_connections = free_connections_pct*mysrvc->max_connections/100; - if (total_connections < expected_warm_connections) { - needs_warming = true; - } - } - if (l && ff==false && needs_warming==false) { - if (l>32768) { - i=rand()%l; - } else { - i=fastrand()%l; - } - if (sess && sess->client_myds && sess->client_myds->myconn && sess->client_myds->myconn->userinfo) { - MySQL_Connection * client_conn = sess->client_myds->myconn; - get_random_MyConn_inner_search(i, l, conn_found_idx, connection_quality_level, number_of_matching_session_variables, client_conn); - if (connection_quality_level !=3 ) { // we didn't find the perfect connection - get_random_MyConn_inner_search(0, i, conn_found_idx, connection_quality_level, number_of_matching_session_variables, client_conn); - } - // connection_quality_level: - // 1 : tracked options are OK , but CHANGE USER is required - // 2 : tracked options are OK , CHANGE USER is not required, but some SET statement or INIT_DB needs to be executed - switch (connection_quality_level) { - case 0: // not found any good connection, tracked options are not OK - // we must check if connections need to be freed before - // creating a new connection - { - unsigned int conns_free = mysrvc->ConnectionsFree->conns_length(); - unsigned int conns_used = mysrvc->ConnectionsUsed->conns_length(); - unsigned int pct_max_connections = (3 * mysrvc->max_connections) / 4; - unsigned int connections_to_free = 0; - - if (conns_free >= 1) { - // connection cleanup is triggered when connections exceed 3/4 of the total - // allowed max connections, this cleanup ensures that at least *one connection* - // will be freed. - if (pct_max_connections <= (conns_free + conns_used)) { - connections_to_free = (conns_free + conns_used) - pct_max_connections; - if (connections_to_free == 0) connections_to_free = 1; - } - - while (conns_free && connections_to_free) { - MySQL_Connection* conn = mysrvc->ConnectionsFree->remove(0); - delete conn; - - conns_free = mysrvc->ConnectionsFree->conns_length(); - connections_to_free -= 1; - } - } - - // we must create a new connection - conn = new MySQL_Connection(); - conn->parent=mysrvc; - // if attributes.multiplex == true , STATUS_MYSQL_CONNECTION_NO_MULTIPLEX_HG is set to false. And vice-versa - conn->set_status(!conn->parent->myhgc->attributes.multiplex, STATUS_MYSQL_CONNECTION_NO_MULTIPLEX_HG); - __sync_fetch_and_add(&MyHGM->status.server_connections_created, 1); - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySQL Connection %p, server %s:%d\n", conn, conn->parent->address, conn->parent->port); - } - break; - case 1: //tracked options are OK , but CHANGE USER is required - // we may consider creating a new connection - { - unsigned int conns_free = mysrvc->ConnectionsFree->conns_length(); - unsigned int conns_used = mysrvc->ConnectionsUsed->conns_length(); - if ((conns_used > conns_free) && (mysrvc->max_connections > (conns_free/2 + conns_used/2)) ) { - conn = new MySQL_Connection(); - conn->parent=mysrvc; - // if attributes.multiplex == true , STATUS_MYSQL_CONNECTION_NO_MULTIPLEX_HG is set to false. And vice-versa - conn->set_status(!conn->parent->myhgc->attributes.multiplex, STATUS_MYSQL_CONNECTION_NO_MULTIPLEX_HG); - __sync_fetch_and_add(&MyHGM->status.server_connections_created, 1); - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySQL Connection %p, server %s:%d\n", conn, conn->parent->address, conn->parent->port); - } else { - conn=(MySQL_Connection *)conns->remove_index_fast(conn_found_idx); - } - } - break; - case 2: // tracked options are OK , CHANGE USER is not required, but some SET statement or INIT_DB needs to be executed - case 3: // tracked options are OK , CHANGE USER is not required, and it seems that SET statements or INIT_DB ARE not required - // here we return the best connection we have, no matter if connection_quality_level is 2 or 3 - conn=(MySQL_Connection *)conns->remove_index_fast(conn_found_idx); - break; - default: // this should never happen - // LCOV_EXCL_START - assert(0); - break; - // LCOV_EXCL_STOP - } - } else { - conn=(MySQL_Connection *)conns->remove_index_fast(i); - } - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySQL Connection %p, server %s:%d\n", conn, conn->parent->address, conn->parent->port); - return conn; - } else { - unsigned long long curtime = monotonic_time(); - curtime = curtime / 1000 / 1000; // convert to second - MyHGC *_myhgc = mysrvc->myhgc; - if (curtime > _myhgc->current_time_now) { - _myhgc->current_time_now = curtime; - _myhgc->new_connections_now = 0; - } - _myhgc->new_connections_now++; - unsigned int throttle_connections_per_sec_to_hostgroup = (unsigned int) mysql_thread___throttle_connections_per_sec_to_hostgroup; - if (_myhgc->attributes.configured == true) { - // mysql_hostgroup_attributes takes priority - throttle_connections_per_sec_to_hostgroup = _myhgc->attributes.throttle_connections_per_sec; - } - if (_myhgc->new_connections_now > (unsigned int) throttle_connections_per_sec_to_hostgroup) { - __sync_fetch_and_add(&MyHGM->status.server_connections_delayed, 1); - return NULL; - } else { - conn = new MySQL_Connection(); - conn->parent=mysrvc; - // if attributes.multiplex == true , STATUS_MYSQL_CONNECTION_NO_MULTIPLEX_HG is set to false. And vice-versa - conn->set_status(!conn->parent->myhgc->attributes.multiplex, STATUS_MYSQL_CONNECTION_NO_MULTIPLEX_HG); - __sync_fetch_and_add(&MyHGM->status.server_connections_created, 1); - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySQL Connection %p, server %s:%d\n", conn, conn->parent->address, conn->parent->port); - return conn; - } - } - return NULL; // never reach here -} - void MySQL_HostGroups_Manager::unshun_server_all_hostgroups(const char * address, uint16_t port, time_t t, int max_wait_sec, unsigned int *skip_hid) { // we scan all hostgroups looking for a specific server to unshun // if skip_hid is not NULL , the specific hostgroup is skipped diff --git a/lib/MySrvC.cpp b/lib/MySrvC.cpp new file mode 100644 index 0000000000..94401e9016 --- /dev/null +++ b/lib/MySrvC.cpp @@ -0,0 +1,198 @@ +#include "MySQL_HostGroups_Manager.h" +/* +#include "proxysql.h" +#include "cpp.h" + +#include "MySQL_PreparedStatement.h" +#include "MySQL_Data_Stream.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include "prometheus_helpers.h" +#include "proxysql_utils.h" + +#define char_malloc (char *)malloc +#define itostr(__s, __i) { __s=char_malloc(32); sprintf(__s, "%lld", __i); } + +#include "thread.h" +#include "wqueue.h" + +#include "ev.h" + +#include +#include +#include + +using std::function; + +#ifdef TEST_AURORA +static unsigned long long array_mysrvc_total = 0; +static unsigned long long array_mysrvc_cands = 0; +#endif // TEST_AURORA + +#define SAFE_SQLITE3_STEP(_stmt) do {\ + do {\ + rc=(*proxy_sqlite3_step)(_stmt);\ + if (rc!=SQLITE_DONE) {\ + assert(rc==SQLITE_LOCKED);\ + usleep(100);\ + }\ + } while (rc!=SQLITE_DONE);\ +} while (0) + +extern ProxySQL_Admin *GloAdmin; + +extern MySQL_Threads_Handler *GloMTH; + +extern MySQL_Monitor *GloMyMon; +*/ + +class MySrvConnList; +class MySrvC; +class MySrvList; +class MyHGC; + +MySrvC::MySrvC( + char* add, uint16_t p, uint16_t gp, int64_t _weight, enum MySerStatus _status, unsigned int _compression, + int64_t _max_connections, unsigned int _max_replication_lag, int32_t _use_ssl, unsigned int _max_latency_ms, + char* _comment +) { + address=strdup(add); + port=p; + gtid_port=gp; + weight=_weight; + status=_status; + compression=_compression; + max_connections=_max_connections; + max_replication_lag=_max_replication_lag; + use_ssl=_use_ssl; + cur_replication_lag=0; + cur_replication_lag_count=0; + max_latency_us=_max_latency_ms*1000; + current_latency_us=0; + aws_aurora_current_lag_us = 0; + connect_OK=0; + connect_ERR=0; + queries_sent=0; + bytes_sent=0; + bytes_recv=0; + max_connections_used=0; + queries_gtid_sync=0; + time_last_detected_error=0; + connect_ERR_at_time_last_detected_error=0; + shunned_automatic=false; + shunned_and_kill_all_connections=false; // false to default + //charset=_charset; + myhgc=NULL; + comment=strdup(_comment); + ConnectionsUsed=new MySrvConnList(this); + ConnectionsFree=new MySrvConnList(this); +} + +void MySrvC::connect_error(int err_num, bool get_mutex) { + // NOTE: this function operates without any mutex + // although, it is not extremely important if any counter is lost + // as a single connection failure won't make a significant difference + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Connect failed with code '%d'\n", err_num); + __sync_fetch_and_add(&connect_ERR,1); + __sync_fetch_and_add(&MyHGM->status.server_connections_aborted,1); + if (err_num >= 1048 && err_num <= 1052) + return; + if (err_num >= 1054 && err_num <= 1075) + return; + if (err_num >= 1099 && err_num <= 1104) + return; + if (err_num >= 1106 && err_num <= 1113) + return; + if (err_num >= 1116 && err_num <= 1118) + return; + if (err_num == 1136 || (err_num >= 1138 && err_num <= 1149)) + return; + switch (err_num) { + case 1007: // Can't create database + case 1008: // Can't drop database + case 1044: // access denied + case 1045: // access denied +/* + case 1048: // Column cannot be null + case 1049: // Unknown database + case 1050: // Table already exists + case 1051: // Unknown table + case 1052: // Column is ambiguous +*/ + case 1120: + case 1203: // User %s already has more than 'max_user_connections' active connections + case 1226: // User '%s' has exceeded the '%s' resource (current value: %ld) + case 3118: // Access denied for user '%s'. Account is locked.. + return; + break; + default: + break; + } + time_t t=time(NULL); + if (t > time_last_detected_error) { + time_last_detected_error=t; + connect_ERR_at_time_last_detected_error=1; + } else { + if (t < time_last_detected_error) { + // time_last_detected_error is in the future + // this means that monitor has a ping interval too big and tuned that in the future + return; + } + // same time + /** + * @brief The expected configured retries set by 'mysql-connect_retries_on_failure' + '2' extra expected + * connection errors. + * @details This two extra connections errors are expected: + * 1. An initial connection error generated by the datastream and the connection when being created, + * this is, right after the session has requested a connection to the connection pool. This error takes + * places directly in the state machine from 'MySQL_Connection'. Because of this, we consider this + * additional error to be a consequence of the two states machines, and it's not considered for + * 'connect_retries'. + * 2. A second connection connection error, which is the initial connection error generated by 'MySQL_Session' + * when already in the 'CONNECTING_SERVER' state. This error is an 'extra error' to always consider, since + * it's not part of the retries specified by 'mysql_thread___connect_retries_on_failure', thus, we set the + * 'connect_retries' to be 'mysql_thread___connect_retries_on_failure + 1'. + */ + int connect_retries = mysql_thread___connect_retries_on_failure + 1; + int max_failures = mysql_thread___shun_on_failures > connect_retries ? connect_retries : mysql_thread___shun_on_failures; + + if (__sync_add_and_fetch(&connect_ERR_at_time_last_detected_error,1) >= (unsigned int)max_failures) { + bool _shu=false; + if (get_mutex==true) + MyHGM->wrlock(); // to prevent race conditions, lock here. See #627 + if (status==MYSQL_SERVER_STATUS_ONLINE) { + status=MYSQL_SERVER_STATUS_SHUNNED; + shunned_automatic=true; + _shu=true; + } else { + _shu=false; + } + if (get_mutex==true) + MyHGM->wrunlock(); + if (_shu) { + proxy_error("Shunning server %s:%d with %u errors/sec. Shunning for %u seconds\n", address, port, connect_ERR_at_time_last_detected_error , mysql_thread___shun_recovery_time_sec); + } + } + } +} + +void MySrvC::shun_and_killall() { + status=MYSQL_SERVER_STATUS_SHUNNED; + shunned_automatic=true; + shunned_and_kill_all_connections=true; +} + +MySrvC::~MySrvC() { + if (address) free(address); + if (comment) free(comment); + delete ConnectionsUsed; + delete ConnectionsFree; +} diff --git a/lib/MySrvConnList.cpp b/lib/MySrvConnList.cpp new file mode 100644 index 0000000000..abe0c44ee7 --- /dev/null +++ b/lib/MySrvConnList.cpp @@ -0,0 +1,256 @@ +#include "MySQL_HostGroups_Manager.h" + +#include "MySQL_Data_Stream.h" + +extern ProxySQL_Admin *GloAdmin; + +extern MySQL_Threads_Handler *GloMTH; + +extern MySQL_Monitor *GloMyMon; + +class MySrvConnList; +class MySrvC; +class MySrvList; +class MyHGC; + +MySQL_Connection *MySrvConnList::index(unsigned int _k) { + return (MySQL_Connection *)conns->index(_k); +} + +MySQL_Connection * MySrvConnList::remove(int _k) { + return (MySQL_Connection *)conns->remove_index_fast(_k); +} + +MySrvConnList::MySrvConnList(MySrvC *_mysrvc) { + mysrvc=_mysrvc; + conns=new PtrArray(); +} + +void MySrvConnList::add(MySQL_Connection *c) { + conns->add(c); +} + +MySrvConnList::~MySrvConnList() { + mysrvc=NULL; + while (conns_length()) { + MySQL_Connection *conn=(MySQL_Connection *)conns->remove_index_fast(0); + delete conn; + } + delete conns; +} + +void MySrvConnList::drop_all_connections() { + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Dropping all connections (%u total) on MySrvConnList %p for server %s:%d , hostgroup=%d , status=%d\n", conns_length(), this, mysrvc->address, mysrvc->port, mysrvc->myhgc->hid, mysrvc->status); + while (conns_length()) { + MySQL_Connection *conn=(MySQL_Connection *)conns->remove_index_fast(0); + delete conn; + } +} + +void MySrvConnList::get_random_MyConn_inner_search(unsigned int start, unsigned int end, unsigned int& conn_found_idx, unsigned int& connection_quality_level, unsigned int& number_of_matching_session_variables, const MySQL_Connection * client_conn) { + char *schema = client_conn->userinfo->schemaname; + MySQL_Connection * conn=NULL; + unsigned int k; + for (k = start; k < end; k++) { + conn = (MySQL_Connection *)conns->index(k); + if (conn->match_tracked_options(client_conn)) { + if (connection_quality_level == 0) { + // this is our best candidate so far + connection_quality_level = 1; + conn_found_idx = k; + } + if (conn->requires_CHANGE_USER(client_conn)==false) { + if (connection_quality_level == 1) { + // this is our best candidate so far + connection_quality_level = 2; + conn_found_idx = k; + } + unsigned int cnt_match = 0; // number of matching session variables + unsigned int not_match = 0; // number of not matching session variables + cnt_match = conn->number_of_matching_session_variables(client_conn, not_match); + if (strcmp(conn->userinfo->schemaname,schema)==0) { + cnt_match++; + } else { + not_match++; + } + if (not_match==0) { + // it seems we found the perfect connection + number_of_matching_session_variables = cnt_match; + connection_quality_level = 3; + conn_found_idx = k; + return; // exit immediately, we found the perfect connection + } else { + // we didn't find the perfect connection + // but maybe is better than what we have so far? + if (cnt_match > number_of_matching_session_variables) { + // this is our best candidate so far + number_of_matching_session_variables = cnt_match; + conn_found_idx = k; + } + } + } else { + if (connection_quality_level == 1) { + int rca = mysql_thread___reset_connection_algorithm; + if (rca==1) { + int ql = GloMTH->variables.connpoll_reset_queue_length; + if (ql==0) { + // if: + // mysql-reset_connection_algorithm=1 and + // mysql-connpoll_reset_queue_length=0 + // we will not return a connection with connection_quality_level == 1 + // because we want to run COM_CHANGE_USER + // This change was introduced to work around Galera bug + // https://github.com/codership/galera/issues/613 + connection_quality_level = 0; + } + } + } + } + } + } +} + + + +MySQL_Connection * MySrvConnList::get_random_MyConn(MySQL_Session *sess, bool ff) { + MySQL_Connection * conn=NULL; + unsigned int i; + unsigned int conn_found_idx; + unsigned int l=conns_length(); + unsigned int connection_quality_level = 0; + bool needs_warming = false; + // connection_quality_level: + // 0 : not found any good connection, tracked options are not OK + // 1 : tracked options are OK , but CHANGE USER is required + // 2 : tracked options are OK , CHANGE USER is not required, but some SET statement or INIT_DB needs to be executed + // 3 : tracked options are OK , CHANGE USER is not required, and it seems that SET statements or INIT_DB ARE not required + unsigned int number_of_matching_session_variables = 0; // this includes session variables AND schema + bool connection_warming = mysql_thread___connection_warming; + int free_connections_pct = mysql_thread___free_connections_pct; + if (mysrvc->myhgc->attributes.configured == true) { + // mysql_hostgroup_attributes takes priority + connection_warming = mysrvc->myhgc->attributes.connection_warming; + free_connections_pct = mysrvc->myhgc->attributes.free_connections_pct; + } + if (connection_warming == true) { + unsigned int total_connections = mysrvc->ConnectionsFree->conns_length()+mysrvc->ConnectionsUsed->conns_length(); + unsigned int expected_warm_connections = free_connections_pct*mysrvc->max_connections/100; + if (total_connections < expected_warm_connections) { + needs_warming = true; + } + } + if (l && ff==false && needs_warming==false) { + if (l>32768) { + i=rand()%l; + } else { + i=fastrand()%l; + } + if (sess && sess->client_myds && sess->client_myds->myconn && sess->client_myds->myconn->userinfo) { + MySQL_Connection * client_conn = sess->client_myds->myconn; + get_random_MyConn_inner_search(i, l, conn_found_idx, connection_quality_level, number_of_matching_session_variables, client_conn); + if (connection_quality_level !=3 ) { // we didn't find the perfect connection + get_random_MyConn_inner_search(0, i, conn_found_idx, connection_quality_level, number_of_matching_session_variables, client_conn); + } + // connection_quality_level: + // 1 : tracked options are OK , but CHANGE USER is required + // 2 : tracked options are OK , CHANGE USER is not required, but some SET statement or INIT_DB needs to be executed + switch (connection_quality_level) { + case 0: // not found any good connection, tracked options are not OK + // we must check if connections need to be freed before + // creating a new connection + { + unsigned int conns_free = mysrvc->ConnectionsFree->conns_length(); + unsigned int conns_used = mysrvc->ConnectionsUsed->conns_length(); + unsigned int pct_max_connections = (3 * mysrvc->max_connections) / 4; + unsigned int connections_to_free = 0; + + if (conns_free >= 1) { + // connection cleanup is triggered when connections exceed 3/4 of the total + // allowed max connections, this cleanup ensures that at least *one connection* + // will be freed. + if (pct_max_connections <= (conns_free + conns_used)) { + connections_to_free = (conns_free + conns_used) - pct_max_connections; + if (connections_to_free == 0) connections_to_free = 1; + } + + while (conns_free && connections_to_free) { + MySQL_Connection* conn = mysrvc->ConnectionsFree->remove(0); + delete conn; + + conns_free = mysrvc->ConnectionsFree->conns_length(); + connections_to_free -= 1; + } + } + + // we must create a new connection + conn = new MySQL_Connection(); + conn->parent=mysrvc; + // if attributes.multiplex == true , STATUS_MYSQL_CONNECTION_NO_MULTIPLEX_HG is set to false. And vice-versa + conn->set_status(!conn->parent->myhgc->attributes.multiplex, STATUS_MYSQL_CONNECTION_NO_MULTIPLEX_HG); + __sync_fetch_and_add(&MyHGM->status.server_connections_created, 1); + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySQL Connection %p, server %s:%d\n", conn, conn->parent->address, conn->parent->port); + } + break; + case 1: //tracked options are OK , but CHANGE USER is required + // we may consider creating a new connection + { + unsigned int conns_free = mysrvc->ConnectionsFree->conns_length(); + unsigned int conns_used = mysrvc->ConnectionsUsed->conns_length(); + if ((conns_used > conns_free) && (mysrvc->max_connections > (conns_free/2 + conns_used/2)) ) { + conn = new MySQL_Connection(); + conn->parent=mysrvc; + // if attributes.multiplex == true , STATUS_MYSQL_CONNECTION_NO_MULTIPLEX_HG is set to false. And vice-versa + conn->set_status(!conn->parent->myhgc->attributes.multiplex, STATUS_MYSQL_CONNECTION_NO_MULTIPLEX_HG); + __sync_fetch_and_add(&MyHGM->status.server_connections_created, 1); + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySQL Connection %p, server %s:%d\n", conn, conn->parent->address, conn->parent->port); + } else { + conn=(MySQL_Connection *)conns->remove_index_fast(conn_found_idx); + } + } + break; + case 2: // tracked options are OK , CHANGE USER is not required, but some SET statement or INIT_DB needs to be executed + case 3: // tracked options are OK , CHANGE USER is not required, and it seems that SET statements or INIT_DB ARE not required + // here we return the best connection we have, no matter if connection_quality_level is 2 or 3 + conn=(MySQL_Connection *)conns->remove_index_fast(conn_found_idx); + break; + default: // this should never happen + // LCOV_EXCL_START + assert(0); + break; + // LCOV_EXCL_STOP + } + } else { + conn=(MySQL_Connection *)conns->remove_index_fast(i); + } + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySQL Connection %p, server %s:%d\n", conn, conn->parent->address, conn->parent->port); + return conn; + } else { + unsigned long long curtime = monotonic_time(); + curtime = curtime / 1000 / 1000; // convert to second + MyHGC *_myhgc = mysrvc->myhgc; + if (curtime > _myhgc->current_time_now) { + _myhgc->current_time_now = curtime; + _myhgc->new_connections_now = 0; + } + _myhgc->new_connections_now++; + unsigned int throttle_connections_per_sec_to_hostgroup = (unsigned int) mysql_thread___throttle_connections_per_sec_to_hostgroup; + if (_myhgc->attributes.configured == true) { + // mysql_hostgroup_attributes takes priority + throttle_connections_per_sec_to_hostgroup = _myhgc->attributes.throttle_connections_per_sec; + } + if (_myhgc->new_connections_now > (unsigned int) throttle_connections_per_sec_to_hostgroup) { + __sync_fetch_and_add(&MyHGM->status.server_connections_delayed, 1); + return NULL; + } else { + conn = new MySQL_Connection(); + conn->parent=mysrvc; + // if attributes.multiplex == true , STATUS_MYSQL_CONNECTION_NO_MULTIPLEX_HG is set to false. And vice-versa + conn->set_status(!conn->parent->myhgc->attributes.multiplex, STATUS_MYSQL_CONNECTION_NO_MULTIPLEX_HG); + __sync_fetch_and_add(&MyHGM->status.server_connections_created, 1); + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySQL Connection %p, server %s:%d\n", conn, conn->parent->address, conn->parent->port); + return conn; + } + } + return NULL; // never reach here +} + diff --git a/lib/MySrvList.cpp b/lib/MySrvList.cpp new file mode 100644 index 0000000000..22ed9b1427 --- /dev/null +++ b/lib/MySrvList.cpp @@ -0,0 +1,44 @@ +#include "MySQL_HostGroups_Manager.h" + +class MySrvConnList; +class MySrvC; +class MySrvList; +class MyHGC; + +MySrvList::MySrvList(MyHGC *_myhgc) { + myhgc=_myhgc; + servers=new PtrArray(); +} + +void MySrvList::add(MySrvC *s) { + if (s->myhgc==NULL) { + s->myhgc=myhgc; + } + servers->add(s); +} + + +int MySrvList::find_idx(MySrvC *s) { + for (unsigned int i=0; ilen; i++) { + MySrvC *mysrv=(MySrvC *)servers->index(i); + if (mysrv==s) { + return (unsigned int)i; + } + } + return -1; +} + +void MySrvList::remove(MySrvC *s) { + int i=find_idx(s); + assert(i>=0); + servers->remove_index_fast((unsigned int)i); +} + +MySrvList::~MySrvList() { + myhgc=NULL; + while (servers->len) { + MySrvC *mysrvc=(MySrvC *)servers->remove_index_fast(0); + delete mysrvc; + } + delete servers; +} From dad6f0f344617be1f965b89c1b348aa1ae113ac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Sun, 21 Apr 2024 14:10:08 +0000 Subject: [PATCH 08/79] Split of MySQL_Protocol.cpp into multiple files --- include/MySQL_Prepared_Stmt_info.h | 25 + include/MySQL_Protocol.h | 11 +- include/MySQL_encode.h | 22 + lib/Makefile | 1 + lib/MySQL_Protocol.cpp | 753 +---------------------------- lib/MySQL_ResultSet.cpp | 549 +++++++++++++++++++++ lib/MySQL_encode.cpp | 239 +++++++++ 7 files changed, 840 insertions(+), 760 deletions(-) create mode 100644 include/MySQL_Prepared_Stmt_info.h create mode 100644 include/MySQL_encode.h create mode 100644 lib/MySQL_ResultSet.cpp create mode 100644 lib/MySQL_encode.cpp diff --git a/include/MySQL_Prepared_Stmt_info.h b/include/MySQL_Prepared_Stmt_info.h new file mode 100644 index 0000000000..ad221720a5 --- /dev/null +++ b/include/MySQL_Prepared_Stmt_info.h @@ -0,0 +1,25 @@ +#ifndef CLASS_MySQL_Prepared_Stmt_info_H +#define CLASS_MySQL_Prepared_Stmt_info_H +class MySQL_Prepared_Stmt_info { + public: + uint32_t statement_id; + uint16_t num_columns; + uint16_t num_params; + uint16_t warning_count; + uint16_t pending_num_columns; + uint16_t pending_num_params; + MySQL_Prepared_Stmt_info(unsigned char *pkt, unsigned int length) { + pkt += 5; + statement_id = CPY4(pkt); + pkt += sizeof(uint32_t); + num_columns = CPY2(pkt); + pkt += sizeof(uint16_t); + num_params = CPY2(pkt); + pkt += sizeof(uint16_t); + pkt++; // reserved_1 + warning_count = CPY2(pkt); + pending_num_columns=num_columns; + pending_num_params=num_params; + } +}; +#endif // CLASS_MySQL_Prepared_Stmt_info_H diff --git a/include/MySQL_Protocol.h b/include/MySQL_Protocol.h index 0d30b223bf..a95251f8bf 100644 --- a/include/MySQL_Protocol.h +++ b/include/MySQL_Protocol.h @@ -4,6 +4,7 @@ #include "proxysql.h" #include "cpp.h" #include "MySQL_Variables.h" +#include "MySQL_Prepared_Stmt_info.h" #define RESULTSET_BUFLEN 16300 @@ -70,16 +71,6 @@ class MySQL_ResultSet { unsigned long long current_size(); }; -class MySQL_Prepared_Stmt_info { - public: - uint32_t statement_id; - uint16_t num_columns; - uint16_t num_params; - uint16_t warning_count; - uint16_t pending_num_columns; - uint16_t pending_num_params; - MySQL_Prepared_Stmt_info(unsigned char *, unsigned int); -}; uint8_t mysql_decode_length(unsigned char *ptr, uint64_t *len); diff --git a/include/MySQL_encode.h b/include/MySQL_encode.h new file mode 100644 index 0000000000..27273d6127 --- /dev/null +++ b/include/MySQL_encode.h @@ -0,0 +1,22 @@ +#ifndef CLASS_MySQL_encode_H +#define CLASS_MySQL_encode_H +#ifdef DEBUG +void __dump_pkt(const char *func, unsigned char *_ptr, unsigned int len); +#endif // DEBUG +char *sha1_pass_hex(char *sha1_pass); +double proxy_my_rnd(struct rand_struct *rand_st); +void proxy_create_random_string(char *_to, uint length, struct rand_struct *rand_st); +int write_encoded_length(unsigned char *p, uint64_t val, uint8_t len, char prefix); +int write_encoded_length_and_string(unsigned char *p, uint64_t val, uint8_t len, char prefix, char *string); +void proxy_compute_sha1_hash_multi(uint8_t *digest, const char *buf1, int len1, const char *buf2, int len2); +void proxy_compute_sha1_hash(uint8_t *digest, const char *buf, int len); +void proxy_compute_two_stage_sha1_hash(const char *password, size_t pass_len, uint8_t *hash_stage1, uint8_t *hash_stage2); +void proxy_my_crypt(char *to, const uint8_t *s1, const uint8_t *s2, uint len); +unsigned char decode_char(char x); +void unhex_pass(uint8_t *out, const char *in); +void proxy_scramble(char *to, const char *message, const char *password); +bool proxy_scramble_sha1(char *pass_reply, const char *message, const char *sha1_sha1_pass, char *sha1_pass); +unsigned int CPY3(unsigned char *ptr); +uint64_t CPY8(unsigned char *ptr); +uint8_t mysql_encode_length(uint64_t len, char *hd); +#endif // CLASS_MySQL_encode_H diff --git a/lib/Makefile b/lib/Makefile index 8ab3daea5f..58b17c29eb 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -129,6 +129,7 @@ _OBJ_CXX := ProxySQL_GloVars.oo network.oo debug.oo configfile.oo Query_Cache.oo sha256crypt.oo \ QP_rule_text.oo QP_query_digest_stats.oo \ GTID_Server_Data.oo MyHGC.oo MySrvConnList.oo MySrvList.oo MySrvC.oo \ + MySQL_encode.oo MySQL_ResultSet.oo \ proxysql_find_charset.oo ProxySQL_Poll.oo OBJ_CXX := $(patsubst %,$(ODIR)/%,$(_OBJ_CXX)) HEADERS := ../include/*.h ../include/*.hpp diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index 527c87bc93..cba71c6a8a 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -26,8 +26,6 @@ extern ClickHouse_Authentication *GloClickHouseAuth; #undef max_allowed_packet #endif -//#define RESULTSET_BUFLEN 16300 - #ifndef CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA #define CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA 0x00200000 #endif @@ -43,240 +41,7 @@ static const char *plugins[3] = { "caching_sha2_password", }; -#ifdef DEBUG -static void __dump_pkt(const char *func, unsigned char *_ptr, unsigned int len) { - - if (GloVars.global.gdbg==0) return; - if (GloVars.global.gdbg_lvl[PROXY_DEBUG_MYSQL_PROTOCOL].verbosity < 8 ) return; - unsigned int i; - fprintf(stderr,"DUMP %d bytes FROM %s\n", len, func); - for(i = 0; i < len; i++) { - if(isprint(_ptr[i])) fprintf(stderr,"%c", _ptr[i]); else fprintf(stderr,"."); - if (i>0 && (i%16==15 || i==len-1)) { - unsigned int j; - if (i%16!=15) { - j=15-i%16; - while (j--) fprintf(stderr," "); - } - fprintf(stderr," --- "); - for (j=(i==len-1 ? ((int)(i/16))*16 : i-15 ) ; j<=i; j++) { - fprintf(stderr,"%02x ", _ptr[j]); - } - fprintf(stderr,"\n"); - } - } - fprintf(stderr,"\n\n"); - - -} -#endif - -char *sha1_pass_hex(char *sha1_pass) { - if (sha1_pass==NULL) return NULL; - char *buff=(char *)malloc(SHA_DIGEST_LENGTH*2+2); - buff[0]='*'; - buff[SHA_DIGEST_LENGTH*2+1]='\0'; - int i; - uint8_t a = 0; - for (i=0;iseed1= (rand_st->seed1*3+rand_st->seed2) % rand_st->max_value; - rand_st->seed2= (rand_st->seed1+rand_st->seed2+33) % rand_st->max_value; - return (((double) rand_st->seed1) / rand_st->max_value_dbl); -} - -void proxy_create_random_string(char *_to, uint length, struct rand_struct *rand_st) { - unsigned char * to = (unsigned char *)_to; - int rc = 0; - uint i; - rc = RAND_bytes((unsigned char *)to,length); -#ifdef DEBUG - if (rc==1) { - // For code coverage (to test the following code and other function) - // in DEBUG mode we pretend that RAND_bytes() fails 1% of the time - if(rand()%100==0) { - rc=0; - } - } -#endif // DEBUG - if (rc!=1) { - for (i=0; i 127) { - *to -= 128; - } - if (*to == 0) { - *to = 'a'; - } - to++; - } - } - *to= '\0'; -} - -static inline int write_encoded_length(unsigned char *p, uint64_t val, uint8_t len, char prefix) { - if (len==1) { - *p=(char)val; - return 1; - } - *p=prefix; - p++; - memcpy(p,&val,len-1); - return len; -} - -static inline int write_encoded_length_and_string(unsigned char *p, uint64_t val, uint8_t len, char prefix, char *string) { - int l=write_encoded_length(p,val,len,prefix); - if (val) { - memcpy(p+l,string,val); - } - return l+val; -} - -void proxy_compute_sha1_hash_multi(uint8_t *digest, const char *buf1, int len1, const char *buf2, int len2) { - PROXY_TRACE(); - const EVP_MD *evp_digest = EVP_get_digestbyname("sha1"); - assert(evp_digest != NULL); - EVP_MD_CTX *ctx = EVP_MD_CTX_new(); - EVP_MD_CTX_init(ctx); - EVP_DigestInit_ex(ctx, evp_digest, NULL); - EVP_DigestUpdate(ctx, buf1, len1); - EVP_DigestUpdate(ctx, buf2, len2); - unsigned int olen = 0; - EVP_DigestFinal(ctx, digest, &olen); - EVP_MD_CTX_free(ctx); -} - -void proxy_compute_sha1_hash(uint8_t *digest, const char *buf, int len) { - PROXY_TRACE(); - const EVP_MD *evp_digest = EVP_get_digestbyname("sha1"); - assert(evp_digest != NULL); - EVP_MD_CTX *ctx = EVP_MD_CTX_new(); - EVP_MD_CTX_init(ctx); - EVP_DigestInit_ex(ctx, evp_digest, NULL); - EVP_DigestUpdate(ctx, buf, len); - unsigned int olen = 0; - EVP_DigestFinal(ctx, digest, &olen); - EVP_MD_CTX_free(ctx); -} - -void proxy_compute_two_stage_sha1_hash(const char *password, size_t pass_len, uint8_t *hash_stage1, uint8_t *hash_stage2) { - proxy_compute_sha1_hash(hash_stage1, password, pass_len); - proxy_compute_sha1_hash(hash_stage2, (const char *) hash_stage1, SHA_DIGEST_LENGTH); -} - -void proxy_my_crypt(char *to, const uint8_t *s1, const uint8_t *s2, uint len) { - const uint8_t *s1_end= s1 + len; - while (s1 < s1_end) - *to++= *s1++ ^ *s2++; -} - -unsigned char decode_char(char x) { - if (x >= '0' && x <= '9') - return (x - 0x30); - else if (x >= 'A' && x <= 'F') - return(x - 0x37); - else if (x >= 'a' && x <= 'f') - return(x - 0x57); - else { - proxy_error("Invalid char"); - return 0; - } -} - -void unhex_pass(uint8_t *out, const char *in) { - int i=0; - for (i=0;ibuffer + myrs->buffer_used; myrs->buffer_used += size; } - memcpy(_ptr, &myhdr, sizeof(mysql_hdr)); - int l=sizeof(mysql_hdr); + memcpy(_ptr, &myhdr, sizeof(mysql_hdr)); + int l=sizeof(mysql_hdr); _ptr[l]=0xfe; l++; int16_t internal_status = status; if (sess) { @@ -2977,501 +2725,6 @@ bool MySQL_Protocol::generate_COM_QUERY_from_COM_FIELD_LIST(PtrSize_t *pkt) { return true; } -MySQL_ResultSet::MySQL_ResultSet() { - buffer = NULL; - //reset_pid = true; -} - -void MySQL_ResultSet::buffer_init(MySQL_Protocol* myproto) { - if (buffer==NULL) { - buffer=(unsigned char *)malloc(RESULTSET_BUFLEN); - } - - buffer_used=0; - myprot = myproto; -} - -void MySQL_ResultSet::init(MySQL_Protocol *_myprot, MYSQL_RES *_res, MYSQL *_my, MYSQL_STMT *_stmt) { - PROXY_TRACE2(); - transfer_started=false; - resultset_completed=false; - myprot=_myprot; - mysql=_my; - stmt=_stmt; - if (buffer==NULL) { - //if (_stmt==NULL) { // we allocate this buffer only for not prepared statements - // removing the previous assumption. We allocate this buffer also for prepared statements - buffer=(unsigned char *)malloc(RESULTSET_BUFLEN); - //} - } - buffer_used=0; - myds=NULL; - if (myprot) { // if myprot = NULL , this is a mirror - myds=myprot->get_myds(); - } - //if (reset_pid==true) { - sid=0; - //PSarrayOUT = NULL; - if (myprot) { // if myprot = NULL , this is a mirror - sid=myds->pkt_sid+1; - //PSarrayOUT = new PtrSizeArray(8); - } - //} - //reset_pid=true; - result=_res; - resultset_size=0; - num_rows=0; - num_fields=mysql_field_count(mysql); - PtrSize_t pkt; - // immediately generate the first set of packets - // columns count - if (myprot==NULL) { - return; // this is a mirror - } - MySQL_Data_Stream * c_myds = *(myprot->myds); - if (c_myds->com_field_list==false) { - myprot->generate_pkt_column_count(false,&pkt.ptr,&pkt.size,sid,num_fields,this); - sid++; - resultset_size+=pkt.size; - } - // columns description - for (unsigned int i=0; icom_field_list==false) { - // we are replacing generate_pkt_field() with a more efficient version - //myprot->generate_pkt_field(false,&pkt.ptr,&pkt.size,sid,field->db,field->table,field->org_table,field->name,field->org_name,field->charsetnr,field->length,field->type,field->flags,field->decimals,false,0,NULL,this); - myprot->generate_pkt_field2(&pkt.ptr,&pkt.size,sid,field,this); - resultset_size+=pkt.size; - sid++; - } else { - if (c_myds->com_field_wild==NULL || mywildcmp(c_myds->com_field_wild,field->name)) { - myprot->generate_pkt_field(false,&pkt.ptr,&pkt.size,sid,field->db,field->table,field->org_table,field->name,field->org_name,field->charsetnr,field->length,field->type,field->flags,field->decimals,true,4,(char *)"null",this); - resultset_size+=pkt.size; - sid++; - } - } - } - - deprecate_eof_active = c_myds->myconn && (c_myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF); - - // first EOF - unsigned int nTrx=myds->sess->NumActiveTransactions(); - uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); - if (myds->sess->autocommit) setStatus += SERVER_STATUS_AUTOCOMMIT; - setStatus |= ( mysql->server_status & ~SERVER_STATUS_AUTOCOMMIT ); // get flags from server_status but ignore autocommit - setStatus = setStatus & ~SERVER_STATUS_CURSOR_EXISTS; // Do not send cursor #1128 -// if (_stmt) { // binary protocol , we also assume we have ALL the resultset -// myprot->generate_pkt_EOF(false,&pkt.ptr,&pkt.size,sid,0,mysql->server_status|setStatus); -// sid++; -// PSarrayOUT.add(pkt.ptr,pkt.size); -// resultset_size+=pkt.size; - //} else { - if (RESULTSET_BUFLEN <= (buffer_used + 9)) { - buffer_to_PSarrayOut(); - } - if (!deprecate_eof_active && myds->com_field_list==false) { - // up to 2.2.0 we used to add an EOF here. - // due to bug #3547 we move the logic into add_eof() that can now handle also prepared statements - PROXY_TRACE2(); - // if the backend server has CLIENT_DEPRECATE_EOF enabled, and the client does not support - // CLIENT_DEPRECATE_EOF, warning_count will be excluded from the intermediate EOF packet - add_eof((mysql->server_capabilities & CLIENT_DEPRECATE_EOF)); - } -} - - -// due to bug #3547 , in case of an error we remove the EOF -// and replace it with an ERR -// note that EOF is added on a packet on its own, instead of using a buffer, -// so that can be removed using remove_last_eof() -void MySQL_ResultSet::remove_last_eof() { - PROXY_TRACE2(); - PtrSize_t pkt; - if (PSarrayOUT.len) { - unsigned int l = PSarrayOUT.len-1; - PtrSize_t * pktp = PSarrayOUT.index(l); - if (pktp->size == 9) { - PROXY_TRACE2(); - PSarrayOUT.remove_index(l,&pkt); - l_free(pkt.size, pkt.ptr); - sid--; - } - } -} - -void MySQL_ResultSet::init_with_stmt(MySQL_Connection *myconn) { - PROXY_TRACE2(); - assert(stmt); - MYSQL_STMT *_stmt = stmt; - MySQL_Data_Stream * c_myds = *(myprot->myds); - buffer_to_PSarrayOut(); - unsigned long long total_size=0; - MYSQL_ROWS *r=_stmt->result.data; - if (r) { - total_size+=r->length; - if (r->length > 0xFFFFFF) { - total_size+=(r->length / 0xFFFFFF) * sizeof(mysql_hdr); - } - total_size+=sizeof(mysql_hdr); - while(r->next) { - r=r->next; - total_size+=r->length; - if (r->length > 0xFFFFFF) { - total_size+=(r->length / 0xFFFFFF) * sizeof(mysql_hdr); - } - total_size+=sizeof(mysql_hdr); - } -#define MAXBUFFSTMT 12*1024*1024 // hardcoded to LESS *very important* than 16MB - if (total_size < MAXBUFFSTMT) { - PtrSize_t pkt; - pkt.size=total_size; - pkt.ptr=malloc(pkt.size); - total_size=0; - r=_stmt->result.data; - add_row2(r,(unsigned char *)pkt.ptr); - total_size+=r->length; - if (r->length > 0xFFFFFF) { - total_size+=(r->length / 0xFFFFFF) * sizeof(mysql_hdr); - } - total_size+=sizeof(mysql_hdr); - while(r->next) { - r=r->next; - add_row2(r,(unsigned char *)pkt.ptr+total_size); - total_size+=r->length; - if (r->length > 0xFFFFFF) { - total_size+=(r->length / 0xFFFFFF) * sizeof(mysql_hdr); - } - total_size+=sizeof(mysql_hdr); - } - PSarrayOUT.add(pkt.ptr,pkt.size); - if (resultset_size/0xFFFFFFF != ((resultset_size+pkt.size)/0xFFFFFFF)) { - // generate a heartbeat every 256MB - unsigned long long curtime=monotonic_time(); - c_myds->sess->thread->atomic_curtime=curtime; - } - resultset_size+=pkt.size; - } else { // this code fixes a bug: resultset larger than 4GB would cause a crash - unsigned long long tmp_pkt_size = 0; - r=_stmt->result.data; - MYSQL_ROWS * r2 = NULL; - while (r) { - if (r->length >= MAXBUFFSTMT) { - // we have a large row - // we will send just that - tmp_pkt_size = r->length; - if (r->length > 0xFFFFFF) { - tmp_pkt_size+=(r->length / 0xFFFFFF) * sizeof(mysql_hdr); - } - tmp_pkt_size += sizeof(mysql_hdr); - PtrSize_t pkt; - pkt.size=tmp_pkt_size; - pkt.ptr=malloc(pkt.size); - add_row2(r,(unsigned char *)pkt.ptr); - PSarrayOUT.add(pkt.ptr,pkt.size); - if (resultset_size/0xFFFFFFF != ((resultset_size+pkt.size)/0xFFFFFFF)) { - // generate a heartbeat every 256MB - unsigned long long curtime=monotonic_time(); - c_myds->sess->thread->atomic_curtime=curtime; - } - resultset_size+=pkt.size; - r=r->next; // next row - } else { // we have small row - r2 = r; - tmp_pkt_size = 0; - unsigned int a = 0; - while (r && (tmp_pkt_size + r->length) < MAXBUFFSTMT) { - a++; - tmp_pkt_size += r->length; - tmp_pkt_size += sizeof(mysql_hdr); - //if (r->next) { - r = r->next; - //} - } - r = r2; // we reset it back to the beginning - if (tmp_pkt_size) { // this should always be true - unsigned long long tmp2 = 0; - PtrSize_t pkt; - pkt.size=tmp_pkt_size; - pkt.ptr=malloc(pkt.size); - while (tmp2 < tmp_pkt_size) { - add_row2(r,(unsigned char *)pkt.ptr+tmp2); - tmp2 += r->length; - tmp2 += sizeof(mysql_hdr); - r = r->next; - } - PSarrayOUT.add(pkt.ptr,pkt.size); - if (resultset_size/0xFFFFFFF != ((resultset_size+pkt.size)/0xFFFFFFF)) { - // generate a heartbeat every 256MB - unsigned long long curtime=monotonic_time(); - c_myds->sess->thread->atomic_curtime=curtime; - } - resultset_size+=pkt.size; - } - } - } - } - } - // up to 2.2.0 we were always adding an EOF - // due to bug #3547 , in case of an error we remove the EOF - // and replace it with an ERR - // note that EOF is added on a packet on its own, instead of using a buffer, - // so that can be removed - // - // NOTE: After 2.4.5 previous behavior is modified in favor of the following: - // - // When CLIENT_DEPRECATE_EOF two EOF packets are two be expected in the response: - // 1. After the columns definitions (This is added directly by 'MySQL_ResultSet::init'). - // 2. After the rows values, this can either be and EOF packet or a ERR packet in case of error. - // - // First EOF packet isn't optional, and it's just the second the one that is optionaly either an EOF - // or an ERR packet. The following code adds either the final EOF or ERR packet. This is equally valid - // for when CLIENT_DEPRECATE_EOF is enabled or not. If CLIENT_DEPRECATE_EOF is: - // * DISABLED: The behavior is as described before. - // * ENABLED: Code is identical for this case. The initial EOF packet is conditionally added by - // 'MySQL_ResultSet::init', thus, this packet should not be present if not needed at this point. - // In case of error an ERR packet needs to be added, otherwise `add_eof` handles the generation of - // the equivalent OK packet replacing the final EOF packet. - int myerr = mysql_stmt_errno(_stmt); - if (myerr) { - PROXY_TRACE2(); - add_err(myconn->myds); - } else { - PROXY_TRACE2(); - add_eof(); - } -} - -MySQL_ResultSet::~MySQL_ResultSet() { - PtrSize_t pkt; - //if (PSarrayOUT) { - while (PSarrayOUT.len) { - PSarrayOUT.remove_index_fast(0,&pkt); - l_free(pkt.size, pkt.ptr); - } - //delete PSarrayOUT; - //} - if (buffer) { - free(buffer); - buffer=NULL; - } - //if (myds) myds->pkt_sid=sid-1; -} - -// this function is used for binary protocol -// maybe later on can be adapted for text protocol too -unsigned int MySQL_ResultSet::add_row(MYSQL_ROWS *rows) { - unsigned int pkt_length=0; - MYSQL_ROW row = rows->data; - unsigned long row_length = rows->length; - // we call generate_pkt_row3 passing row_length - sid=myprot->generate_pkt_row3(this, &pkt_length, sid, 0, NULL, row, row_length); - sid++; - resultset_size+=pkt_length; - num_rows++; - return pkt_length; -} - - -// this function is used for text protocol -unsigned int MySQL_ResultSet::add_row(MYSQL_ROW row) { - unsigned long *lengths=mysql_fetch_lengths(result); - unsigned int pkt_length=0; - if (myprot) { - // we call generate_pkt_row3 without passing row_length - sid=myprot->generate_pkt_row3(this, &pkt_length, sid, num_fields, lengths, row, 0); - } else { - unsigned int col=0; - for (col=0; collength; - num_rows++; - uint8_t pkt_sid=sid; - if (length < (0xFFFFFF+sizeof(mysql_hdr))) { - mysql_hdr myhdr; - myhdr.pkt_length=length; - myhdr.pkt_id=pkt_sid; - memcpy(offset, &myhdr, sizeof(mysql_hdr)); - memcpy(offset+sizeof(mysql_hdr), row->data, row->length); - pkt_sid++; - } else { - unsigned int left=length; - unsigned int copied=0; - while (left>=0xFFFFFF) { - mysql_hdr myhdr; - myhdr.pkt_length=0xFFFFFF; - myhdr.pkt_id=pkt_sid; - pkt_sid++; - memcpy(offset, &myhdr, sizeof(mysql_hdr)); - offset+=sizeof(mysql_hdr); - char *o = (char *) row->data; - o += copied; - memcpy(offset, o, myhdr.pkt_length); - offset+=0xFFFFFF; - // we are writing a large packet (over 16MB), we assume we are always outside the buffer - copied+=0xFFFFFF; - left-=0xFFFFFF; - } - mysql_hdr myhdr; - myhdr.pkt_length=left; - myhdr.pkt_id=pkt_sid; - pkt_sid++; - memcpy(offset, &myhdr, sizeof(mysql_hdr)); - offset+=sizeof(mysql_hdr); - char *o = (char *) row->data; - o += copied; - memcpy(offset, o, myhdr.pkt_length); - // we are writing a large packet (over 16MB), we assume we are always outside the buffer - } - sid=pkt_sid; - return length; -} - -void MySQL_ResultSet::add_eof(bool suppress_warning_count) { - if (myprot) { - unsigned int nTrx=myds->sess->NumActiveTransactions(); - uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); - if (myds->sess->autocommit) setStatus += SERVER_STATUS_AUTOCOMMIT; - setStatus |= ( mysql->server_status & ~SERVER_STATUS_AUTOCOMMIT ); // get flags from server_status but ignore autocommit - setStatus = setStatus & ~SERVER_STATUS_CURSOR_EXISTS; // Do not send cursor #1128 - //myprot->generate_pkt_EOF(false,&pkt.ptr,&pkt.size,sid,0,mysql->server_status|setStatus); - //PSarrayOUT->add(pkt.ptr,pkt.size); - //sid++; - //resultset_size+=pkt.size; - - // Note: warnings count will only be sent to the client if mysql-query_digests is enabled - const MySQL_Backend* _mybe = myds->sess->mybe; - const MySQL_Data_Stream* _server_myds = (_mybe && _mybe->server_myds) ? _mybe->server_myds : nullptr; - const MySQL_Connection* _myconn = (_server_myds && _server_myds->myds_type == MYDS_BACKEND && _server_myds->myconn) ? - _server_myds->myconn : nullptr; - const unsigned int warning_count = (_myconn && suppress_warning_count == false) ? _myconn->warning_count : 0; - if (deprecate_eof_active) { - PtrSize_t pkt; - buffer_to_PSarrayOut(); - myprot->generate_pkt_OK(false, &pkt.ptr, &pkt.size, sid, 0, 0, setStatus, warning_count, NULL, true); - PSarrayOUT.add(pkt.ptr, pkt.size); - resultset_size += pkt.size; - } - else { - // due to bug #3547 , in case of an error we remove the EOF - // and replace it with an ERR - // note that EOF is added on a packet on its own, instead of using a buffer, - // so that can be removed using remove_last_eof() - buffer_to_PSarrayOut(); - myprot->generate_pkt_EOF(false, NULL, NULL, sid, warning_count, setStatus, this); - resultset_size += 9; - buffer_to_PSarrayOut(); - } - sid++; - } - resultset_completed=true; -} - -void MySQL_ResultSet::add_err(MySQL_Data_Stream *_myds) { - PtrSize_t pkt; - if (myprot) { - MYSQL *_mysql=_myds->myconn->mysql; - buffer_to_PSarrayOut(); - char sqlstate[10]; - sprintf(sqlstate,"%s",mysql_sqlstate(_mysql)); - if (_myds && _myds->killed_at) { // see case #750 - if (_myds->kill_type == 0) { - myprot->generate_pkt_ERR(false,&pkt.ptr,&pkt.size,sid,1907,sqlstate,(char *)"Query execution was interrupted, query_timeout exceeded"); - MyHGM->p_update_mysql_error_counter(p_mysql_error_type::proxysql, _myds->myconn->parent->myhgc->hid, _myds->myconn->parent->address, _myds->myconn->parent->port, 1907); - } else { - myprot->generate_pkt_ERR(false,&pkt.ptr,&pkt.size,sid,1317,sqlstate,(char *)"Query execution was interrupted"); - MyHGM->p_update_mysql_error_counter(p_mysql_error_type::proxysql, _myds->myconn->parent->myhgc->hid, _myds->myconn->parent->address, _myds->myconn->parent->port, 1317); - } - } else { - int myerr = 0; - // the error code is returned from: - // - mysql_stmt_errno() if using a prepared statement - // - mysql_errno() if not using a prepared statement - if (stmt) { - myerr = mysql_stmt_errno(stmt); - myprot->generate_pkt_ERR(false,&pkt.ptr,&pkt.size,sid,myerr,sqlstate,mysql_stmt_error(stmt)); - } else { - myerr = mysql_errno(_mysql); - myprot->generate_pkt_ERR(false,&pkt.ptr,&pkt.size,sid,myerr,sqlstate,mysql_error(_mysql)); - } - // TODO: Check this is a mysql error - MyHGM->p_update_mysql_error_counter(p_mysql_error_type::mysql, _myds->myconn->parent->myhgc->hid, _myds->myconn->parent->address, _myds->myconn->parent->port, myerr); - } - PSarrayOUT.add(pkt.ptr,pkt.size); - sid++; - resultset_size+=pkt.size; - } - resultset_completed=true; -} - -/* -bool MySQL_ResultSet::get_COM_FIELD_LIST_response(PtrSizeArray *PSarrayFinal) { - transfer_started=true; - if (myprot) { - } - return resultset_completed; -} -*/ - -bool MySQL_ResultSet::get_resultset(PtrSizeArray *PSarrayFinal) { - transfer_started=true; - if (myprot) { - PSarrayFinal->copy_add(&PSarrayOUT,0,PSarrayOUT.len); - while (PSarrayOUT.len) - PSarrayOUT.remove_index(PSarrayOUT.len-1,NULL); - } - return resultset_completed; -} - -void MySQL_ResultSet::buffer_to_PSarrayOut(bool _last) { - if (buffer_used==0) - return; // exit immediately if the buffer is empty - if (buffer_used < RESULTSET_BUFLEN/2) { - if (_last == false) { - buffer=(unsigned char *)realloc(buffer,buffer_used); - } - } - PSarrayOUT.add(buffer,buffer_used); - if (_last) { - buffer = NULL; - } else { - buffer=(unsigned char *)malloc(RESULTSET_BUFLEN); - } - buffer_used=0; -} - -unsigned long long MySQL_ResultSet::current_size() { - unsigned long long intsize=0; - intsize+=sizeof(MySQL_ResultSet); - intsize+=RESULTSET_BUFLEN; // size of buffer - if (PSarrayOUT.len==0) // see bug #699 - return intsize; - intsize+=sizeof(PtrSizeArray); - intsize+=(PSarrayOUT.size*sizeof(PtrSize_t *)); - unsigned int i; - for (i=0; isize>RESULTSET_BUFLEN) { - intsize+=pkt->size; - } else { - intsize+=RESULTSET_BUFLEN; - } - } - return intsize; -} - my_bool proxy_mysql_stmt_close(MYSQL_STMT* stmt) { // Clean internal structures for 'stmt->mysql->stmts'. if (stmt->mysql) { diff --git a/lib/MySQL_ResultSet.cpp b/lib/MySQL_ResultSet.cpp new file mode 100644 index 0000000000..87e713aa76 --- /dev/null +++ b/lib/MySQL_ResultSet.cpp @@ -0,0 +1,549 @@ +#include +#include "proxysql.h" +#include "cpp.h" +#include "re2/re2.h" +#include "re2/regexp.h" + +#include "MySQL_PreparedStatement.h" +#include "MySQL_Data_Stream.h" +#include "MySQL_Authentication.hpp" +#include "MySQL_LDAP_Authentication.hpp" +#include "MySQL_Variables.h" + +#include + +//#include + +extern MySQL_Authentication *GloMyAuth; +extern MySQL_LDAP_Authentication *GloMyLdapAuth; +extern MySQL_Threads_Handler *GloMTH; + +#ifdef PROXYSQLCLICKHOUSE +extern ClickHouse_Authentication *GloClickHouseAuth; +#endif /* PROXYSQLCLICKHOUSE */ + +#ifdef max_allowed_packet +#undef max_allowed_packet +#endif + +#ifndef CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA +#define CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA 0x00200000 +#endif + +#include "proxysql_find_charset.h" + + +extern "C" char * sha256_crypt_r (const char *key, const char *salt, char *buffer, int buflen); + + +uint8_t mysql_encode_length(uint64_t len, char *hd); + + +MySQL_ResultSet::MySQL_ResultSet() { + buffer = NULL; + //reset_pid = true; +} + +void MySQL_ResultSet::buffer_init(MySQL_Protocol* myproto) { + if (buffer==NULL) { + buffer=(unsigned char *)malloc(RESULTSET_BUFLEN); + } + + buffer_used=0; + myprot = myproto; +} + +void MySQL_ResultSet::init(MySQL_Protocol *_myprot, MYSQL_RES *_res, MYSQL *_my, MYSQL_STMT *_stmt) { + PROXY_TRACE2(); + transfer_started=false; + resultset_completed=false; + myprot=_myprot; + mysql=_my; + stmt=_stmt; + if (buffer==NULL) { + //if (_stmt==NULL) { // we allocate this buffer only for not prepared statements + // removing the previous assumption. We allocate this buffer also for prepared statements + buffer=(unsigned char *)malloc(RESULTSET_BUFLEN); + //} + } + buffer_used=0; + myds=NULL; + if (myprot) { // if myprot = NULL , this is a mirror + myds=myprot->get_myds(); + } + //if (reset_pid==true) { + sid=0; + //PSarrayOUT = NULL; + if (myprot) { // if myprot = NULL , this is a mirror + sid=myds->pkt_sid+1; + //PSarrayOUT = new PtrSizeArray(8); + } + //} + //reset_pid=true; + result=_res; + resultset_size=0; + num_rows=0; + num_fields=mysql_field_count(mysql); + PtrSize_t pkt; + // immediately generate the first set of packets + // columns count + if (myprot==NULL) { + return; // this is a mirror + } + MySQL_Data_Stream * c_myds = *(myprot->myds); + if (c_myds->com_field_list==false) { + myprot->generate_pkt_column_count(false,&pkt.ptr,&pkt.size,sid,num_fields,this); + sid++; + resultset_size+=pkt.size; + } + // columns description + for (unsigned int i=0; icom_field_list==false) { + // we are replacing generate_pkt_field() with a more efficient version + //myprot->generate_pkt_field(false,&pkt.ptr,&pkt.size,sid,field->db,field->table,field->org_table,field->name,field->org_name,field->charsetnr,field->length,field->type,field->flags,field->decimals,false,0,NULL,this); + myprot->generate_pkt_field2(&pkt.ptr,&pkt.size,sid,field,this); + resultset_size+=pkt.size; + sid++; + } else { + if (c_myds->com_field_wild==NULL || mywildcmp(c_myds->com_field_wild,field->name)) { + myprot->generate_pkt_field(false,&pkt.ptr,&pkt.size,sid,field->db,field->table,field->org_table,field->name,field->org_name,field->charsetnr,field->length,field->type,field->flags,field->decimals,true,4,(char *)"null",this); + resultset_size+=pkt.size; + sid++; + } + } + } + + deprecate_eof_active = c_myds->myconn && (c_myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF); + + // first EOF + unsigned int nTrx=myds->sess->NumActiveTransactions(); + uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); + if (myds->sess->autocommit) setStatus += SERVER_STATUS_AUTOCOMMIT; + setStatus |= ( mysql->server_status & ~SERVER_STATUS_AUTOCOMMIT ); // get flags from server_status but ignore autocommit + setStatus = setStatus & ~SERVER_STATUS_CURSOR_EXISTS; // Do not send cursor #1128 +// if (_stmt) { // binary protocol , we also assume we have ALL the resultset +// myprot->generate_pkt_EOF(false,&pkt.ptr,&pkt.size,sid,0,mysql->server_status|setStatus); +// sid++; +// PSarrayOUT.add(pkt.ptr,pkt.size); +// resultset_size+=pkt.size; + //} else { + if (RESULTSET_BUFLEN <= (buffer_used + 9)) { + buffer_to_PSarrayOut(); + } + if (!deprecate_eof_active && myds->com_field_list==false) { + // up to 2.2.0 we used to add an EOF here. + // due to bug #3547 we move the logic into add_eof() that can now handle also prepared statements + PROXY_TRACE2(); + // if the backend server has CLIENT_DEPRECATE_EOF enabled, and the client does not support + // CLIENT_DEPRECATE_EOF, warning_count will be excluded from the intermediate EOF packet + add_eof((mysql->server_capabilities & CLIENT_DEPRECATE_EOF)); + } +} + + +// due to bug #3547 , in case of an error we remove the EOF +// and replace it with an ERR +// note that EOF is added on a packet on its own, instead of using a buffer, +// so that can be removed using remove_last_eof() +void MySQL_ResultSet::remove_last_eof() { + PROXY_TRACE2(); + PtrSize_t pkt; + if (PSarrayOUT.len) { + unsigned int l = PSarrayOUT.len-1; + PtrSize_t * pktp = PSarrayOUT.index(l); + if (pktp->size == 9) { + PROXY_TRACE2(); + PSarrayOUT.remove_index(l,&pkt); + l_free(pkt.size, pkt.ptr); + sid--; + } + } +} + +void MySQL_ResultSet::init_with_stmt(MySQL_Connection *myconn) { + PROXY_TRACE2(); + assert(stmt); + MYSQL_STMT *_stmt = stmt; + MySQL_Data_Stream * c_myds = *(myprot->myds); + buffer_to_PSarrayOut(); + unsigned long long total_size=0; + MYSQL_ROWS *r=_stmt->result.data; + if (r) { + total_size+=r->length; + if (r->length > 0xFFFFFF) { + total_size+=(r->length / 0xFFFFFF) * sizeof(mysql_hdr); + } + total_size+=sizeof(mysql_hdr); + while(r->next) { + r=r->next; + total_size+=r->length; + if (r->length > 0xFFFFFF) { + total_size+=(r->length / 0xFFFFFF) * sizeof(mysql_hdr); + } + total_size+=sizeof(mysql_hdr); + } +#define MAXBUFFSTMT 12*1024*1024 // hardcoded to LESS *very important* than 16MB + if (total_size < MAXBUFFSTMT) { + PtrSize_t pkt; + pkt.size=total_size; + pkt.ptr=malloc(pkt.size); + total_size=0; + r=_stmt->result.data; + add_row2(r,(unsigned char *)pkt.ptr); + total_size+=r->length; + if (r->length > 0xFFFFFF) { + total_size+=(r->length / 0xFFFFFF) * sizeof(mysql_hdr); + } + total_size+=sizeof(mysql_hdr); + while(r->next) { + r=r->next; + add_row2(r,(unsigned char *)pkt.ptr+total_size); + total_size+=r->length; + if (r->length > 0xFFFFFF) { + total_size+=(r->length / 0xFFFFFF) * sizeof(mysql_hdr); + } + total_size+=sizeof(mysql_hdr); + } + PSarrayOUT.add(pkt.ptr,pkt.size); + if (resultset_size/0xFFFFFFF != ((resultset_size+pkt.size)/0xFFFFFFF)) { + // generate a heartbeat every 256MB + unsigned long long curtime=monotonic_time(); + c_myds->sess->thread->atomic_curtime=curtime; + } + resultset_size+=pkt.size; + } else { // this code fixes a bug: resultset larger than 4GB would cause a crash + unsigned long long tmp_pkt_size = 0; + r=_stmt->result.data; + MYSQL_ROWS * r2 = NULL; + while (r) { + if (r->length >= MAXBUFFSTMT) { + // we have a large row + // we will send just that + tmp_pkt_size = r->length; + if (r->length > 0xFFFFFF) { + tmp_pkt_size+=(r->length / 0xFFFFFF) * sizeof(mysql_hdr); + } + tmp_pkt_size += sizeof(mysql_hdr); + PtrSize_t pkt; + pkt.size=tmp_pkt_size; + pkt.ptr=malloc(pkt.size); + add_row2(r,(unsigned char *)pkt.ptr); + PSarrayOUT.add(pkt.ptr,pkt.size); + if (resultset_size/0xFFFFFFF != ((resultset_size+pkt.size)/0xFFFFFFF)) { + // generate a heartbeat every 256MB + unsigned long long curtime=monotonic_time(); + c_myds->sess->thread->atomic_curtime=curtime; + } + resultset_size+=pkt.size; + r=r->next; // next row + } else { // we have small row + r2 = r; + tmp_pkt_size = 0; + unsigned int a = 0; + while (r && (tmp_pkt_size + r->length) < MAXBUFFSTMT) { + a++; + tmp_pkt_size += r->length; + tmp_pkt_size += sizeof(mysql_hdr); + //if (r->next) { + r = r->next; + //} + } + r = r2; // we reset it back to the beginning + if (tmp_pkt_size) { // this should always be true + unsigned long long tmp2 = 0; + PtrSize_t pkt; + pkt.size=tmp_pkt_size; + pkt.ptr=malloc(pkt.size); + while (tmp2 < tmp_pkt_size) { + add_row2(r,(unsigned char *)pkt.ptr+tmp2); + tmp2 += r->length; + tmp2 += sizeof(mysql_hdr); + r = r->next; + } + PSarrayOUT.add(pkt.ptr,pkt.size); + if (resultset_size/0xFFFFFFF != ((resultset_size+pkt.size)/0xFFFFFFF)) { + // generate a heartbeat every 256MB + unsigned long long curtime=monotonic_time(); + c_myds->sess->thread->atomic_curtime=curtime; + } + resultset_size+=pkt.size; + } + } + } + } + } + // up to 2.2.0 we were always adding an EOF + // due to bug #3547 , in case of an error we remove the EOF + // and replace it with an ERR + // note that EOF is added on a packet on its own, instead of using a buffer, + // so that can be removed + // + // NOTE: After 2.4.5 previous behavior is modified in favor of the following: + // + // When CLIENT_DEPRECATE_EOF two EOF packets are two be expected in the response: + // 1. After the columns definitions (This is added directly by 'MySQL_ResultSet::init'). + // 2. After the rows values, this can either be and EOF packet or a ERR packet in case of error. + // + // First EOF packet isn't optional, and it's just the second the one that is optionaly either an EOF + // or an ERR packet. The following code adds either the final EOF or ERR packet. This is equally valid + // for when CLIENT_DEPRECATE_EOF is enabled or not. If CLIENT_DEPRECATE_EOF is: + // * DISABLED: The behavior is as described before. + // * ENABLED: Code is identical for this case. The initial EOF packet is conditionally added by + // 'MySQL_ResultSet::init', thus, this packet should not be present if not needed at this point. + // In case of error an ERR packet needs to be added, otherwise `add_eof` handles the generation of + // the equivalent OK packet replacing the final EOF packet. + int myerr = mysql_stmt_errno(_stmt); + if (myerr) { + PROXY_TRACE2(); + add_err(myconn->myds); + } else { + PROXY_TRACE2(); + add_eof(); + } +} + +MySQL_ResultSet::~MySQL_ResultSet() { + PtrSize_t pkt; + //if (PSarrayOUT) { + while (PSarrayOUT.len) { + PSarrayOUT.remove_index_fast(0,&pkt); + l_free(pkt.size, pkt.ptr); + } + //delete PSarrayOUT; + //} + if (buffer) { + free(buffer); + buffer=NULL; + } + //if (myds) myds->pkt_sid=sid-1; +} + +// this function is used for binary protocol +// maybe later on can be adapted for text protocol too +unsigned int MySQL_ResultSet::add_row(MYSQL_ROWS *rows) { + unsigned int pkt_length=0; + MYSQL_ROW row = rows->data; + unsigned long row_length = rows->length; + // we call generate_pkt_row3 passing row_length + sid=myprot->generate_pkt_row3(this, &pkt_length, sid, 0, NULL, row, row_length); + sid++; + resultset_size+=pkt_length; + num_rows++; + return pkt_length; +} + + +// this function is used for text protocol +unsigned int MySQL_ResultSet::add_row(MYSQL_ROW row) { + unsigned long *lengths=mysql_fetch_lengths(result); + unsigned int pkt_length=0; + if (myprot) { + // we call generate_pkt_row3 without passing row_length + sid=myprot->generate_pkt_row3(this, &pkt_length, sid, num_fields, lengths, row, 0); + } else { + unsigned int col=0; + for (col=0; collength; + num_rows++; + uint8_t pkt_sid=sid; + if (length < (0xFFFFFF+sizeof(mysql_hdr))) { + mysql_hdr myhdr; + myhdr.pkt_length=length; + myhdr.pkt_id=pkt_sid; + memcpy(offset, &myhdr, sizeof(mysql_hdr)); + memcpy(offset+sizeof(mysql_hdr), row->data, row->length); + pkt_sid++; + } else { + unsigned int left=length; + unsigned int copied=0; + while (left>=0xFFFFFF) { + mysql_hdr myhdr; + myhdr.pkt_length=0xFFFFFF; + myhdr.pkt_id=pkt_sid; + pkt_sid++; + memcpy(offset, &myhdr, sizeof(mysql_hdr)); + offset+=sizeof(mysql_hdr); + char *o = (char *) row->data; + o += copied; + memcpy(offset, o, myhdr.pkt_length); + offset+=0xFFFFFF; + // we are writing a large packet (over 16MB), we assume we are always outside the buffer + copied+=0xFFFFFF; + left-=0xFFFFFF; + } + mysql_hdr myhdr; + myhdr.pkt_length=left; + myhdr.pkt_id=pkt_sid; + pkt_sid++; + memcpy(offset, &myhdr, sizeof(mysql_hdr)); + offset+=sizeof(mysql_hdr); + char *o = (char *) row->data; + o += copied; + memcpy(offset, o, myhdr.pkt_length); + // we are writing a large packet (over 16MB), we assume we are always outside the buffer + } + sid=pkt_sid; + return length; +} + +void MySQL_ResultSet::add_eof(bool suppress_warning_count) { + if (myprot) { + unsigned int nTrx=myds->sess->NumActiveTransactions(); + uint16_t setStatus = (nTrx ? SERVER_STATUS_IN_TRANS : 0 ); + if (myds->sess->autocommit) setStatus += SERVER_STATUS_AUTOCOMMIT; + setStatus |= ( mysql->server_status & ~SERVER_STATUS_AUTOCOMMIT ); // get flags from server_status but ignore autocommit + setStatus = setStatus & ~SERVER_STATUS_CURSOR_EXISTS; // Do not send cursor #1128 + //myprot->generate_pkt_EOF(false,&pkt.ptr,&pkt.size,sid,0,mysql->server_status|setStatus); + //PSarrayOUT->add(pkt.ptr,pkt.size); + //sid++; + //resultset_size+=pkt.size; + + // Note: warnings count will only be sent to the client if mysql-query_digests is enabled + const MySQL_Backend* _mybe = myds->sess->mybe; + const MySQL_Data_Stream* _server_myds = (_mybe && _mybe->server_myds) ? _mybe->server_myds : nullptr; + const MySQL_Connection* _myconn = (_server_myds && _server_myds->myds_type == MYDS_BACKEND && _server_myds->myconn) ? + _server_myds->myconn : nullptr; + const unsigned int warning_count = (_myconn && suppress_warning_count == false) ? _myconn->warning_count : 0; + if (deprecate_eof_active) { + PtrSize_t pkt; + buffer_to_PSarrayOut(); + myprot->generate_pkt_OK(false, &pkt.ptr, &pkt.size, sid, 0, 0, setStatus, warning_count, NULL, true); + PSarrayOUT.add(pkt.ptr, pkt.size); + resultset_size += pkt.size; + } + else { + // due to bug #3547 , in case of an error we remove the EOF + // and replace it with an ERR + // note that EOF is added on a packet on its own, instead of using a buffer, + // so that can be removed using remove_last_eof() + buffer_to_PSarrayOut(); + myprot->generate_pkt_EOF(false, NULL, NULL, sid, warning_count, setStatus, this); + resultset_size += 9; + buffer_to_PSarrayOut(); + } + sid++; + } + resultset_completed=true; +} + +void MySQL_ResultSet::add_err(MySQL_Data_Stream *_myds) { + PtrSize_t pkt; + if (myprot) { + MYSQL *_mysql=_myds->myconn->mysql; + buffer_to_PSarrayOut(); + char sqlstate[10]; + sprintf(sqlstate,"%s",mysql_sqlstate(_mysql)); + if (_myds && _myds->killed_at) { // see case #750 + if (_myds->kill_type == 0) { + myprot->generate_pkt_ERR(false,&pkt.ptr,&pkt.size,sid,1907,sqlstate,(char *)"Query execution was interrupted, query_timeout exceeded"); + MyHGM->p_update_mysql_error_counter(p_mysql_error_type::proxysql, _myds->myconn->parent->myhgc->hid, _myds->myconn->parent->address, _myds->myconn->parent->port, 1907); + } else { + myprot->generate_pkt_ERR(false,&pkt.ptr,&pkt.size,sid,1317,sqlstate,(char *)"Query execution was interrupted"); + MyHGM->p_update_mysql_error_counter(p_mysql_error_type::proxysql, _myds->myconn->parent->myhgc->hid, _myds->myconn->parent->address, _myds->myconn->parent->port, 1317); + } + } else { + int myerr = 0; + // the error code is returned from: + // - mysql_stmt_errno() if using a prepared statement + // - mysql_errno() if not using a prepared statement + if (stmt) { + myerr = mysql_stmt_errno(stmt); + myprot->generate_pkt_ERR(false,&pkt.ptr,&pkt.size,sid,myerr,sqlstate,mysql_stmt_error(stmt)); + } else { + myerr = mysql_errno(_mysql); + myprot->generate_pkt_ERR(false,&pkt.ptr,&pkt.size,sid,myerr,sqlstate,mysql_error(_mysql)); + } + // TODO: Check this is a mysql error + MyHGM->p_update_mysql_error_counter(p_mysql_error_type::mysql, _myds->myconn->parent->myhgc->hid, _myds->myconn->parent->address, _myds->myconn->parent->port, myerr); + } + PSarrayOUT.add(pkt.ptr,pkt.size); + sid++; + resultset_size+=pkt.size; + } + resultset_completed=true; +} + +/* +bool MySQL_ResultSet::get_COM_FIELD_LIST_response(PtrSizeArray *PSarrayFinal) { + transfer_started=true; + if (myprot) { + } + return resultset_completed; +} +*/ + +bool MySQL_ResultSet::get_resultset(PtrSizeArray *PSarrayFinal) { + transfer_started=true; + if (myprot) { + PSarrayFinal->copy_add(&PSarrayOUT,0,PSarrayOUT.len); + while (PSarrayOUT.len) + PSarrayOUT.remove_index(PSarrayOUT.len-1,NULL); + } + return resultset_completed; +} + +void MySQL_ResultSet::buffer_to_PSarrayOut(bool _last) { + if (buffer_used==0) + return; // exit immediately if the buffer is empty + if (buffer_used < RESULTSET_BUFLEN/2) { + if (_last == false) { + buffer=(unsigned char *)realloc(buffer,buffer_used); + } + } + PSarrayOUT.add(buffer,buffer_used); + if (_last) { + buffer = NULL; + } else { + buffer=(unsigned char *)malloc(RESULTSET_BUFLEN); + } + buffer_used=0; +} + +unsigned long long MySQL_ResultSet::current_size() { + unsigned long long intsize=0; + intsize+=sizeof(MySQL_ResultSet); + intsize+=RESULTSET_BUFLEN; // size of buffer + if (PSarrayOUT.len==0) // see bug #699 + return intsize; + intsize+=sizeof(PtrSizeArray); + intsize+=(PSarrayOUT.size*sizeof(PtrSize_t *)); + unsigned int i; + for (i=0; isize>RESULTSET_BUFLEN) { + intsize+=pkt->size; + } else { + intsize+=RESULTSET_BUFLEN; + } + } + return intsize; +} + +/* +my_bool proxy_mysql_stmt_close(MYSQL_STMT* stmt) { + // Clean internal structures for 'stmt->mysql->stmts'. + if (stmt->mysql) { + stmt->mysql->stmts = + list_delete(stmt->mysql->stmts, &stmt->list); + } + // Nullify 'mysql' field to avoid sending a blocking command to the server. + stmt->mysql = NULL; + // Perform the regular close operation. + return mysql_stmt_close(stmt); +} +*/ diff --git a/lib/MySQL_encode.cpp b/lib/MySQL_encode.cpp new file mode 100644 index 0000000000..13a4f0461b --- /dev/null +++ b/lib/MySQL_encode.cpp @@ -0,0 +1,239 @@ +#include +#include "proxysql.h" +#include "cpp.h" + +#ifdef DEBUG +void __dump_pkt(const char *func, unsigned char *_ptr, unsigned int len) { + + if (GloVars.global.gdbg==0) return; + if (GloVars.global.gdbg_lvl[PROXY_DEBUG_MYSQL_PROTOCOL].verbosity < 8 ) return; + unsigned int i; + fprintf(stderr,"DUMP %d bytes FROM %s\n", len, func); + for(i = 0; i < len; i++) { + if(isprint(_ptr[i])) fprintf(stderr,"%c", _ptr[i]); else fprintf(stderr,"."); + if (i>0 && (i%16==15 || i==len-1)) { + unsigned int j; + if (i%16!=15) { + j=15-i%16; + while (j--) fprintf(stderr," "); + } + fprintf(stderr," --- "); + for (j=(i==len-1 ? ((int)(i/16))*16 : i-15 ) ; j<=i; j++) { + fprintf(stderr,"%02x ", _ptr[j]); + } + fprintf(stderr,"\n"); + } + } + fprintf(stderr,"\n\n"); + + +} +#endif + +char *sha1_pass_hex(char *sha1_pass) { + if (sha1_pass==NULL) return NULL; + char *buff=(char *)malloc(SHA_DIGEST_LENGTH*2+2); + buff[0]='*'; + buff[SHA_DIGEST_LENGTH*2+1]='\0'; + int i; + uint8_t a = 0; + for (i=0;iseed1= (rand_st->seed1*3+rand_st->seed2) % rand_st->max_value; + rand_st->seed2= (rand_st->seed1+rand_st->seed2+33) % rand_st->max_value; + return (((double) rand_st->seed1) / rand_st->max_value_dbl); +} + +void proxy_create_random_string(char *_to, uint length, struct rand_struct *rand_st) { + unsigned char * to = (unsigned char *)_to; + int rc = 0; + uint i; + rc = RAND_bytes((unsigned char *)to,length); +#ifdef DEBUG + if (rc==1) { + // For code coverage (to test the following code and other function) + // in DEBUG mode we pretend that RAND_bytes() fails 1% of the time + if(rand()%100==0) { + rc=0; + } + } +#endif // DEBUG + if (rc!=1) { + for (i=0; i 127) { + *to -= 128; + } + if (*to == 0) { + *to = 'a'; + } + to++; + } + } + *to= '\0'; +} + +int write_encoded_length(unsigned char *p, uint64_t val, uint8_t len, char prefix) { + if (len==1) { + *p=(char)val; + return 1; + } + *p=prefix; + p++; + memcpy(p,&val,len-1); + return len; +} + +int write_encoded_length_and_string(unsigned char *p, uint64_t val, uint8_t len, char prefix, char *string) { + int l=write_encoded_length(p,val,len,prefix); + if (val) { + memcpy(p+l,string,val); + } + return l+val; +} + +void proxy_compute_sha1_hash_multi(uint8_t *digest, const char *buf1, int len1, const char *buf2, int len2) { + PROXY_TRACE(); + const EVP_MD *evp_digest = EVP_get_digestbyname("sha1"); + assert(evp_digest != NULL); + EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + EVP_MD_CTX_init(ctx); + EVP_DigestInit_ex(ctx, evp_digest, NULL); + EVP_DigestUpdate(ctx, buf1, len1); + EVP_DigestUpdate(ctx, buf2, len2); + unsigned int olen = 0; + EVP_DigestFinal(ctx, digest, &olen); + EVP_MD_CTX_free(ctx); +} + +void proxy_compute_sha1_hash(uint8_t *digest, const char *buf, int len) { + PROXY_TRACE(); + const EVP_MD *evp_digest = EVP_get_digestbyname("sha1"); + assert(evp_digest != NULL); + EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + EVP_MD_CTX_init(ctx); + EVP_DigestInit_ex(ctx, evp_digest, NULL); + EVP_DigestUpdate(ctx, buf, len); + unsigned int olen = 0; + EVP_DigestFinal(ctx, digest, &olen); + EVP_MD_CTX_free(ctx); +} + +void proxy_compute_two_stage_sha1_hash(const char *password, size_t pass_len, uint8_t *hash_stage1, uint8_t *hash_stage2) { + proxy_compute_sha1_hash(hash_stage1, password, pass_len); + proxy_compute_sha1_hash(hash_stage2, (const char *) hash_stage1, SHA_DIGEST_LENGTH); +} + +void proxy_my_crypt(char *to, const uint8_t *s1, const uint8_t *s2, uint len) { + const uint8_t *s1_end= s1 + len; + while (s1 < s1_end) + *to++= *s1++ ^ *s2++; +} + +unsigned char decode_char(char x) { + if (x >= '0' && x <= '9') + return (x - 0x30); + else if (x >= 'A' && x <= 'F') + return(x - 0x37); + else if (x >= 'a' && x <= 'f') + return(x - 0x57); + else { + proxy_error("Invalid char"); + return 0; + } +} + +void unhex_pass(uint8_t *out, const char *in) { + int i=0; + for (i=0;i Date: Mon, 22 Apr 2024 02:02:24 +0000 Subject: [PATCH 09/79] Adding some clean up to test_digest_umap_aux-t Also modified tap to return the number of completed test --- test/tap/tap/tap.cpp | 4 ++++ test/tap/tap/tap.h | 6 ++++++ test/tap/tests/test_digest_umap_aux-t.cpp | 9 ++++++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/test/tap/tap/tap.cpp b/test/tap/tap/tap.cpp index 8afb7939cd..b067432a2d 100644 --- a/test/tap/tap/tap.cpp +++ b/test/tap/tap/tap.cpp @@ -415,6 +415,10 @@ int tests_failed() { return g_test.failed; } +int tests_last() { + return g_test.last; +} + /** @mainpage Testing C and C++ using MyTAP diff --git a/test/tap/tap/tap.h b/test/tap/tap/tap.h index 4b7e76354a..7678c1a390 100644 --- a/test/tap/tap/tap.h +++ b/test/tap/tap/tap.h @@ -311,6 +311,12 @@ void todo_end(); int tests_failed(); +/** + * @brief Return the number of tests that have passed. + */ + +int tests_last(); + /** @} */ #ifdef __cplusplus diff --git a/test/tap/tests/test_digest_umap_aux-t.cpp b/test/tap/tests/test_digest_umap_aux-t.cpp index 227f5af08d..22391e05b8 100644 --- a/test/tap/tests/test_digest_umap_aux-t.cpp +++ b/test/tap/tests/test_digest_umap_aux-t.cpp @@ -178,7 +178,8 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } - plan(1 + DUMMY_QUERIES.size() * 5); // always specify the number of tests that are going to be performed + int nplan = (1 + DUMMY_QUERIES.size() * 5); // always specify the number of tests that are going to be performed + plan(nplan); MYSQL *proxy_admin = mysql_init(NULL); if (!mysql_real_connect(proxy_admin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { @@ -314,6 +315,12 @@ int main(int argc, char** argv) { ); } + if (tests_last() == nplan && tests_failed == 0) { + string q = "TRUNCATE TABLE stats.stats_mysql_query_digest"; + diag("Running %s", q.c_str()); + MYSQL_QUERY(proxy_admin, q.c_str()); + } + mysql_close(proxy_admin); return exit_status(); From eb7a0e37f14f252c354644e7aa786297a2595977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Mon, 22 Apr 2024 02:24:01 +0000 Subject: [PATCH 10/79] Moved legacy code away from c_tokenizer.cpp mysql_query_digest_and_first_comment() moved into c_tokenizer_legacy.cpp . c_tokenizer_legacy.cpp is present but not compiled, here for reference only. --- include/c_tokenizer.h | 3 - lib/c_tokenizer.cpp | 588 ------------------------------------- lib/c_tokenizer_legacy.cpp | 588 +++++++++++++++++++++++++++++++++++++ 3 files changed, 588 insertions(+), 591 deletions(-) create mode 100644 lib/c_tokenizer_legacy.cpp diff --git a/include/c_tokenizer.h b/include/c_tokenizer.h index abe0fdb30d..4444ca2e6a 100644 --- a/include/c_tokenizer.h +++ b/include/c_tokenizer.h @@ -1,5 +1,3 @@ -/* c_tokenizer.h */ -// some code borrowed from http://www.cplusplus.com/faq/sequences/strings/split/ #pragma once #ifndef C_TOKENIZER_H @@ -34,7 +32,6 @@ const char* free_tokenizer( tokenizer_t* tokenizer ); const char* tokenize( tokenizer_t* tokenizer ); char * mysql_query_digest_first_stage(const char* const q, int q_len, char** const fst_cmnt, char* const buf); char * mysql_query_digest_second_stage(const char* const q, int q_len, char** const fst_cmnt, char* const buf); -char * mysql_query_digest_and_first_comment(char *s , int len , char **first_comment, char *buf); char * mysql_query_digest_and_first_comment_2(const char* const q, int q_len, char** const fst_cmnt, char* const buf); char * mysql_query_digest_and_first_comment_one_it(char *s , int len , char **first_comment, char *buf); char * mysql_query_strip_comments(char *s , int len); diff --git a/lib/c_tokenizer.cpp b/lib/c_tokenizer.cpp index c468e0b185..5b35ef0fb3 100644 --- a/lib/c_tokenizer.cpp +++ b/lib/c_tokenizer.cpp @@ -1,6 +1,3 @@ -/* c_tokenizer.c */ -// Borrowed from http://www.cplusplus.com/faq/sequences/strings/split/ - #include #include #include @@ -234,591 +231,6 @@ static inline void replace_with_q_mark( } } -char *mysql_query_digest_and_first_comment(char *s, int _len, char **first_comment, char *buf){ - int i = 0; - - char cur_comment[FIRST_COMMENT_MAX_LENGTH]; - cur_comment[0]=0; - int ccl=0; - int cmd=0; - - int len = _len; - if (_len > mysql_thread___query_digests_max_query_length) { - len = mysql_thread___query_digests_max_query_length; - } - char *r = buf; - if (r==NULL) { - r = (char *) malloc(len + SIZECHAR); - } - char *p_r = r; - char *p_r_t = r; - - char prev_char = 0; - char qutr_char = 0; - - char flag = 0; - char fc=0; - int fc_len=0; - - char fns=0; - - bool lowercase=0; - bool replace_null=0; - bool replace_number=0; - - char grouping_digest=0; - char grouping_limit_exceeded=0; - int grouping_count=0; - int grouping_lim = mysql_thread___query_digests_grouping_limit; - - lowercase=mysql_thread___query_digests_lowercase; - replace_null = mysql_thread___query_digests_replace_null; - replace_number = mysql_thread___query_digests_no_digits; - - while(i < len) - { - // Handy for debugging purposes - // ============================ - // printf( - // "state-1: { flag: `%d`, prev_char: `%c`, s: `%s`, p_r: `%s`, r: `%s`}\n", - // flag, prev_char, s, p_r, r - // ); - // ============================ - - // ================================================= - // START - read token char and set flag what's going on. - // ================================================= - if(flag == 0) - { - // store current position - p_r_t = p_r; - - // comment type 1 - start with '/*' - if(prev_char == '/' && *s == '*') - { - ccl=0; - flag = 1; - if (i != (len-1) && *(s+1)=='!') - cmd=1; - } - - // comment type 2 - start with '#' - else if(*s == '#') - { - flag = 2; - } - - // comment type 3 - start with '--' - - // NOTE: Looks like the general rule for parsing comments of this type could simply be: - // - // - `.*--.*` which could be translated into `(*s == '-' && *(s+1) == '-')`. - // - // But this can not hold, since the first '-' could have been consumed previously, for example - // during the parsing of a digit: - // - // - `select 1.1-- final_comment\n` - // - // For this reason 'prev_char' needs to be checked too when searching for the `--` pattern. - else if(i != (len-1) && prev_char == '-' && *s == '-' && ((*(s+1)==' ') || (*(s+1)=='\n') || (*(s+1)=='\r') || (*(s+1)=='\t') )) - { - flag = 3; - } - - // Previous character can be a consumed ' ' instead of '-' as in the previous case, for this - // reason, we need to look ahead for '--'. - // - // NOTE: There is no reason for not checking for the subsequent space char that should follow - // the '-- ', otherwise we would consider valid queries as `SELECT --1` like comments. - else if (i != (len-1) && *s == '-' && (*(s+1)=='-')) { - if (prev_char != '-') { - flag = 3; - } - else if (i==0) { - flag = 3; - } - } - - // string - start with ' - else if(*s == '\'' || *s == '"') - { - flag = 4; - qutr_char = *s; - } - - // may be digit - start with digit - else if(is_token_char(prev_char) && is_digit_char(*s)) - { - flag = 5; - if(len == i+1) - continue; - } - - // not above case - remove duplicated space char - else - { - flag = 0; - if (fns==0 && is_space_char(*s)) { - s++; - i++; - continue; - } - if (fns==0) fns=1; - if(is_space_char(prev_char) && is_space_char(*s)){ - prev_char = ' '; - *p_r = ' '; - s++; - i++; - continue; - } - if (replace_number) { - if (!is_digit_char(prev_char) && is_digit_char(*s)) { - *p_r++ = '?'; - while(*s != '\0' && is_digit_char(*s)) { - s++; - i++; - } - } - } - { - char* p = p_r - 2; - // suppress spaces before arithmetic operators - if (p >= r && is_space_char(prev_char) && is_arithmetic_op(*s)) { - if (*p == '?') { - prev_char = *s; - --p_r; - *p_r++ = *s; - s++; - i++; - continue; - } - } - // suppress spaces before and after commas - if (p >= r && is_space_char(prev_char) && ((*s == ',') || (*p == ','))) { - if (*s == ',') { - --p_r; - // only copy the comma if we are not grouping a query - if (!grouping_limit_exceeded) { - *p_r++ = *s; - } - prev_char = ','; - s++; - i++; - } else { - prev_char = ','; - --p_r; - } - continue; - } - // suppress spaces before closing brackets when grouping or mark is present - if (p >= r && (*p == '.' || *p == '?') && is_space_char(prev_char) && (*s == ')')) { - prev_char = *s; - --p_r; - *p_r++ = *s; - s++; - i++; - continue; - } - } - if (replace_null) { - if (*s == 'n' || *s == 'N') { // we search for NULL , #2171 - if (i && is_token_char(prev_char)) { - if (len>=4) { - if (i=2) fc_len-=2; - char *c=*first_comment+fc_len; - *c=0; - //*first_comment[fc_len]=0; - fc=2; - } - } - } - if( - // comment type 1 - /* .. */ - (flag == 1 && prev_char == '*' && *s == '/') || - - // comment type 2 - # ... \n - (flag == 2 && (*s == '\n' || *s == '\r' || (i == len - 1) )) - || - // comment type 3 - -- ... \n - (flag == 3 && (*s == '\n' || *s == '\r' || (i == len -1) )) - ) - { - p_r = p_r_t; - if (flag == 1 || (i == len -1)) { - p_r -= SIZECHAR; - } - if (cmd) { - cur_comment[ccl]=0; - if (ccl>=2) { - ccl-=2; - cur_comment[ccl]=0; - char el=0; - int fcc=0; - while (el==0 && fcc= r && ( *(_p+2) == '-' || *(_p+2) == '+') ) { - if ( - ( *(_p+1) == ',' ) || ( *(_p+1) == '(' ) || - ( ( *(_p+1) == ' ' ) && ( *_p == ',' || *_p == '(' ) ) - ) { - p_r--; - } - } - - replace_with_q_mark( - grouping_digest, grouping_lim, &grouping_count, &p_r, &grouping_limit_exceeded - ); - - flag = 0; - break; - } - - // need to be ignored case - if(p_r > p_r_t + SIZECHAR) - { - if( - (prev_char == '\\' && *s == '\\') || // to process '\\\\', '\\' - (prev_char == '\\' && *s == qutr_char) || // to process '\'' - (prev_char == qutr_char && *s == qutr_char) // to process '''' - ) - { - prev_char = 'X'; - s++; - i++; - continue; - } - } - - // satisfied closing string - swap string to ? - if(*s == qutr_char && (len == i+1 || *(s + SIZECHAR) != qutr_char)) - { - char *_p = p_r_t; - _p-=3; - p_r = p_r_t; - if ( _p >= r && ( *(_p+2) == '-' || *(_p+2) == '+') ) { - if ( - ( *(_p+1) == ',' ) || ( *(_p+1) == '(' ) || - ( ( *(_p+1) == ' ' ) && ( *_p == ',' || *_p == '(' ) ) - ) { - p_r--; - } - } - - // Remove spaces before each literal found - if ( _p >= r && is_space_char(*(_p + 2)) && !is_normal_char(*(_p + 1))) { - if ( _p >= r && ( *(_p+3) == '\''|| *(_p+3) == '"' )) { - p_r--; - } - } - - replace_with_q_mark( - grouping_digest, grouping_lim, &grouping_count, &p_r, &grouping_limit_exceeded - ); - - prev_char = qutr_char; - qutr_char = 0; - flag = 0; - if(i < len) - s++; - i++; - continue; - } - } - - // -------- - // digit - // -------- - else if(flag == 5) - { - // last single char - if(p_r_t == p_r) - { - char *_p = p_r_t; - _p-=3; - if ( _p >= r && ( *(_p+2) == '-' || *(_p+2) == '+') ) { - if ( - ( *(_p+1) == ',' ) || ( *(_p+1) == '(' ) || - ( ( *(_p+1) == ' ' ) && ( *_p == ',' || *_p == '(' ) ) - ) { - p_r--; - } - } - if ( _p >= r && is_space_char(*(_p + 2))) { - if ( _p >= r && ( *(_p+1) == '-' || *(_p+1) == '+' || *(_p+1) == '*' || *(_p+1) == '/' || *(_p+1) == '%' || *(_p+1) == ',')) { - p_r--; - } - } - *p_r++ = '?'; - i++; - continue; - } - - // is float - if (*s == '.' || *s == 'e' || ((*s == '+' || *s == '-') && prev_char == 'e')) { - prev_char = *s; - i++; - s++; - continue; - } - - // token char or last char - if(is_token_char(*s) || len == i+1) - { - if(is_digit_string(p_r_t, p_r)) - { - char *_p = p_r_t; - _p-=3; - p_r = p_r_t; - // remove symbol and keep parenthesis or comma - if ( _p >= r && ( *(_p+2) == '-' || *(_p+2) == '+') ) { - if ( - ( *(_p+1) == ',' ) || ( *(_p+1) == '(' ) || - ( ( *(_p+1) == ' ' ) && ( *_p == ',' || *_p == '(' ) ) - ) { - p_r--; - } - } - - // Remove spaces before number counting with possible '.' presence - if (_p >= r && *_p == '.' && (*(_p + 1) == ' ' || *(_p + 1) == '.') && (*(_p+2) == '-' || *(_p+2) == '+') ) { - if (*(_p + 1) == ' ') { - p_r--; - } - p_r--; - } - - // Remove spaces after a opening bracket when followed by a number - if (_p >= r && *(_p+1) == '(' && *(_p+2) == ' ') { - p_r--; - } - - // Remove spaces before number - if ( _p >= r && is_space_char(*(_p + 2))) { - // A point can be found prior to a number in case of query grouping - if ( _p >= r && ( *(_p+1) == '-' || *(_p+1) == '+' || *(_p+1) == '*' || *(_p+1) == '/' || *(_p+1) == '%' || *(_p+1) == ',' || *(_p+1) == '.')) { - p_r--; - } - } - - replace_with_q_mark( - grouping_digest, grouping_lim, &grouping_count, &p_r, &grouping_limit_exceeded - ); - - if(len == i+1) - { - if(is_token_char(*s)) - *p_r++ = *s; - i++; - continue; - } - } else { - // collapse any digits found in the string - if (replace_number) { - int str_len = p_r - p_r_t + 1; - int collapsed = 0; - - for (int j = 0; j < str_len; j++) { - char* const c_p_r_t = ((char*)p_r_t + j); - char* const n_p_r_t = ((char*)p_r_t + j + 1); - - if (is_digit_char(*c_p_r_t) && is_digit_char(*n_p_r_t)) { - memmove(c_p_r_t, c_p_r_t + 1, str_len - j); - collapsed += 1; - } - } - - p_r -= collapsed; - - int new_str_len = p_r - p_r_t + 1; - for (int j = 0; j < new_str_len; j++) { - char* const c_p_r_t = ((char*)p_r_t + j); - if (is_digit_char(*c_p_r_t)) { - *c_p_r_t = '?'; - } - } - } - } - - flag = 0; - } - } - } - - // ================================================= - // COPY CHAR - // ================================================= - // convert every space char to ' ' - if (*s == ')') { - if (grouping_digest > 0) { - grouping_digest -= 1; - }; - grouping_count = 0; - grouping_limit_exceeded = 0; - } - - if (lowercase==0) { - *p_r++ = !is_space_char(*s) ? *s : ' '; - } else { - *p_r++ = !is_space_char(*s) ? (tolower(*s)) : ' '; - } - - if (*s == '(') { - grouping_digest += 1; - grouping_count = 0; - grouping_limit_exceeded = 0; - } - - prev_char = *s++; - - i++; - } - - // remove a trailing space - if (p_r>r) { - char *e=p_r; - e--; - if (*e==' ') { - *e=0; - // maybe 2 trailing spaces . It happens with comments - e--; - if (*e==' ') { - *e=0; - } - } - } - - *p_r = 0; - - // process query stats - return r; -} - /** * @brief Struct for holding all the configuration options used for query digests generation. */ diff --git a/lib/c_tokenizer_legacy.cpp b/lib/c_tokenizer_legacy.cpp new file mode 100644 index 0000000000..ae93027afe --- /dev/null +++ b/lib/c_tokenizer_legacy.cpp @@ -0,0 +1,588 @@ +/* + this file is here only for reference. + It includes the old mysql_query_digest_and_first_comment() , outdated since ProxySQL 2.4.0 +*/ +char *mysql_query_digest_and_first_comment(char *s, int _len, char **first_comment, char *buf){ + int i = 0; + + char cur_comment[FIRST_COMMENT_MAX_LENGTH]; + cur_comment[0]=0; + int ccl=0; + int cmd=0; + + int len = _len; + if (_len > mysql_thread___query_digests_max_query_length) { + len = mysql_thread___query_digests_max_query_length; + } + char *r = buf; + if (r==NULL) { + r = (char *) malloc(len + SIZECHAR); + } + char *p_r = r; + char *p_r_t = r; + + char prev_char = 0; + char qutr_char = 0; + + char flag = 0; + char fc=0; + int fc_len=0; + + char fns=0; + + bool lowercase=0; + bool replace_null=0; + bool replace_number=0; + + char grouping_digest=0; + char grouping_limit_exceeded=0; + int grouping_count=0; + int grouping_lim = mysql_thread___query_digests_grouping_limit; + + lowercase=mysql_thread___query_digests_lowercase; + replace_null = mysql_thread___query_digests_replace_null; + replace_number = mysql_thread___query_digests_no_digits; + + while(i < len) + { + // Handy for debugging purposes + // ============================ + // printf( + // "state-1: { flag: `%d`, prev_char: `%c`, s: `%s`, p_r: `%s`, r: `%s`}\n", + // flag, prev_char, s, p_r, r + // ); + // ============================ + + // ================================================= + // START - read token char and set flag what's going on. + // ================================================= + if(flag == 0) + { + // store current position + p_r_t = p_r; + + // comment type 1 - start with '/*' + if(prev_char == '/' && *s == '*') + { + ccl=0; + flag = 1; + if (i != (len-1) && *(s+1)=='!') + cmd=1; + } + + // comment type 2 - start with '#' + else if(*s == '#') + { + flag = 2; + } + + // comment type 3 - start with '--' + + // NOTE: Looks like the general rule for parsing comments of this type could simply be: + // + // - `.*--.*` which could be translated into `(*s == '-' && *(s+1) == '-')`. + // + // But this can not hold, since the first '-' could have been consumed previously, for example + // during the parsing of a digit: + // + // - `select 1.1-- final_comment\n` + // + // For this reason 'prev_char' needs to be checked too when searching for the `--` pattern. + else if(i != (len-1) && prev_char == '-' && *s == '-' && ((*(s+1)==' ') || (*(s+1)=='\n') || (*(s+1)=='\r') || (*(s+1)=='\t') )) + { + flag = 3; + } + + // Previous character can be a consumed ' ' instead of '-' as in the previous case, for this + // reason, we need to look ahead for '--'. + // + // NOTE: There is no reason for not checking for the subsequent space char that should follow + // the '-- ', otherwise we would consider valid queries as `SELECT --1` like comments. + else if (i != (len-1) && *s == '-' && (*(s+1)=='-')) { + if (prev_char != '-') { + flag = 3; + } + else if (i==0) { + flag = 3; + } + } + + // string - start with ' + else if(*s == '\'' || *s == '"') + { + flag = 4; + qutr_char = *s; + } + + // may be digit - start with digit + else if(is_token_char(prev_char) && is_digit_char(*s)) + { + flag = 5; + if(len == i+1) + continue; + } + + // not above case - remove duplicated space char + else + { + flag = 0; + if (fns==0 && is_space_char(*s)) { + s++; + i++; + continue; + } + if (fns==0) fns=1; + if(is_space_char(prev_char) && is_space_char(*s)){ + prev_char = ' '; + *p_r = ' '; + s++; + i++; + continue; + } + if (replace_number) { + if (!is_digit_char(prev_char) && is_digit_char(*s)) { + *p_r++ = '?'; + while(*s != '\0' && is_digit_char(*s)) { + s++; + i++; + } + } + } + { + char* p = p_r - 2; + // suppress spaces before arithmetic operators + if (p >= r && is_space_char(prev_char) && is_arithmetic_op(*s)) { + if (*p == '?') { + prev_char = *s; + --p_r; + *p_r++ = *s; + s++; + i++; + continue; + } + } + // suppress spaces before and after commas + if (p >= r && is_space_char(prev_char) && ((*s == ',') || (*p == ','))) { + if (*s == ',') { + --p_r; + // only copy the comma if we are not grouping a query + if (!grouping_limit_exceeded) { + *p_r++ = *s; + } + prev_char = ','; + s++; + i++; + } else { + prev_char = ','; + --p_r; + } + continue; + } + // suppress spaces before closing brackets when grouping or mark is present + if (p >= r && (*p == '.' || *p == '?') && is_space_char(prev_char) && (*s == ')')) { + prev_char = *s; + --p_r; + *p_r++ = *s; + s++; + i++; + continue; + } + } + if (replace_null) { + if (*s == 'n' || *s == 'N') { // we search for NULL , #2171 + if (i && is_token_char(prev_char)) { + if (len>=4) { + if (i=2) fc_len-=2; + char *c=*first_comment+fc_len; + *c=0; + //*first_comment[fc_len]=0; + fc=2; + } + } + } + if( + // comment type 1 - /* .. */ + (flag == 1 && prev_char == '*' && *s == '/') || + + // comment type 2 - # ... \n + (flag == 2 && (*s == '\n' || *s == '\r' || (i == len - 1) )) + || + // comment type 3 - -- ... \n + (flag == 3 && (*s == '\n' || *s == '\r' || (i == len -1) )) + ) + { + p_r = p_r_t; + if (flag == 1 || (i == len -1)) { + p_r -= SIZECHAR; + } + if (cmd) { + cur_comment[ccl]=0; + if (ccl>=2) { + ccl-=2; + cur_comment[ccl]=0; + char el=0; + int fcc=0; + while (el==0 && fcc= r && ( *(_p+2) == '-' || *(_p+2) == '+') ) { + if ( + ( *(_p+1) == ',' ) || ( *(_p+1) == '(' ) || + ( ( *(_p+1) == ' ' ) && ( *_p == ',' || *_p == '(' ) ) + ) { + p_r--; + } + } + + replace_with_q_mark( + grouping_digest, grouping_lim, &grouping_count, &p_r, &grouping_limit_exceeded + ); + + flag = 0; + break; + } + + // need to be ignored case + if(p_r > p_r_t + SIZECHAR) + { + if( + (prev_char == '\\' && *s == '\\') || // to process '\\\\', '\\' + (prev_char == '\\' && *s == qutr_char) || // to process '\'' + (prev_char == qutr_char && *s == qutr_char) // to process '''' + ) + { + prev_char = 'X'; + s++; + i++; + continue; + } + } + + // satisfied closing string - swap string to ? + if(*s == qutr_char && (len == i+1 || *(s + SIZECHAR) != qutr_char)) + { + char *_p = p_r_t; + _p-=3; + p_r = p_r_t; + if ( _p >= r && ( *(_p+2) == '-' || *(_p+2) == '+') ) { + if ( + ( *(_p+1) == ',' ) || ( *(_p+1) == '(' ) || + ( ( *(_p+1) == ' ' ) && ( *_p == ',' || *_p == '(' ) ) + ) { + p_r--; + } + } + + // Remove spaces before each literal found + if ( _p >= r && is_space_char(*(_p + 2)) && !is_normal_char(*(_p + 1))) { + if ( _p >= r && ( *(_p+3) == '\''|| *(_p+3) == '"' )) { + p_r--; + } + } + + replace_with_q_mark( + grouping_digest, grouping_lim, &grouping_count, &p_r, &grouping_limit_exceeded + ); + + prev_char = qutr_char; + qutr_char = 0; + flag = 0; + if(i < len) + s++; + i++; + continue; + } + } + + // -------- + // digit + // -------- + else if(flag == 5) + { + // last single char + if(p_r_t == p_r) + { + char *_p = p_r_t; + _p-=3; + if ( _p >= r && ( *(_p+2) == '-' || *(_p+2) == '+') ) { + if ( + ( *(_p+1) == ',' ) || ( *(_p+1) == '(' ) || + ( ( *(_p+1) == ' ' ) && ( *_p == ',' || *_p == '(' ) ) + ) { + p_r--; + } + } + if ( _p >= r && is_space_char(*(_p + 2))) { + if ( _p >= r && ( *(_p+1) == '-' || *(_p+1) == '+' || *(_p+1) == '*' || *(_p+1) == '/' || *(_p+1) == '%' || *(_p+1) == ',')) { + p_r--; + } + } + *p_r++ = '?'; + i++; + continue; + } + + // is float + if (*s == '.' || *s == 'e' || ((*s == '+' || *s == '-') && prev_char == 'e')) { + prev_char = *s; + i++; + s++; + continue; + } + + // token char or last char + if(is_token_char(*s) || len == i+1) + { + if(is_digit_string(p_r_t, p_r)) + { + char *_p = p_r_t; + _p-=3; + p_r = p_r_t; + // remove symbol and keep parenthesis or comma + if ( _p >= r && ( *(_p+2) == '-' || *(_p+2) == '+') ) { + if ( + ( *(_p+1) == ',' ) || ( *(_p+1) == '(' ) || + ( ( *(_p+1) == ' ' ) && ( *_p == ',' || *_p == '(' ) ) + ) { + p_r--; + } + } + + // Remove spaces before number counting with possible '.' presence + if (_p >= r && *_p == '.' && (*(_p + 1) == ' ' || *(_p + 1) == '.') && (*(_p+2) == '-' || *(_p+2) == '+') ) { + if (*(_p + 1) == ' ') { + p_r--; + } + p_r--; + } + + // Remove spaces after a opening bracket when followed by a number + if (_p >= r && *(_p+1) == '(' && *(_p+2) == ' ') { + p_r--; + } + + // Remove spaces before number + if ( _p >= r && is_space_char(*(_p + 2))) { + // A point can be found prior to a number in case of query grouping + if ( _p >= r && ( *(_p+1) == '-' || *(_p+1) == '+' || *(_p+1) == '*' || *(_p+1) == '/' || *(_p+1) == '%' || *(_p+1) == ',' || *(_p+1) == '.')) { + p_r--; + } + } + + replace_with_q_mark( + grouping_digest, grouping_lim, &grouping_count, &p_r, &grouping_limit_exceeded + ); + + if(len == i+1) + { + if(is_token_char(*s)) + *p_r++ = *s; + i++; + continue; + } + } else { + // collapse any digits found in the string + if (replace_number) { + int str_len = p_r - p_r_t + 1; + int collapsed = 0; + + for (int j = 0; j < str_len; j++) { + char* const c_p_r_t = ((char*)p_r_t + j); + char* const n_p_r_t = ((char*)p_r_t + j + 1); + + if (is_digit_char(*c_p_r_t) && is_digit_char(*n_p_r_t)) { + memmove(c_p_r_t, c_p_r_t + 1, str_len - j); + collapsed += 1; + } + } + + p_r -= collapsed; + + int new_str_len = p_r - p_r_t + 1; + for (int j = 0; j < new_str_len; j++) { + char* const c_p_r_t = ((char*)p_r_t + j); + if (is_digit_char(*c_p_r_t)) { + *c_p_r_t = '?'; + } + } + } + } + + flag = 0; + } + } + } + + // ================================================= + // COPY CHAR + // ================================================= + // convert every space char to ' ' + if (*s == ')') { + if (grouping_digest > 0) { + grouping_digest -= 1; + }; + grouping_count = 0; + grouping_limit_exceeded = 0; + } + + if (lowercase==0) { + *p_r++ = !is_space_char(*s) ? *s : ' '; + } else { + *p_r++ = !is_space_char(*s) ? (tolower(*s)) : ' '; + } + + if (*s == '(') { + grouping_digest += 1; + grouping_count = 0; + grouping_limit_exceeded = 0; + } + + prev_char = *s++; + + i++; + } + + // remove a trailing space + if (p_r>r) { + char *e=p_r; + e--; + if (*e==' ') { + *e=0; + // maybe 2 trailing spaces . It happens with comments + e--; + if (*e==' ') { + *e=0; + } + } + } + + *p_r = 0; + + // process query stats + return r; +} From 6417b78497b550404e9c98a7b2c44ff9cab50cda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Mon, 22 Apr 2024 07:22:22 +0000 Subject: [PATCH 11/79] Removed tests related to mysql_query_digest_and_first_comment() --- .../test_mysql_query_digests_stages-t.cpp | 4 + test/tap/tests/test_tokenizer-t.cpp | 474 ------------------ 2 files changed, 4 insertions(+), 474 deletions(-) delete mode 100644 test/tap/tests/test_tokenizer-t.cpp diff --git a/test/tap/tests/test_mysql_query_digests_stages-t.cpp b/test/tap/tests/test_mysql_query_digests_stages-t.cpp index c4c79ddf0d..1533faf02e 100644 --- a/test/tap/tests/test_mysql_query_digests_stages-t.cpp +++ b/test/tap/tests/test_mysql_query_digests_stages-t.cpp @@ -89,11 +89,15 @@ uint64_t benchmark_parsing(const vector& queries, int mode, uint32_t ite char* c_res = NULL; if (mode == 0) { + diag("Invalid test. mysql_query_digest_and_first_comment() was deprecated in 2.4.0"); + exit(EXIT_FAILURE); +/* c_res = mysql_query_digest_and_first_comment( const_cast(query.c_str()), query.length(), &first_comment, ((query.size() < QUERY_DIGEST_BUF) ? buf : NULL) ); +*/ } else if (mode == 1) { c_res = mysql_query_digest_and_first_comment_one_it( diff --git a/test/tap/tests/test_tokenizer-t.cpp b/test/tap/tests/test_tokenizer-t.cpp deleted file mode 100644 index a409a55ddb..0000000000 --- a/test/tap/tests/test_tokenizer-t.cpp +++ /dev/null @@ -1,474 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "proxysql.h" - -#include "tap.h" -#include "command_line.h" - -#include "utils.h" - -#include - -__thread int mysql_thread___query_digests_max_query_length = 65000; -__thread bool mysql_thread___query_digests_lowercase = false; -__thread bool mysql_thread___query_digests_replace_null = false; -__thread bool mysql_thread___query_digests_no_digits = false; -__thread bool mysql_thread___query_digests_keep_comment = false; -__thread int mysql_thread___query_digests_grouping_limit = 3; -__thread int mysql_thread___query_digests_groups_grouping_limit = 1; - -using std::vector; -using std::pair; -using std::string; -using std::tuple; - -const vector> query_digest_pairs { - // TODO: KnownIssue - 10 - // { - // "select /* COMMENT */ 1", - // "select ?" - // // Actual: "select ?# final_comment" - // }, - { - "select/* COMMENT */ 1", - "select ?" - }, - { - "select/* COMMENT */1", - "select ?" - }, - // initial '#' comments - { - "# random_comment \n select 1.1", - "select ?" - }, - { - "#random_comment \nselect 1.1", - "select ?" - }, - { - "#random_comment\nselect 1.1", - "select ?" - }, - // initial '--' comments - { - "-- random_comment \n select 1.1", - "select ?" - }, - { - "--random_comment \nselect 1.1", - "select ?" - }, - { - "--random_comment\nselect 1.1", - "select ?" - }, - // final/initial '#|--' comments - { - "# random_comment\n select 1.1 #final_comment\n ", - "select ?" - }, - // TODO: KnownIssue - 1 - // { - // "# random_comment\n select 1.1# final_comment \n", - // "select ?" - // // Actual: "select ?# final_comment" - // }, - { - "# random_comment\n select 1.1 #final_comment \n ", - "select ?" - }, - { - "-- random_comment\n select 1.1 --final_comment\n ", - "select ?" - }, - { - "-- random_comment\n select 1.1-- final_comment \n", - "select ?" - }, - // NOTE: Comments with '--' should always be followed by an space. - // { - // "-- random_comment\n select 1.1--final_comment \n", - // "select ?" - // }, - { - "-- random_comment\n select 1.1 --final_comment \n ", - "select ?" - }, - // floats - { "select 1.1", "select ?" }, - { "select 99.1929", "select ?" }, - // exponentials - { "select 1.1e9", "select ?" }, - { "select 1.1e+9", "select ?" }, - { "select 1.1e-9", "select ?" }, - // TODO: KnownIssue - 2: Exponentials are case sensitive - // { "select 1.1E9", "select ?" }, - // { "select 1.1E+9", "select ?" }, - // { "select 1.1E-9", "select ?" }, - // operators - { "select 1 +1", "select ?+?" }, - { "select 1+ 1", "select ?+?" }, - { "select 1- 1", "select ?-?" }, - { "select 1 -1", "select ?-?" }, - { "select 1* 1", "select ?*?" }, - { "select 1 *1", "select ?*?" }, - { "select 1/ 1", "select ?/?" }, - { "select 1 /1", "select ?/?" }, - { "select 1% 1", "select ?%?" }, - { "select 1 %1", "select ?%?" }, - // operators and commas - { - "select 1+ 1, 1 -1, 1 * 1 , 1/1 , 100 % 3", - "select ?+?,?-?,?*?,?/?,?%?", - }, - { - "SELECT * FROM t t1, t t2 ,t t3,t t4 LIMIT 0", - "SELECT * FROM t t1,t t2,t t3,t t4 LIMIT ?" - }, - // mixing operators, commas and literals - { - "select 1+ 1,'1 -1', 1 * 1 , '1/1 ',100 % 3", - "select ?+?,?,?*?,?,?%?" - }, - { - "select 1+ 1 ,'1 -1' ,1 * 1 , '1 ' , 100 % 3", - "select ?+?,?,?*?,?,?%?" - }, - { - "select 1 + 1 , '1 - 1' , 1 * 1 , '1 ' , 100 % 3 ", - "select ?+?,?,?*?,?,?%?" - }, - // TODO: KnownIssue - 8: Operators not removed when extra space precedes the value - { - "select + 1", - "select +?" - }, - // strings - simple - { - "select * from t where t = \"foo\"", - "select * from t where t = ?", - }, - { - "select \"1+ 1, 1 -1, 1 * 1 , 1/1 , 100 % 3\"", - "select ?", - }, - // string - preceded by signs - outside parenthesis, not preceded by commas - { "select -\"1\"", "select -?", }, - { "select +\"1\",'foo'", "select +?,?", }, - // string - preceded by signs - inside parenthesis, or preceded by commas - { "select (-'89')", "select (?)", }, - { "select 10,-'89'", "select ?,?", }, - { "select 10, -'89' ", "select ?,?", }, - // string - leading strings get it's extra spaces removed, but not firsts - { "select '10', -'89' ", "select ?,?", }, - { "select 10, -'89 ',+'5'", "select ?,?,?", }, - // TODO: KnownIssue - 7: Spaces not removed after parenthesis when literal strings are preceded by '+|-' - { "select CONCAT( -'89'+'10')", "select CONCAT( ?+?)", }, - // ^ preserved space - { "select CONCAT( -'89'+'10')", "select CONCAT( ?+?)", }, - { "select CONCAT( -'89' + '10' )", "select CONCAT( ?+?)", }, - // TODO: KnownIssue - 8: Operators not removed when extra space precedes the literal (value) - { "select CONCAT(- '89')", "select CONCAT(-?)", }, - // ^ preserved operator - - // not modified - { "select * fromt t", "select * fromt t" }, - // test query_digest reduction - { - "SELECT * FROM tablename WHERE id IN (1,2,3,4,5,6,7,8,9,10)", - "SELECT * FROM tablename WHERE id IN (?,?,?,...)" - }, - { - "SELECT * FROM tablename WHERE id IN (1,2,3,4)", - "SELECT * FROM tablename WHERE id IN (?,?,?,...)" - }, - // invalid request grouping - { - "SELECT * tablename where id IN (1,2,3,4,5,6,7,8, AND j in (1,2,3,4,5,6 and k=1", - "SELECT * tablename where id IN (?,?,?,...,AND j in (?,?,?,... and k=?" - }, - // random insert queries - { - "INSERT INTO db.table(col1) VALUES ('val')", - "INSERT INTO db.table(col1) VALUES (?)" - }, - { - "INSERT INTO db.table (col1) VALUES ('val')", - "INSERT INTO db.table (col1) VALUES (?)" - }, - { - "INSERT INTO db.table( col1) VALUES ( 'val' )", - "INSERT INTO db.table( col1) VALUES (?)" - }, - { - "INSERT INTO db.table( col1) VALUES ( 'val' )", - "INSERT INTO db.table( col1) VALUES (?)" - }, - // TODO: KnownIssue - 6: When 'no-digits' is enabled, space before parenthesis closing brakcet is - // collapsed when an identifier name finish with a number. - // { - // "INSERT INTO db.table ( col1 ) VALUES ( 'val' )", - // "INSERT INTO db.table ( col1 ) VALUES (?)" - // }, - { - "INSERT INTO db.table (col1, col2,col3,col4) VALUES ('val',2,3,'foo')", - "INSERT INTO db.table (col1,col2,col3,col4) VALUES (?,?,?,...)" - }, - // TODO: KnownIssue - 6: When 'no-digits' is enabled, space before parenthesis closing brakcet is - // collapsed when an identifier name finish with a number. - // { - // "INSERT INTO db.table ( col1, col2,col3,col4 ) VALUES ('val',2,3,'foo')", - // "INSERT INTO db.table ( col1,col2,col3,col4 ) VALUES (?,?,?,...)" - // }, - { - "INSERT INTO db.table_25 (col1, col2,col3,col4) VALUES ('val',2,3,'foo')", - "INSERT INTO db.table_25 (col1,col2,col3,col4) VALUES (?,?,?,...)" - }, - { - "INSERT INTO db.table1_25 ( col_121,col2121 ,col12_3, col41203_ ) VALUES (?,?,?,...)", - "INSERT INTO db.table1_25 ( col_121,col2121,col12_3,col41203_ ) VALUES (?,?,?,...)" - }, - // TODO: KnownIssue - 5: Arithmetics operators breaks grouping - // { - // "INSERT INTO db.table ( col1, col2,col3,col4, col5 ) VALUES ('val',2,3,'foo', 5 + 10, 6 - 9)", - // "INSERT INTO db.table ( col1,col2,col3,col4,col5 ) VALUES (?,?,?,...)" - // // Actual: "INSERT INTO db.table ( col1,col2,col3,col4,col5 ) VALUES (?,?,?,... + -)" - // }, -}; - -const vector> queries_digests_grouping { - // test query_digest reduction - { - "SELECT * FROM tablename WHERE id IN (1,2, 3,4 ,5 ,6,7,8,9,10)", - "SELECT * FROM tablename WHERE id IN (?,...)" - }, - // invalid request grouping - { - "SELECT * tablename where id IN (1,2,3,4,5 , 6,7,8, AND j in (1, 2,3, 4 ,5,6,7,8,9 and k=1", - "SELECT * tablename where id IN (?,...,AND j in (?,... and k=?" - }, - // more random grouping - { - "SELECT (1.1, 1, 2, 13, 4.81, 12) FROM db.table", - "SELECT (?,...) FROM db.table" - }, - { - "SELECT (1.1, 1.12934 , 21.32 , 91, 91, 12.93 ) FROM db.table2", - "SELECT (?,...) FROM db.table2" - }, - { - "SELECT (1.1, 1.12934 , 21.32 , 91.2 , 91, 12 ) FROM db.table7", - "SELECT (?,...) FROM db.table7" - }, - { - "SELECT (1.1, 1.12934, 21.32, 391,2381,28.493,1283 ) FROM db.table2", - "SELECT (?,...) FROM db.table2" - }, - { - "SELECT (1.1, 1.12934, 21.32 , 91, 91, 12.1 ) FROM db.table3", - "SELECT (?,...) FROM db.table3" - } -}; - -const vector> null_queries_digests { - { - "select Null , '1*2/2',NULL,null , '1 ' , 100 % 3 ", - "select Null,?,NULL,null,?,?%?", - "select ?,?,?,?,?,?%?" - }, - // TODO: KnownIssue - 3: Grouping count isn't reset by NULL. - // { - // "INSERT INTO db.table VALUES ( Null , NULL, '',NULL, 'a', 'b', 'z',nuLL)", - // "INSERT INTO db.table VALUES (Null,NULL,?,NULL,?,?,?,nuLL)", - // "INSERT INTO db.table VALUES (?,?,?,...)" - // // Act: INSERT INTO db.table VALUES ( Null,NULL,?,NULL,?,?,...,nuLL) - // }, - { - "INSERT INTO db.table VALUES ( NULL, 'a', 'b', 'z', -4, nuLL)", - // TODO: KnownIssue - 4: Spaces preceding NULL values are not properly removed - "INSERT INTO db.table VALUES ( NULL,?,?,?,...,nuLL)", - "INSERT INTO db.table VALUES (?,?,?,...)" - }, -}; - -std::string replace_str(const std::string& str, const std::string& match, const std::string& repl) { - if(match.empty()) { - return str; - } - - std::string result = str; - size_t start_pos = 0; - - while((start_pos = result.find(match, start_pos)) != std::string::npos) { - result.replace(start_pos, match.length(), repl); - start_pos += repl.length(); - } - - return result; -} - -std::string increase_mark_num(const std::string query, uint32_t num) { - std::string result = query; - std::string marks = ""; - - for (uint32_t i = 0; i < num - 1; i++) { - marks += "?,"; - } - marks += "?,..."; - - result = replace_str(result, "?,...", marks); - - return result; -} - -char is_digit_char(char c) { - if(c >= '0' && c <= '9') { - return 1; - } - return 0; -} - -vector extract_numbers(const string query) { - vector numbers {}; - string number {}; - - for (const char c : query) { - if (is_digit_char(c)) { - number += c; - } else { - if (!number.empty()) { - numbers.push_back(number); - number.clear(); - } - } - } - - return numbers; -} - -string replace_numbers(const string query, const char mark) { - vector numbers { extract_numbers(query) }; - std::sort( - numbers.begin(), numbers.end(), - [](const string& s1, const string& s2) -> bool { return s1.size() > s2.size(); } - ); - - string query_res { query }; - - for (const string& num : numbers) { - query_res = replace_str(query_res, num, string { mark }); - } - - return query_res; -} - -int main(int argc, char** argv) { - if (query_digest_pairs.size() != query_digest_pairs.size()) { - ok(0, "queries and exp_results sizes doesn't match"); - return exit_status(); - } - - plan( - query_digest_pairs.size()*2 + queries_digests_grouping.size()*5 + null_queries_digests.size()*2 - ); - - char buf[QUERY_DIGEST_BUF]; - - const auto test_query_digests = [&](bool replace_digits) -> void { - mysql_thread___query_digests_no_digits=replace_digits; - - for (size_t i = 0; i < query_digest_pairs.size(); i++) { - const auto& query = query_digest_pairs[i].first; - const auto& query_str_rep = replace_str(query_digest_pairs[i].first, "\n", "\\n"); - char* first_comment = NULL; - std::string exp_res {}; - - if (replace_digits == false) { - exp_res = query_digest_pairs[i].second; - } else { - exp_res = replace_numbers(query_digest_pairs[i].second, '?'); - } - - char* c_res = mysql_query_digest_and_first_comment(const_cast(query.c_str()), query.length(), &first_comment, buf); - const std::string result(c_res); - std::string ok_msg {}; - - if (replace_digits == false) { - ok_msg = "Digest should be equal to exp result:\n * Query: `%s`,\n * Act: `%s`,\n * Exp: `%s`"; - } else { - ok_msg = "No-Digits digest should be equal to exp result:\n * Query: `%s`,\n * Act: `%s`,\n * Exp: `%s`"; - } - - ok( - result == exp_res, ok_msg.c_str(), - query_str_rep.c_str(), result.c_str(), exp_res.c_str() - ); - } - - mysql_thread___query_digests_no_digits=0; - }; - - /* Test queries without replacing digits */ - test_query_digests(false); - /* Test queries replacing digits */ - test_query_digests(true); - - const auto test_null_replacting = [&](bool replace_nulls) -> void { - mysql_thread___query_digests_replace_null=replace_nulls; - - for (size_t i = 0; i < null_queries_digests.size(); i++) { - const auto& query = std::get<0>(null_queries_digests[i]); - std::string exp_res {}; - - if (replace_nulls) { - exp_res = std::get<2>(null_queries_digests[i]); - } else { - exp_res = std::get<1>(null_queries_digests[i]); - } - - char* c_res = mysql_query_digest_and_first_comment(const_cast(query.c_str()), query.length(), NULL, buf); - std::string result(c_res); - - ok( - result == exp_res, - "Replaced NULL values digest should be equal to exp result:" - "\n * Query: `%s`,\n * Act: `%s`,\n * Exp: `%s`", - query.c_str(), result.c_str(), exp_res.c_str() - ); - } - - mysql_thread___query_digests_replace_null=0; - }; - - /* Test queries containing 'NULL', NOT replacing the 'NULL' values */ - test_null_replacting(false); - /* Test queries containing 'NULL', replacing the 'NULL' values */ - test_null_replacting(true); - - for (size_t i = 0; i < queries_digests_grouping.size(); i++) { - for (int j = 1; j <= 5; j++) { - mysql_thread___query_digests_grouping_limit = j; - - const auto& query = queries_digests_grouping[i].first; - const auto& exp_res = increase_mark_num(queries_digests_grouping[i].second, j); - - char* c_res = mysql_query_digest_and_first_comment(const_cast(query.c_str()), query.length(), NULL, buf); - std::string result(c_res); - - ok( - result == exp_res, - "Grouping digest should be equal to exp result:" - "\n * Query: `%s`,\n * Act: `%s`,\n * Exp: `%s`", - query.c_str(), result.c_str(), exp_res.c_str() - ); - } - } - - return exit_status(); -} From 229b5d7d3f921679aa813c329c235846ef1f22e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Tue, 23 Apr 2024 01:13:12 +0000 Subject: [PATCH 12/79] Some code cleanup --- lib/MySQL_PreparedStatement.cpp | 65 --------------------------------- 1 file changed, 65 deletions(-) diff --git a/lib/MySQL_PreparedStatement.cpp b/lib/MySQL_PreparedStatement.cpp index 5a47e81119..1b27c6a6e9 100644 --- a/lib/MySQL_PreparedStatement.cpp +++ b/lib/MySQL_PreparedStatement.cpp @@ -738,53 +738,16 @@ MySQL_STMTs_local_v14::~MySQL_STMTs_local_v14() { GloMyStmt->ref_count_server(global_stmt_id, -1); } } -/* - for (std::map::iterator it = m.begin(); - it != m.end(); ++it) { - uint32_t stmt_id = it->first; - MYSQL_STMT *stmt = it->second; - if (stmt) { // is a server - if (stmt->mysql) { - stmt->mysql->stmts = - list_delete(stmt->mysql->stmts, &stmt->list); - } - // we do a hack here: we pretend there is no server associate - // the connection will be dropped anyway immediately after - stmt->mysql = NULL; - mysql_stmt_close(stmt); - GloMyStmt->ref_count(stmt_id, -1, true, false); - } else { // is a client - GloMyStmt->ref_count(stmt_id, -1, true, true); - } - } - m.erase(m.begin(), m.end()); -*/ } MySQL_STMT_Global_info *MySQL_STMT_Manager_v14::find_prepared_statement_by_hash( uint64_t hash) { - //uint64_t hash, bool lock) { // removed in 2.3 MySQL_STMT_Global_info *ret = NULL; // assume we do not find it -/* removed in 2.3 - if (lock) { - pthread_rwlock_wrlock(&rwlock_); - } -*/ auto s = map_stmt_hash_to_info.find(hash); if (s != map_stmt_hash_to_info.end()) { ret = s->second; - //__sync_fetch_and_add(&ret->ref_count_client,1); // increase reference - //count -// __sync_fetch_and_add(&find_prepared_statement_by_hash_calls, 1); -// __sync_fetch_and_add(&ret->ref_count_client, 1); } - -/* removed in 2.3 - if (lock) { - pthread_rwlock_unlock(&rwlock_); - } -*/ return ret; } @@ -808,15 +771,6 @@ MySQL_STMT_Global_info *MySQL_STMT_Manager_v14::find_prepared_statement_by_stmt_ uint32_t MySQL_STMTs_local_v14::generate_new_client_stmt_id(uint64_t global_statement_id) { uint32_t ret=0; -/* - //auto s2 = global_stmt_to_client_ids.find(global_statement_id); - std::pair::iterator, std::multimap::iterator> itret; - itret = global_stmt_to_client_ids.equal_range(global_statement_id); - for (std::multimap::iterator it=itret.first; it!=itret.second; ++it) { - ret = it->second; - return ret; - } -*/ if (free_client_ids.size()) { ret=free_client_ids.top(); free_client_ids.pop(); @@ -893,7 +847,6 @@ MySQL_STMT_Global_info *MySQL_STMT_Manager_v14::add_prepared_statement( } else { next_id = next_statement_id; next_statement_id++; - //__sync_fetch_and_add(&next_statement_id, 1); } //next_statement_id++; @@ -902,14 +855,7 @@ MySQL_STMT_Global_info *MySQL_STMT_Manager_v14::add_prepared_statement( // insert it in both maps map_stmt_id_to_info.insert(std::make_pair(a->statement_id, a)); map_stmt_hash_to_info.insert(std::make_pair(a->hash, a)); - // ret=a->statement_id; ret = a; - // next_statement_id++; // increment it - //__sync_fetch_and_add(&ret->ref_count_client,1); // increase reference - //count -// __sync_fetch_and_add(&ret->ref_count_client, -// 1); // increase reference count -// *is_new = true; __sync_add_and_fetch(&num_stmt_with_ref_client_count_zero,1); __sync_add_and_fetch(&num_stmt_with_ref_server_count_zero,1); } @@ -918,9 +864,6 @@ MySQL_STMT_Global_info *MySQL_STMT_Manager_v14::add_prepared_statement( } ret->ref_count_server++; statuses.s_total++; -// __sync_fetch_and_add(&add_prepared_statement_calls, 1); -// __sync_fetch_and_add(&ret->ref_count_server, -// 1); // increase reference count if (lock) { pthread_rwlock_unlock(&rwlock_); } @@ -1101,14 +1044,6 @@ SQLite3_result * MySQL_STMT_Manager_v14::get_prepared_statements_global_infos() pgs->free_row(pta); delete pgs; } -/* - for (std::unordered_map::iterator it=digest_umap.begin(); it!=digest_umap.end(); ++it) { - QP_query_digest_stats *qds=(QP_query_digest_stats *)it->second; - char **pta=qds->get_row(); - result->add_row(pta); - qds->free_row(pta); - } -*/ unlock(); return result; } From 8c92f9292a2878b4666885dd85afde15f52e04fe Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Tue, 23 Apr 2024 11:40:32 +0500 Subject: [PATCH 13/79] A hostgroup can now be configured to have a maximum number of online servers simultaneously. Once this limit is reached, any new connections attempting to access the connection pool will be denied, and existing connections will gradually be reassigned to the available connection pool until the number of online servers is reduced to meet or fall below the specified maximum. --- include/MySQL_HostGroups_Manager.h | 35 +++++++++- lib/MySQL_HostGroups_Manager.cpp | 108 ++++++++++++++++++++++------- lib/MySQL_Session.cpp | 18 +++-- lib/mysql_connection.cpp | 4 +- 4 files changed, 132 insertions(+), 33 deletions(-) diff --git a/include/MySQL_HostGroups_Manager.h b/include/MySQL_HostGroups_Manager.h index f01a030ee2..e27755aedb 100644 --- a/include/MySQL_HostGroups_Manager.h +++ b/include/MySQL_HostGroups_Manager.h @@ -190,6 +190,29 @@ class MySrvConnList { MySQL_Connection *index(unsigned int); }; +class MySrvC; +class MySrvStatus { +public: + MySrvStatus(MySrvC* _mysrvc); + ~MySrvStatus(); + + enum MySerStatus operator=(enum MySerStatus _status); + + inline + operator enum MySerStatus() const { + return status; + } + + inline + bool operator==(enum MySerStatus _status) const { + return status == _status; + } + +private: + MySrvC *mysrvc; + enum MySerStatus status; +}; + class MySrvC { // MySQL Server Container public: MyHGC *myhgc; @@ -198,7 +221,7 @@ class MySrvC { // MySQL Server Container uint16_t gtid_port; uint16_t flags; int64_t weight; - enum MySerStatus status; + MySrvStatus status; unsigned int compression; int64_t max_connections; unsigned int aws_aurora_current_lag_us; @@ -279,6 +302,8 @@ class MySrvList { // MySQL Server List class MyHGC { // MySQL Host Group Container public: unsigned int hid; + std::atomic num_online_servers; + unsigned long long last_log_time_num_online_servers; unsigned long long current_time_now; uint32_t new_connections_now; MySrvList *mysrvs; @@ -310,6 +335,13 @@ class MyHGC { // MySQL Host Group Container MyHGC(int); ~MyHGC(); MySrvC *get_random_MySrvC(char * gtid_uuid, uint64_t gtid_trxid, int max_lag_ms, MySQL_Session *sess); + void refresh_online_server_count(); + void log_num_online_server_count_error(); + inline + bool online_servers_within_threshold() const { + if (num_online_servers.load(std::memory_order_relaxed) <= attributes.max_num_online_servers) return true; + return false; + } }; class Group_Replication_Info { @@ -927,6 +959,7 @@ class MySQL_HostGroups_Manager { void init(); void wrlock(); void wrunlock(); + bool is_locked = false; // will be removed after testing int servers_add(SQLite3_result *resultset); /** * @brief Generates a new global checksum for module 'mysql_servers_v2' using the provided hash. diff --git a/lib/MySQL_HostGroups_Manager.cpp b/lib/MySQL_HostGroups_Manager.cpp index b185416af8..216aba8aa1 100644 --- a/lib/MySQL_HostGroups_Manager.cpp +++ b/lib/MySQL_HostGroups_Manager.cpp @@ -703,6 +703,7 @@ void MySrvList::add(MySrvC *s) { s->myhgc=myhgc; } servers->add(s); + myhgc->refresh_online_server_count(); } @@ -720,6 +721,7 @@ void MySrvList::remove(MySrvC *s) { int i=find_idx(s); assert(i>=0); servers->remove_index_fast((unsigned int)i); + myhgc->refresh_online_server_count(); } void MySrvConnList::drop_all_connections() { @@ -735,12 +737,11 @@ MySrvC::MySrvC( char* add, uint16_t p, uint16_t gp, int64_t _weight, enum MySerStatus _status, unsigned int _compression, int64_t _max_connections, unsigned int _max_replication_lag, int32_t _use_ssl, unsigned int _max_latency_ms, char* _comment -) { +) : status(this) { address=strdup(add); port=p; gtid_port=gp; weight=_weight; - status=_status; compression=_compression; max_connections=_max_connections; max_replication_lag=_max_replication_lag; @@ -766,6 +767,7 @@ MySrvC::MySrvC( comment=strdup(_comment); ConnectionsUsed=new MySrvConnList(this); ConnectionsFree=new MySrvConnList(this); + status=_status; } void MySrvC::connect_error(int err_num, bool get_mutex) { @@ -879,6 +881,17 @@ MySrvList::~MySrvList() { } +MySrvStatus::MySrvStatus(MySrvC* _mysrvc) : mysrvc(_mysrvc) {} + +MySrvStatus::~MySrvStatus() { mysrvc->myhgc->refresh_online_server_count(); } + +enum MySerStatus MySrvStatus::operator=(enum MySerStatus _status) { + status = _status; + if (mysrvc->myhgc) + mysrvc->myhgc->refresh_online_server_count(); +} + + MyHGC::MyHGC(int _hid) { hid=_hid; mysrvs=new MySrvList(this); @@ -890,6 +903,8 @@ MyHGC::MyHGC(int _hid) { servers_defaults.weight = -1; servers_defaults.max_connections = -1; servers_defaults.use_ssl = -1; + num_online_servers.store(0, std::memory_order_relaxed);; + last_log_time_num_online_servers = 0; } void MyHGC::reset_attributes() { @@ -921,6 +936,31 @@ MyHGC::~MyHGC() { delete mysrvs; } +void MyHGC::refresh_online_server_count() { + assert(MyHGM->is_locked); + + unsigned int online_servers_count = 0; + for (unsigned int i=0; iservers->len; i++) { + MySrvC *mysrvc=(MySrvC *)mysrvs->servers->index(i); + if (mysrvc->status == MYSQL_SERVER_STATUS_ONLINE) { + online_servers_count++; + } + } + num_online_servers.store(online_servers_count, std::memory_order_relaxed); +} + +void MyHGC::log_num_online_server_count_error() { + const unsigned long long curtime = monotonic_time(); + // if this is the first time the method is called or if more than 10 seconds have passed since the last log + if (last_log_time_num_online_servers == 0 || + (curtime-last_log_time_num_online_servers > 10*1000*1000)) { + last_log_time_num_online_servers = curtime; + proxy_error( + "Number of online servers detected in a hostgroup exceeds the configured maximum online servers. hostgroup:%u, num_online_servers:%u, max_online_servers:%u\n", + hid, num_online_servers.load(std::memory_order_relaxed), attributes.max_num_online_servers); + } +} + using metric_name = std::string; using metric_help = std::string; using metric_tags = std::map; @@ -1441,6 +1481,7 @@ void MySQL_HostGroups_Manager::wrlock() { #else spin_wrlock(&rwlock); #endif + is_locked = true; } void MySQL_HostGroups_Manager::p_update_mysql_error_counter(p_mysql_error_type err_type, unsigned int hid, char* address, uint16_t port, unsigned int code) { @@ -1480,6 +1521,7 @@ void MySQL_HostGroups_Manager::wrunlock() { #else spin_wrunlock(&rwlock); #endif + is_locked = false; } @@ -2464,6 +2506,8 @@ void MySQL_HostGroups_Manager::purge_mysql_servers_table() { if (mysrvc->ConnectionsUsed->conns_length()==0 && mysrvc->ConnectionsFree->conns_length()==0) { // no more connections for OFFLINE_HARD server, removing it mysrvc=(MySrvC *)myhgc->mysrvs->servers->remove_index_fast(j); + // already being refreshed in MySrvC destructor + //myhgc->refresh_online_server_count(); j--; delete mysrvc; } @@ -3075,8 +3119,9 @@ void MySQL_HostGroups_Manager::push_MyConn_to_pool(MySQL_Connection *c, bool _lo if (mysrvc->status==MYSQL_SERVER_STATUS_ONLINE) { if (c->async_state_machine==ASYNC_IDLE) { if (GloMTH == NULL) { goto __exit_push_MyConn_to_pool; } - // Check if the connection has too many prepared statements - if (c->local_stmts->get_num_backend_stmts() > (unsigned int)GloMTH->variables.max_stmts_per_connection) { + const bool online_servers_within_threshold = c->parent->myhgc->online_servers_within_threshold(); + if (/*online_servers_within_threshold == true &&*/ // if online servers in a hostgroup surpass the configured maximum, add connection to ConnectionFree pool + c->local_stmts->get_num_backend_stmts() > (unsigned int)GloMTH->variables.max_stmts_per_connection) { // Check if the connection has too many prepared statements // Log debug information about destroying the connection due to too many prepared statements proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Destroying MySQL_Connection %p, server %s:%d with status %d because has too many prepared statements\n", c, mysrvc->address, mysrvc->port, mysrvc->status); // delete c; @@ -3085,6 +3130,9 @@ void MySQL_HostGroups_Manager::push_MyConn_to_pool(MySQL_Connection *c, bool _lo } else { c->optimize(); // Optimize the connection mysrvc->ConnectionsFree->add(c); // Add the connection to the list of free connections + if (online_servers_within_threshold == false) { + mysrvc->myhgc->log_num_online_server_count_error(); + } } } else { // Log debug information about destroying the connection @@ -3164,35 +3212,43 @@ MySrvC *MyHGC::get_random_MySrvC(char * gtid_uuid, uint64_t gtid_trxid, int max_ for (j=0; jidx(j); if (mysrvc->status==MYSQL_SERVER_STATUS_ONLINE) { // consider this server only if ONLINE - if (mysrvc->ConnectionsUsed->conns_length() < mysrvc->max_connections) { // consider this server only if didn't reach max_connections - if ( mysrvc->current_latency_us < ( mysrvc->max_latency_us ? mysrvc->max_latency_us : mysql_thread___default_max_latency_ms*1000 ) ) { // consider the host only if not too far - if (gtid_trxid) { - if (MyHGM->gtid_exists(mysrvc, gtid_uuid, gtid_trxid)) { - sum+=mysrvc->weight; - TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); - mysrvcCandidates[num_candidates]=mysrvc; - num_candidates++; + if (mysrvc->myhgc->num_online_servers.load(std::memory_order_relaxed) <= mysrvc->myhgc->attributes.max_num_online_servers) { // number of online servers in HG is within configured range + if (mysrvc->ConnectionsUsed->conns_length() < mysrvc->max_connections) { // consider this server only if didn't reach max_connections + if (mysrvc->current_latency_us < (mysrvc->max_latency_us ? mysrvc->max_latency_us : mysql_thread___default_max_latency_ms * 1000)) { // consider the host only if not too far + if (gtid_trxid) { + if (MyHGM->gtid_exists(mysrvc, gtid_uuid, gtid_trxid)) { + sum += mysrvc->weight; + TotalUsedConn += mysrvc->ConnectionsUsed->conns_length(); + mysrvcCandidates[num_candidates] = mysrvc; + num_candidates++; + } } - } else { - if (max_lag_ms >= 0) { - if ((unsigned int)max_lag_ms >= mysrvc->aws_aurora_current_lag_us/1000) { - sum+=mysrvc->weight; - TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); - mysrvcCandidates[num_candidates]=mysrvc; + else { + if (max_lag_ms >= 0) { + if ((unsigned int)max_lag_ms >= mysrvc->aws_aurora_current_lag_us / 1000) { + sum += mysrvc->weight; + TotalUsedConn += mysrvc->ConnectionsUsed->conns_length(); + mysrvcCandidates[num_candidates] = mysrvc; + num_candidates++; + } + else { + sess->thread->status_variables.stvar[st_var_aws_aurora_replicas_skipped_during_query]++; + } + } + else { + sum += mysrvc->weight; + TotalUsedConn += mysrvc->ConnectionsUsed->conns_length(); + mysrvcCandidates[num_candidates] = mysrvc; num_candidates++; - } else { - sess->thread->status_variables.stvar[st_var_aws_aurora_replicas_skipped_during_query]++; } - } else { - sum+=mysrvc->weight; - TotalUsedConn+=mysrvc->ConnectionsUsed->conns_length(); - mysrvcCandidates[num_candidates]=mysrvc; - num_candidates++; } } } + else { + max_connections_reached = true; + } } else { - max_connections_reached = true; + mysrvc->myhgc->log_num_online_server_count_error(); } } else { if (mysrvc->status==MYSQL_SERVER_STATUS_SHUNNED) { diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 1dc1c19436..31fc36001e 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -4649,16 +4649,20 @@ int MySQL_Session::get_pkts_from_client(bool& wrong_pass, PtrSize_t& pkt) { // 0 : no action // -1 : the calling function will return // 1 : call to NEXT_IMMEDIATE -int MySQL_Session::handler_ProcessingQueryError_CheckBackendConnectionStatus(MySQL_Data_Stream *myds) { - MySQL_Connection *myconn = myds->myconn; +int MySQL_Session::handler_ProcessingQueryError_CheckBackendConnectionStatus(MySQL_Data_Stream* myds) { + MySQL_Connection* myconn = myds->myconn; + + const bool online_servers_within_threshold = myconn->parent->myhgc->online_servers_within_threshold(); // the query failed if ( // due to #774 , we now read myconn->server_status instead of myconn->parent->status - (myconn->server_status==MYSQL_SERVER_STATUS_OFFLINE_HARD) // the query failed because the server is offline hard + (myconn->server_status == MYSQL_SERVER_STATUS_OFFLINE_HARD) // the query failed because the server is offline hard + || + (myconn->server_status == MYSQL_SERVER_STATUS_SHUNNED && myconn->parent->shunned_automatic == true && myconn->parent->shunned_and_kill_all_connections == true) // the query failed because the server is shunned due to a serious failure || - (myconn->server_status==MYSQL_SERVER_STATUS_SHUNNED && myconn->parent->shunned_automatic==true && myconn->parent->shunned_and_kill_all_connections==true) // the query failed because the server is shunned due to a serious failure + (myconn->server_status == MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG) // slave is lagging! see #774 || - (myconn->server_status==MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG) // slave is lagging! see #774 + (online_servers_within_threshold == false) // number of online servers in a hostgroup exceeds the configured maximum servers ) { if (mysql_thread___connect_timeout_server_max) { myds->max_connect_time=thread->curtime+mysql_thread___connect_timeout_server_max*1000; @@ -4668,6 +4672,10 @@ int MySQL_Session::handler_ProcessingQueryError_CheckBackendConnectionStatus(MyS thread->status_variables.stvar[st_var_backend_lagging_during_query]++; proxy_error("Detected a lagging server during query: %s, %d, session_id:%u\n", myconn->parent->address, myconn->parent->port, this->thread_session_id); MyHGM->p_update_mysql_error_counter(p_mysql_error_type::proxysql, myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, ER_PROXYSQL_LAGGING_SRV); + } else if (myconn->server_status == MYSQL_SERVER_STATUS_ONLINE && online_servers_within_threshold == false) { + //proxy_error("Number of online servers detected in a hostgroup exceeds the configured maximum online servers. %s, %d, hostgroup:%u, num_online_servers:%u, max_online_servers:%u, session_id:%u\n", + // myconn->parent->address, myconn->parent->port, myconn->parent->myhgc->hid, num_online_servers, myconn->parent->myhgc->attributes.max_num_online_servers, this->thread_session_id); + myconn->parent->myhgc->log_num_online_server_count_error(); } else { thread->status_variables.stvar[st_var_backend_offline_during_query]++; proxy_error("Detected an offline server during query: %s, %d, session_id:%u\n", myconn->parent->address, myconn->parent->port, this->thread_session_id); diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index 37c24b2db2..c763179092 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -2041,7 +2041,9 @@ bool MySQL_Connection::IsServerOffline() { || (server_status==MYSQL_SERVER_STATUS_SHUNNED && parent->shunned_automatic==true && parent->shunned_and_kill_all_connections==true) // the server is SHUNNED due to a serious issue || - (server_status==MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG) // slave is lagging! see #774 + (server_status==MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG) // slave is lagging! see #774 + || + (parent->myhgc->online_servers_within_threshold() == false) // number of online servers in a hostgroup exceeds the configured maximum servers ) { ret=true; } From e91bcd2c819d6d3555dc1caa6214653a107cb3ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Tue, 23 Apr 2024 09:01:49 +0200 Subject: [PATCH 14/79] Move Admin 'HTTP_Server' and 'RESTAPI' init to end of phase3 - Closes #4510 Prevents invalid accesses that this modules can perform on other yet non-initialized modules during boot process. --- include/proxysql_admin.h | 14 ++ lib/ProxySQL_Admin.cpp | 324 ++++++++++++++++++++------------------- src/main.cpp | 12 +- 3 files changed, 194 insertions(+), 156 deletions(-) diff --git a/include/proxysql_admin.h b/include/proxysql_admin.h index 6906091cc7..d46933a331 100644 --- a/include/proxysql_admin.h +++ b/include/proxysql_admin.h @@ -394,6 +394,8 @@ class ProxySQL_Admin { void public_add_active_users(enum cred_username_type usertype, char *user=NULL) { __add_active_users(usertype, user); } + // @brief True if all ProxySQL modules have been already started. End of 'phase3'. + bool all_modules_started; ProxySQL_Admin(); ~ProxySQL_Admin(); SQLite3DB *admindb; // in memory @@ -416,6 +418,18 @@ class ProxySQL_Admin { */ bool init(const bootstrap_info_t& bootstrap_info); void init_ldap(); + /** @brief Initializes the HTTP server. For safety should be called after 'phase3'. */ + void init_http_server(); + /** + * @brief Loads the HTTP server config to runtime if all modules are ready, no-op otherwise. + * @details Modules ready when 'all_modules_started=true'. See 'all_modules_started'. + */ + void load_http_server(); + /** + * @brief Loads the RESTAPI server config to runtime if all modules are ready, no-op otherwise. + * @details Modules ready when 'all_modules_started=true'. See 'all_modules_started'. + */ + void load_restapi_server(); bool get_read_only() { return variables.admin_read_only; } bool set_read_only(bool ro) { variables.admin_read_only=ro; return variables.admin_read_only; } bool has_variable(const char *name); diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index 17ac811b0c..ae9b3e4502 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -6115,6 +6115,12 @@ void ProxySQL_Admin::init_ldap() { } } +void ProxySQL_Admin::init_http_server() { + AdminHTTPServer = new ProxySQL_HTTP_Server(); + AdminHTTPServer->init(); + AdminHTTPServer->print_version(); +} + struct boot_srv_info_t { string member_id; string member_host; @@ -6381,10 +6387,6 @@ bool ProxySQL_Admin::init(const bootstrap_info_t& bootstrap_info) { cpu_timer cpt; Admin_HTTP_Server = NULL; - AdminHTTPServer = new ProxySQL_HTTP_Server(); - AdminHTTPServer->init(); - AdminHTTPServer->print_version(); - AdminRestApiServer = NULL; /* AdminRestApiServer = new ProxySQL_RESTAPI_Server(); @@ -7220,6 +7222,167 @@ void ProxySQL_Admin::load_or_update_global_settings(SQLite3DB *db) { } } +void ProxySQL_Admin::load_restapi_server() { + if (!all_modules_started) { return; } + + std::function(const httpserver::http_request&)> prometheus_callback { + [this](const httpserver::http_request& request) { + auto headers = request_headers(request); + auto serial_response = this->serial_exposer(headers); + auto http_response = make_response(serial_response); + + return http_response; + } + }; + + bool free_restapi_port = false; + + // Helper lambda taking a boolean reference as a parameter to check if 'restapi_port' is available. + // In case of port not being free or error, logs an error 'ProxySQL_RestAPI_Server' isn't able to be started. + const auto check_restapi_port = [&](bool& restapi_port_free) -> void { + int e_port_check = check_port_availability(variables.restapi_port, &restapi_port_free); + + if (restapi_port_free == false) { + if (e_port_check == -1) { + proxy_error("Unable to start 'ProxySQL_RestAPI_Server', failed to set 'SO_REUSEADDR' to check port availability.\n"); + } else { + proxy_error( + "Unable to start 'ProxySQL_RestAPI_Server', port '%d' already in use.\n", + variables.restapi_port + ); + } + } + }; + + if (variables.restapi_enabled != variables.restapi_enabled_old) { + if (variables.restapi_enabled) { + check_restapi_port(free_restapi_port); + } + + if (variables.restapi_enabled && free_restapi_port) { + AdminRestApiServer = new ProxySQL_RESTAPI_Server( + variables.restapi_port, {{"/metrics", prometheus_callback}} + ); + } else { + delete AdminRestApiServer; + AdminRestApiServer = NULL; + } + variables.restapi_enabled_old = variables.restapi_enabled; + } else { + if (variables.restapi_port != variables.restapi_port_old) { + if (AdminRestApiServer) { + delete AdminRestApiServer; + AdminRestApiServer = NULL; + } + + if (variables.restapi_enabled) { + check_restapi_port(free_restapi_port); + } + + if (variables.restapi_enabled && free_restapi_port) { + AdminRestApiServer = new ProxySQL_RESTAPI_Server( + variables.restapi_port, {{"/metrics", prometheus_callback}} + ); + } + variables.restapi_port_old = variables.restapi_port; + } + } +} + +void ProxySQL_Admin::load_http_server() { + if (!all_modules_started) { return; } + + if (variables.web_enabled != variables.web_enabled_old) { + if (variables.web_enabled) { + if (GloVars.web_interface_plugin == NULL) { + char *key_pem; + char *cert_pem; + GloVars.get_SSL_pem_mem(&key_pem, &cert_pem); + Admin_HTTP_Server = MHD_start_daemon(MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG | MHD_USE_SSL, + variables.web_port, + NULL, NULL, http_handler, NULL, + MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120, MHD_OPTION_STRICT_FOR_CLIENT, (int) 1, + MHD_OPTION_THREAD_POOL_SIZE, (unsigned int) 4, + MHD_OPTION_NONCE_NC_SIZE, (unsigned int) 300, + MHD_OPTION_HTTPS_MEM_KEY, key_pem, + MHD_OPTION_HTTPS_MEM_CERT, cert_pem, + MHD_OPTION_END); + free(key_pem); + free(cert_pem); + } else { + if (GloWebInterface) { + int sfd = 0; + int reuseaddr = 1; + struct sockaddr_in tmp_addr; + + sfd = socket(AF_INET, SOCK_STREAM, 0); + memset(&tmp_addr, 0, sizeof(tmp_addr)); + tmp_addr.sin_family = AF_INET; + tmp_addr.sin_port = htons(variables.web_port); + tmp_addr.sin_addr.s_addr = INADDR_ANY; + + if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuseaddr, sizeof(reuseaddr)) == -1) { + close(sfd); + proxy_error( + "Unable to start WebInterfacePlugin, failed to set 'SO_REUSEADDR' to check port '%d' availability.\n", + variables.web_port + ); + } else { + if (::bind(sfd, (struct sockaddr*)&tmp_addr, (socklen_t)sizeof(tmp_addr)) == -1) { + close(sfd); + proxy_error( + "Unable to start WebInterfacePlugin, port '%d' already in use.\n", + variables.web_port + ); + } else { + close(sfd); + GloWebInterface->start(variables.web_port); + } + } + } + } + } else { + if (GloVars.web_interface_plugin == NULL) { + MHD_stop_daemon(Admin_HTTP_Server); + Admin_HTTP_Server = NULL; + } else { + if (GloWebInterface) { + GloWebInterface->stop(); + } + } + } + variables.web_enabled_old = variables.web_enabled; + } else { + if (variables.web_port != variables.web_port_old) { + if (variables.web_enabled) { + if (GloVars.web_interface_plugin == NULL) { + MHD_stop_daemon(Admin_HTTP_Server); + Admin_HTTP_Server = NULL; + char *key_pem; + char *cert_pem; + GloVars.get_SSL_pem_mem(&key_pem, &cert_pem); + Admin_HTTP_Server = MHD_start_daemon(MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG | MHD_USE_SSL, + variables.web_port, + NULL, NULL, http_handler, NULL, + MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120, MHD_OPTION_STRICT_FOR_CLIENT, (int) 1, + MHD_OPTION_THREAD_POOL_SIZE, (unsigned int) 4, + MHD_OPTION_NONCE_NC_SIZE, (unsigned int) 300, + MHD_OPTION_HTTPS_MEM_KEY, key_pem, + MHD_OPTION_HTTPS_MEM_CERT, cert_pem, + MHD_OPTION_END); + free(key_pem); + free(cert_pem); + } else { + if (GloWebInterface) { + GloWebInterface->start(variables.web_port); + } + } + } + variables.web_port_old = variables.web_port; + } + } +} + void ProxySQL_Admin::flush_admin_variables___database_to_runtime( SQLite3DB *db, bool replace, const string& checksum, const time_t epoch, bool lock ) { @@ -7308,157 +7471,8 @@ void ProxySQL_Admin::flush_admin_variables___database_to_runtime( } wrunlock(); { - std::function(const httpserver::http_request&)> prometheus_callback { - [this](const httpserver::http_request& request) { - auto headers = request_headers(request); - auto serial_response = this->serial_exposer(headers); - auto http_response = make_response(serial_response); - - return http_response; - } - }; - - bool free_restapi_port = false; - - // Helper lambda taking a boolean reference as a parameter to check if 'restapi_port' is available. - // In case of port not being free or error, logs an error 'ProxySQL_RestAPI_Server' isn't able to be started. - const auto check_restapi_port = [&](bool& restapi_port_free) -> void { - int e_port_check = check_port_availability(variables.restapi_port, &restapi_port_free); - - if (restapi_port_free == false) { - if (e_port_check == -1) { - proxy_error("Unable to start 'ProxySQL_RestAPI_Server', failed to set 'SO_REUSEADDR' to check port availability.\n"); - } else { - proxy_error( - "Unable to start 'ProxySQL_RestAPI_Server', port '%d' already in use.\n", - variables.restapi_port - ); - } - } - }; - - if (variables.restapi_enabled != variables.restapi_enabled_old) { - if (variables.restapi_enabled) { - check_restapi_port(free_restapi_port); - } - - if (variables.restapi_enabled && free_restapi_port) { - AdminRestApiServer = new ProxySQL_RESTAPI_Server( - variables.restapi_port, {{"/metrics", prometheus_callback}} - ); - } else { - delete AdminRestApiServer; - AdminRestApiServer = NULL; - } - variables.restapi_enabled_old = variables.restapi_enabled; - } else { - if (variables.restapi_port != variables.restapi_port_old) { - if (AdminRestApiServer) { - delete AdminRestApiServer; - AdminRestApiServer = NULL; - } - - if (variables.restapi_enabled) { - check_restapi_port(free_restapi_port); - } - - if (variables.restapi_enabled && free_restapi_port) { - AdminRestApiServer = new ProxySQL_RESTAPI_Server( - variables.restapi_port, {{"/metrics", prometheus_callback}} - ); - } - variables.restapi_port_old = variables.restapi_port; - } - } - if (variables.web_enabled != variables.web_enabled_old) { - if (variables.web_enabled) { - if (GloVars.web_interface_plugin == NULL) { - char *key_pem; - char *cert_pem; - GloVars.get_SSL_pem_mem(&key_pem, &cert_pem); - Admin_HTTP_Server = MHD_start_daemon(MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG | MHD_USE_SSL, - variables.web_port, - NULL, NULL, http_handler, NULL, - MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120, MHD_OPTION_STRICT_FOR_CLIENT, (int) 1, - MHD_OPTION_THREAD_POOL_SIZE, (unsigned int) 4, - MHD_OPTION_NONCE_NC_SIZE, (unsigned int) 300, - MHD_OPTION_HTTPS_MEM_KEY, key_pem, - MHD_OPTION_HTTPS_MEM_CERT, cert_pem, - MHD_OPTION_END); - free(key_pem); - free(cert_pem); - } else { - if (GloWebInterface) { - int sfd = 0; - int reuseaddr = 1; - struct sockaddr_in tmp_addr; - - sfd = socket(AF_INET, SOCK_STREAM, 0); - memset(&tmp_addr, 0, sizeof(tmp_addr)); - tmp_addr.sin_family = AF_INET; - tmp_addr.sin_port = htons(variables.web_port); - tmp_addr.sin_addr.s_addr = INADDR_ANY; - - if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuseaddr, sizeof(reuseaddr)) == -1) { - close(sfd); - proxy_error( - "Unable to start WebInterfacePlugin, failed to set 'SO_REUSEADDR' to check port '%d' availability.\n", - variables.web_port - ); - } else { - if (::bind(sfd, (struct sockaddr*)&tmp_addr, (socklen_t)sizeof(tmp_addr)) == -1) { - close(sfd); - proxy_error( - "Unable to start WebInterfacePlugin, port '%d' already in use.\n", - variables.web_port - ); - } else { - close(sfd); - GloWebInterface->start(variables.web_port); - } - } - } - } - } else { - if (GloVars.web_interface_plugin == NULL) { - MHD_stop_daemon(Admin_HTTP_Server); - Admin_HTTP_Server = NULL; - } else { - if (GloWebInterface) { - GloWebInterface->stop(); - } - } - } - variables.web_enabled_old = variables.web_enabled; - } else { - if (variables.web_port != variables.web_port_old) { - if (variables.web_enabled) { - if (GloVars.web_interface_plugin == NULL) { - MHD_stop_daemon(Admin_HTTP_Server); - Admin_HTTP_Server = NULL; - char *key_pem; - char *cert_pem; - GloVars.get_SSL_pem_mem(&key_pem, &cert_pem); - Admin_HTTP_Server = MHD_start_daemon(MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG | MHD_USE_SSL, - variables.web_port, - NULL, NULL, http_handler, NULL, - MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120, MHD_OPTION_STRICT_FOR_CLIENT, (int) 1, - MHD_OPTION_THREAD_POOL_SIZE, (unsigned int) 4, - MHD_OPTION_NONCE_NC_SIZE, (unsigned int) 300, - MHD_OPTION_HTTPS_MEM_KEY, key_pem, - MHD_OPTION_HTTPS_MEM_CERT, cert_pem, - MHD_OPTION_END); - free(key_pem); - free(cert_pem); - } else { - if (GloWebInterface) { - GloWebInterface->start(variables.web_port); - } - } - } - variables.web_port_old = variables.web_port; - } - } + load_http_server(); + load_restapi_server(); // Update the admin variable for 'web_verbosity' admin___web_verbosity = variables.web_verbosity; } diff --git a/src/main.cpp b/src/main.cpp index aaac09822c..cfe5f4ff93 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1145,7 +1145,6 @@ void ProxySQL_Main_init_phase3___start_all() { GloAdmin->init_mysql_servers(); GloAdmin->init_proxysql_servers(); GloAdmin->load_scheduler_to_runtime(); - GloAdmin->proxysql_restapi().load_restapi_to_runtime(); #ifdef DEBUG std::cerr << "Main phase3 : GloAdmin initialized in "; #endif @@ -1217,6 +1216,17 @@ void ProxySQL_Main_init_phase3___start_all() { if (GloMyLdapAuth) { GloAdmin->init_ldap_variables(); } + + // HTTP Server should be initialized after other modules. See #4510 + GloAdmin->init_http_server(); + GloAdmin->proxysql_restapi().load_restapi_to_runtime(); + + // Signal ProxySQL_Admin that all modules have been started + GloAdmin->all_modules_started = true; + + // Load the config not previously loaded for these modules + GloAdmin->load_http_server(); + GloAdmin->load_restapi_server(); } From 50972bd15d849f4a242822397fa9900aee1b6d14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Tue, 23 Apr 2024 09:15:52 +0200 Subject: [PATCH 15/79] Fix user-after-free on HTTP_Server 'system' ept --- lib/ProxySQL_HTTP_Server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ProxySQL_HTTP_Server.cpp b/lib/ProxySQL_HTTP_Server.cpp index ff38885582..d8affaf396 100644 --- a/lib/ProxySQL_HTTP_Server.cpp +++ b/lib/ProxySQL_HTTP_Server.cpp @@ -586,7 +586,7 @@ int ProxySQL_HTTP_Server::handler(void *cls, struct MHD_Connection *connection, delete cpu_sqlite; s.append(""); - response = MHD_create_response_from_buffer(s.length(), (void *) s.c_str(), MHD_RESPMEM_PERSISTENT); + response = MHD_create_response_from_buffer(s.length(), (void *) s.c_str(), MHD_RESPMEM_MUST_COPY); ret = MHD_queue_response (connection, MHD_HTTP_OK, response); MHD_destroy_response (response); return ret; From 42c8bb83c6b272305ec1f704b872129d689dbcdf Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Tue, 23 Apr 2024 12:46:35 +0500 Subject: [PATCH 16/79] Few fixes --- include/MySQL_HostGroups_Manager.h | 7 ++++++- lib/MySrvC.cpp | 8 ++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/include/MySQL_HostGroups_Manager.h b/include/MySQL_HostGroups_Manager.h index 5311b57a50..416ef1e76b 100644 --- a/include/MySQL_HostGroups_Manager.h +++ b/include/MySQL_HostGroups_Manager.h @@ -199,7 +199,7 @@ class MySrvStatus { MySrvStatus(MySrvC* _mysrvc); ~MySrvStatus(); - enum MySerStatus operator=(enum MySerStatus _status); + MySrvStatus& operator=(enum MySerStatus _status); inline operator enum MySerStatus() const { @@ -211,6 +211,11 @@ class MySrvStatus { return status == _status; } + inline + bool operator==(const MySrvStatus& _status) const { + return status == _status.status; + } + private: MySrvC *mysrvc; enum MySerStatus status; diff --git a/lib/MySrvC.cpp b/lib/MySrvC.cpp index 4fd3e09d1d..bf6c94487a 100644 --- a/lib/MySrvC.cpp +++ b/lib/MySrvC.cpp @@ -199,10 +199,10 @@ MySrvC::~MySrvC() { MySrvStatus::MySrvStatus(MySrvC* _mysrvc) : mysrvc(_mysrvc) {} -MySrvStatus::~MySrvStatus() { mysrvc->myhgc->refresh_online_server_count(); } +MySrvStatus::~MySrvStatus() { if (mysrvc->myhgc) mysrvc->myhgc->refresh_online_server_count(); } -enum MySerStatus MySrvStatus::operator=(enum MySerStatus _status) { +MySrvStatus& MySrvStatus::operator=(enum MySerStatus _status) { status = _status; - if (mysrvc->myhgc) - mysrvc->myhgc->refresh_online_server_count(); + if (mysrvc->myhgc) mysrvc->myhgc->refresh_online_server_count(); + return *this; } From c846a3eaf9fbcf42f7e9b7352aaadded921a76d8 Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Tue, 23 Apr 2024 14:01:45 +0500 Subject: [PATCH 17/79] Removed online server error log from push_MyConn_to_pool --- lib/MySQL_HostGroups_Manager.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/MySQL_HostGroups_Manager.cpp b/lib/MySQL_HostGroups_Manager.cpp index 45e866d19b..8d4dcfede2 100644 --- a/lib/MySQL_HostGroups_Manager.cpp +++ b/lib/MySQL_HostGroups_Manager.cpp @@ -2362,9 +2362,7 @@ void MySQL_HostGroups_Manager::push_MyConn_to_pool(MySQL_Connection *c, bool _lo if (mysrvc->status==MYSQL_SERVER_STATUS_ONLINE) { if (c->async_state_machine==ASYNC_IDLE) { if (GloMTH == NULL) { goto __exit_push_MyConn_to_pool; } - const bool online_servers_within_threshold = c->parent->myhgc->online_servers_within_threshold(); - if (/*online_servers_within_threshold == true &&*/ // if online servers in a hostgroup surpass the configured maximum, add connection to ConnectionFree pool - c->local_stmts->get_num_backend_stmts() > (unsigned int)GloMTH->variables.max_stmts_per_connection) { // Check if the connection has too many prepared statements + if (c->local_stmts->get_num_backend_stmts() > (unsigned int)GloMTH->variables.max_stmts_per_connection) { // Check if the connection has too many prepared statements // Log debug information about destroying the connection due to too many prepared statements proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Destroying MySQL_Connection %p, server %s:%d with status %d because has too many prepared statements\n", c, mysrvc->address, mysrvc->port, mysrvc->status); // delete c; @@ -2373,9 +2371,6 @@ void MySQL_HostGroups_Manager::push_MyConn_to_pool(MySQL_Connection *c, bool _lo } else { c->optimize(); // Optimize the connection mysrvc->ConnectionsFree->add(c); // Add the connection to the list of free connections - if (online_servers_within_threshold == false) { - mysrvc->myhgc->log_num_online_server_count_error(); - } } } else { // Log debug information about destroying the connection From 363263ff451142e8bdac1a03134a78af6a1ba874 Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Tue, 23 Apr 2024 14:18:21 +0500 Subject: [PATCH 18/79] Implemented shutdown case handling within refresh_online_server_count --- lib/MyHGC.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/MyHGC.cpp b/lib/MyHGC.cpp index 746c41736d..bc347af9cc 100644 --- a/lib/MyHGC.cpp +++ b/lib/MyHGC.cpp @@ -390,6 +390,9 @@ MySrvC *MyHGC::get_random_MySrvC(char * gtid_uuid, uint64_t gtid_trxid, int max_ } void MyHGC::refresh_online_server_count() { + if (__sync_fetch_and_add(&glovars.shutdown, 0) != 0) + return; + assert(MyHGM->is_locked); unsigned int online_servers_count = 0; From f190c0ad0fc1d5b6c8f18b461f746c7e57f27799 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Tue, 23 Apr 2024 16:08:19 +0200 Subject: [PATCH 19/79] Handle 'SELECT LAST_INSERT_ID() FROM DUAL' - Closes #4493 --- lib/MySQL_Session.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 1dc1c19436..4156b58a49 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -44,6 +44,8 @@ #define SELECT_CONNECTION_ID_LEN 22 #define SELECT_LAST_INSERT_ID "SELECT LAST_INSERT_ID()" #define SELECT_LAST_INSERT_ID_LEN 23 +#define SELECT_LAST_INSERT_ID_FROM_DUAL "SELECT LAST_INSERT_ID() FROM DUAL" +#define SELECT_LAST_INSERT_ID_FROM_DUAL_LEN 33 #define SELECT_LAST_INSERT_ID_LIMIT1 "SELECT LAST_INSERT_ID() LIMIT 1" #define SELECT_LAST_INSERT_ID_LIMIT1_LEN 31 #define SELECT_VARIABLE_IDENTITY "SELECT @@IDENTITY" @@ -7267,6 +7269,8 @@ bool MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C if ( (pkt->size==SELECT_LAST_INSERT_ID_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncasecmp((char *)SELECT_LAST_INSERT_ID,(char *)pkt->ptr+5,pkt->size-5)==0) || + (pkt->size==SELECT_LAST_INSERT_ID_FROM_DUAL_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncasecmp((char *)SELECT_LAST_INSERT_ID_FROM_DUAL,(char *)pkt->ptr+5,pkt->size-5)==0) + || (pkt->size==SELECT_LAST_INSERT_ID_LIMIT1_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncasecmp((char *)SELECT_LAST_INSERT_ID_LIMIT1,(char *)pkt->ptr+5,pkt->size-5)==0) || (pkt->size==SELECT_VARIABLE_IDENTITY_LEN+5 && *((char *)(pkt->ptr)+4)==(char)0x03 && strncasecmp((char *)SELECT_VARIABLE_IDENTITY,(char *)pkt->ptr+5,pkt->size-5)==0) From 47d22b8a85d7c8b688270a01dc67c9a02d7cbea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Tue, 23 Apr 2024 16:09:52 +0200 Subject: [PATCH 20/79] Update 'LAST_INSERT_ID' tap test with 'FROM DUAL' support --- test/tap/tests/mysql-last_insert_id-t.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/tap/tests/mysql-last_insert_id-t.cpp b/test/tap/tests/mysql-last_insert_id-t.cpp index 1e8e9d18b1..90549afa4c 100644 --- a/test/tap/tests/mysql-last_insert_id-t.cpp +++ b/test/tap/tests/mysql-last_insert_id-t.cpp @@ -18,8 +18,9 @@ inline unsigned long long monotonic_time() { } -std::string queries[4] = { +std::string queries[5] = { "SELECT LAST_INSERT_ID() LIMIT 1", + "SELECT LAST_INSERT_ID() FROM DUAL", "SELECT LAST_INSERT_ID()", "SELECT @@IDENTITY LIMIT 1", "SELECT @@IDENTITY" From 354bf8135fcdd79c37acf8a2c83041ce8970aa9f Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Wed, 24 Apr 2024 02:08:04 +0500 Subject: [PATCH 21/79] Added TAP test --- ..._hostgroup_attributes_online_servers-t.cpp | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 test/tap/tests/test_hostgroup_attributes_online_servers-t.cpp diff --git a/test/tap/tests/test_hostgroup_attributes_online_servers-t.cpp b/test/tap/tests/test_hostgroup_attributes_online_servers-t.cpp new file mode 100644 index 0000000000..e6d7863a19 --- /dev/null +++ b/test/tap/tests/test_hostgroup_attributes_online_servers-t.cpp @@ -0,0 +1,143 @@ +/** + * @file test_hostgroup_attributes_online_servers-t.cpp + * @brief This test will evaluate configured maximum number of online servers within a hostgroup operates correctly. + * Note: + * This test is based on the assumption that ProxySQL is configured with read and write splitting, with writer servers in hostgroup 0, + * and readers in hostgroup 1 (having multiple servers). + */ + +#include +#include +#include +#include +#include "mysql.h" +#include "mysqld_error.h" +#include "tap.h" +#include "command_line.h" +#include "utils.h" + +#define MYSQL_QUERY__(mysql, query) \ + do { \ + if (mysql_query(mysql, query)) { \ + fprintf(stderr, "File %s, line %d, Error: %s\n", \ + __FILE__, __LINE__, mysql_error(mysql)); \ + goto cleanup; \ + } \ + } while(0) + +#define MYSQL_CLEAR_RESULT(mysql) mysql_free_result(mysql_store_result(mysql)); +#define NUM_QUERY_EXEC 5 + + +std::tuple execute_query_and_check_result(MYSQL* proxysql, const char* query, bool is_select, + bool should_succeed, unsigned int iteration = 1) { + bool res = true; + unsigned int errcode = 0; + unsigned int i; + for (i = 0; i < iteration; i++) { + const int result = mysql_query(proxysql, query); + if (result) errcode = mysql_errno(proxysql); + if (result == 0) { MYSQL_CLEAR_RESULT(proxysql); } + if ((should_succeed && result != 0) || (!should_succeed && result == 0)) { + res = false; + break; + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + return std::make_tuple(res, errcode, i); +} + +int main(int argc, char** argv) { + CommandLine cl; + + if (cl.getEnv()) { + diag("Failed to get the required environmental variables."); + return -1; + } + + plan(4*3); + + // Initialize Admin connection + MYSQL* proxysql_admin = mysql_init(NULL); + if (!proxysql_admin) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin)); + return -1; + } + // Connnect to ProxySQL Admin + if (!mysql_real_connect(proxysql_admin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql_admin)); + return -1; + } + + // Initialize ProxySQL connection + MYSQL* proxysql = mysql_init(NULL); + if (!proxysql) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql)); + return -1; + } + + // Connect to ProxySQL + if (!mysql_real_connect(proxysql, cl.host, cl.username, cl.password, NULL, cl.port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxysql)); + return exit_status(); + } + + bool result; + unsigned int errcode; + unsigned int query_exec_count; + + diag("## Pre-test Check ##\n"); + diag("Executing query... [Reader HG]\n"); + MYSQL_QUERY__(proxysql, "SELECT 1"); + MYSQL_CLEAR_RESULT(proxysql); + diag("Executing query... [Writer HG]\n"); + MYSQL_QUERY__(proxysql, "DO 1"); + MYSQL_CLEAR_RESULT(proxysql); + diag("## Done\n"); + + diag("## Starting test ##\n"); + diag("Setting max_num_online_servers=1 in hostgroup: 1...\n"); + MYSQL_QUERY__(proxysql_admin, "DELETE FROM mysql_hostgroup_attributes WHERE hostgroup_id=1"); + MYSQL_QUERY__(proxysql_admin, "INSERT INTO mysql_hostgroup_attributes (hostgroup_id, max_num_online_servers) values (1,1)"); + MYSQL_QUERY__(proxysql_admin, "LOAD MYSQL SERVERS TO RUNTIME"); + diag("Done\n"); + + diag("Executing query... [Reader HG]\n"); + std::tie(result, errcode, query_exec_count) = execute_query_and_check_result(proxysql, "SELECT 1", true, false, NUM_QUERY_EXEC); + ok(result, "Query execution should fail"); + ok(errcode == 9001, "Error code should be '9001'. Actual value:'%u'", errcode); + ok(query_exec_count == NUM_QUERY_EXEC, "Query execution count should be '%u'. Actual value:'%u'", NUM_QUERY_EXEC, query_exec_count); + + diag("Executing query... [Writer HG]\n"); + std::tie(result, errcode, query_exec_count) = execute_query_and_check_result(proxysql, "DO 1", false, true, NUM_QUERY_EXEC); + ok(result, "Query execution should succeed"); + ok(errcode == 0, "Error code should be '0'. Actual value:'%u'", errcode); + ok(query_exec_count == NUM_QUERY_EXEC, "Query execution count should be '%u'. Actual value:'%u'", NUM_QUERY_EXEC, query_exec_count); + + diag("Setting max_num_online_servers=100 in hostgroup: 1...\n"); + MYSQL_QUERY__(proxysql_admin, "UPDATE mysql_hostgroup_attributes SET max_num_online_servers=100 WHERE hostgroup_id=1"); + MYSQL_QUERY__(proxysql_admin, "LOAD MYSQL SERVERS TO RUNTIME"); + diag("Done\n"); + + diag("Executing query... [Reader HG]...\n"); + std::tie(result, errcode, query_exec_count) = execute_query_and_check_result(proxysql, "SELECT 1", true, true, NUM_QUERY_EXEC); + ok(result, "Query execution should succeed"); + ok(errcode == 0, "Error code should be '0'. Actual value:'%u'", errcode); + ok(query_exec_count == NUM_QUERY_EXEC, "Query execution count should be '%u'. Actual value:'%u'", NUM_QUERY_EXEC, query_exec_count); + + diag("Executing query... [Writer HG]\n"); + std::tie(result, errcode, query_exec_count) = execute_query_and_check_result(proxysql, "DO 1", false, true, NUM_QUERY_EXEC); + ok(result, "Query execution should succeed"); + ok(errcode == 0, "Error code should be '0'. Actual value:'%u'", errcode); + ok(query_exec_count == NUM_QUERY_EXEC, "Query execution count should be '%u'. Actual value:'%u'", NUM_QUERY_EXEC, query_exec_count); + diag("## Done\n"); + +cleanup: + MYSQL_QUERY(proxysql_admin, "DELETE FROM mysql_hostgroup_attributes WHERE hostgroup_id=1"); + MYSQL_QUERY(proxysql_admin, "LOAD MYSQL SERVERS TO RUNTIME"); + + mysql_close(proxysql); + mysql_close(proxysql_admin); + + return exit_status(); +} From 73b7b543b8aa7c9799089d3a11528c243120400a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Wed, 24 Apr 2024 03:39:26 +0000 Subject: [PATCH 22/79] Fixed a compilation with TEST_AURORA --- lib/MyHGC.cpp | 4 +++ lib/MySQL_HostGroups_Manager.cpp | 4 --- lib/MySrvC.cpp | 54 -------------------------------- 3 files changed, 4 insertions(+), 58 deletions(-) diff --git a/lib/MyHGC.cpp b/lib/MyHGC.cpp index 6daa6be295..9a2a8a0629 100644 --- a/lib/MyHGC.cpp +++ b/lib/MyHGC.cpp @@ -1,5 +1,9 @@ #include "MySQL_HostGroups_Manager.h" +#ifdef TEST_AURORA +static unsigned long long array_mysrvc_total = 0; +static unsigned long long array_mysrvc_cands = 0; +#endif // TEST_AURORA extern MySQL_Threads_Handler *GloMTH; diff --git a/lib/MySQL_HostGroups_Manager.cpp b/lib/MySQL_HostGroups_Manager.cpp index 90bc415367..705a06515a 100644 --- a/lib/MySQL_HostGroups_Manager.cpp +++ b/lib/MySQL_HostGroups_Manager.cpp @@ -31,10 +31,6 @@ using std::function; -#ifdef TEST_AURORA -static unsigned long long array_mysrvc_total = 0; -static unsigned long long array_mysrvc_cands = 0; -#endif // TEST_AURORA #define SAFE_SQLITE3_STEP(_stmt) do {\ do {\ diff --git a/lib/MySrvC.cpp b/lib/MySrvC.cpp index 94401e9016..572530d678 100644 --- a/lib/MySrvC.cpp +++ b/lib/MySrvC.cpp @@ -1,58 +1,4 @@ #include "MySQL_HostGroups_Manager.h" -/* -#include "proxysql.h" -#include "cpp.h" - -#include "MySQL_PreparedStatement.h" -#include "MySQL_Data_Stream.h" - -#include -#include -#include - -#include -#include -#include -#include - -#include "prometheus_helpers.h" -#include "proxysql_utils.h" - -#define char_malloc (char *)malloc -#define itostr(__s, __i) { __s=char_malloc(32); sprintf(__s, "%lld", __i); } - -#include "thread.h" -#include "wqueue.h" - -#include "ev.h" - -#include -#include -#include - -using std::function; - -#ifdef TEST_AURORA -static unsigned long long array_mysrvc_total = 0; -static unsigned long long array_mysrvc_cands = 0; -#endif // TEST_AURORA - -#define SAFE_SQLITE3_STEP(_stmt) do {\ - do {\ - rc=(*proxy_sqlite3_step)(_stmt);\ - if (rc!=SQLITE_DONE) {\ - assert(rc==SQLITE_LOCKED);\ - usleep(100);\ - }\ - } while (rc!=SQLITE_DONE);\ -} while (0) - -extern ProxySQL_Admin *GloAdmin; - -extern MySQL_Threads_Handler *GloMTH; - -extern MySQL_Monitor *GloMyMon; -*/ class MySrvConnList; class MySrvC; From a081695ae96031239e1d1e4b2c85c9825e96fd4b Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Wed, 24 Apr 2024 12:57:22 +0500 Subject: [PATCH 23/79] * Solved few warnings * Enforced explicit conversion of MySrvStatus --- include/MySQL_HostGroups_Manager.h | 21 ++++++++++------- lib/MySQL_HostGroups_Manager.cpp | 36 +++++++++++++++--------------- lib/MySrvC.cpp | 2 +- lib/MySrvConnList.cpp | 2 +- lib/mysql_connection.cpp | 16 ++++++------- 5 files changed, 41 insertions(+), 36 deletions(-) diff --git a/include/MySQL_HostGroups_Manager.h b/include/MySQL_HostGroups_Manager.h index 416ef1e76b..6d14670b70 100644 --- a/include/MySQL_HostGroups_Manager.h +++ b/include/MySQL_HostGroups_Manager.h @@ -196,24 +196,29 @@ class MySrvConnList { class MySrvC; class MySrvStatus { public: - MySrvStatus(MySrvC* _mysrvc); + explicit MySrvStatus(MySrvC* _mysrvc); ~MySrvStatus(); - MySrvStatus& operator=(enum MySerStatus _status); + MySrvStatus& operator=(MySerStatus _status); inline - operator enum MySerStatus() const { - return status; + bool operator==(MySerStatus _status) const { + return status == _status; } inline - bool operator==(enum MySerStatus _status) const { - return status == _status; + bool operator!=(MySerStatus _status) const { + return status!=_status; + } + + inline + explicit operator MySerStatus() const { + return status; } inline - bool operator==(const MySrvStatus& _status) const { - return status == _status.status; + explicit operator int() const { + return (int)status; } private: diff --git a/lib/MySQL_HostGroups_Manager.cpp b/lib/MySQL_HostGroups_Manager.cpp index 8d4dcfede2..eb7705e990 100644 --- a/lib/MySQL_HostGroups_Manager.cpp +++ b/lib/MySQL_HostGroups_Manager.cpp @@ -759,12 +759,12 @@ void MySQL_HostGroups_Manager::p_update_mysql_error_counter(p_mysql_error_type e } void MySQL_HostGroups_Manager::wrunlock() { + is_locked = false; #ifdef MHM_PTHREAD_MUTEX pthread_mutex_unlock(&lock); #else spin_wrunlock(&rwlock); #endif - is_locked = false; } @@ -1392,7 +1392,7 @@ bool MySQL_HostGroups_Manager::commit( } if (change_server_status == true) { if (GloMTH->variables.hostgroup_manager_verbose) - proxy_info("Changing status for server %d:%s:%d (%s:%d) from %d (%d) to %d\n", mysrvc->myhgc->hid, mysrvc->address, mysrvc->port, r->fields[1], atoi(r->fields[2]), atoi(r->fields[5]), mysrvc->status, atoi(r->fields[15])); + proxy_info("Changing status for server %d:%s:%d (%s:%d) from %d (%d) to %d\n", mysrvc->myhgc->hid, mysrvc->address, mysrvc->port, r->fields[1], atoi(r->fields[2]), atoi(r->fields[5]), (int)mysrvc->status, atoi(r->fields[15])); mysrvc->status = (MySerStatus)atoi(r->fields[15]); } if (mysrvc->status==MYSQL_SERVER_STATUS_SHUNNED) { @@ -1440,7 +1440,7 @@ bool MySQL_HostGroups_Manager::commit( } if (run_update) { rc=(*proxy_sqlite3_bind_int64)(statement2, 1, mysrvc->weight); ASSERT_SQLITE_OK(rc, mydb); - rc=(*proxy_sqlite3_bind_int64)(statement2, 2, mysrvc->status); ASSERT_SQLITE_OK(rc, mydb); + rc=(*proxy_sqlite3_bind_int64)(statement2, 2, (int)mysrvc->status); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement2, 3, mysrvc->compression); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement2, 4, mysrvc->max_connections); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement2, 5, mysrvc->max_replication_lag); ASSERT_SQLITE_OK(rc, mydb); @@ -1800,7 +1800,7 @@ void MySQL_HostGroups_Manager::generate_mysql_servers_table(int *_onlyhg) { mysrvc=myhgc->mysrvs->idx(j); if (mysql_thread___hostgroup_manager_verbose) { char *st; - switch (mysrvc->status) { + switch ((int)mysrvc->status) { case 0: st=(char *)"ONLINE"; break; @@ -1830,7 +1830,7 @@ void MySQL_HostGroups_Manager::generate_mysql_servers_table(int *_onlyhg) { rc=(*proxy_sqlite3_bind_int64)(statement32, (i*13)+3, mysrvc->port); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement32, (i*13)+4, mysrvc->gtid_port); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement32, (i*13)+5, mysrvc->weight); ASSERT_SQLITE_OK(rc, mydb); - rc=(*proxy_sqlite3_bind_int64)(statement32, (i*13)+6, mysrvc->status); ASSERT_SQLITE_OK(rc, mydb); + rc=(*proxy_sqlite3_bind_int64)(statement32, (i*13)+6, (int)mysrvc->status); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement32, (i*13)+7, mysrvc->compression); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement32, (i*13)+8, mysrvc->max_connections); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement32, (i*13)+9, mysrvc->max_replication_lag); ASSERT_SQLITE_OK(rc, mydb); @@ -1853,7 +1853,7 @@ void MySQL_HostGroups_Manager::generate_mysql_servers_table(int *_onlyhg) { rc=(*proxy_sqlite3_bind_int64)(statement1, 3, mysrvc->port); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement1, 4, mysrvc->gtid_port); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement1, 5, mysrvc->weight); ASSERT_SQLITE_OK(rc, mydb); - rc=(*proxy_sqlite3_bind_int64)(statement1, 6, mysrvc->status); ASSERT_SQLITE_OK(rc, mydb); + rc=(*proxy_sqlite3_bind_int64)(statement1, 6, (int)mysrvc->status); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement1, 7, mysrvc->compression); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement1, 8, mysrvc->max_connections); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement1, 9, mysrvc->max_replication_lag); ASSERT_SQLITE_OK(rc, mydb); @@ -2341,7 +2341,7 @@ void MySQL_HostGroups_Manager::push_MyConn_to_pool(MySQL_Connection *c, bool _lo mysrvc = static_cast(c->parent); // Log debug information about the connection being returned to the pool - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySQL_Connection %p, server %s:%d with status %d\n", c, mysrvc->address, mysrvc->port, mysrvc->status); + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySQL_Connection %p, server %s:%d with status %d\n", c, mysrvc->address, mysrvc->port, (int)mysrvc->status); // Remove the connection from the list of used connections for the parent server mysrvc->ConnectionsUsed->remove(c); @@ -2353,7 +2353,7 @@ void MySQL_HostGroups_Manager::push_MyConn_to_pool(MySQL_Connection *c, bool _lo // If the largest query length exceeds the threshold, destroy the connection if (c->largest_query_length > (unsigned int)GloMTH->variables.threshold_query_length) { - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Destroying MySQL_Connection %p, server %s:%d with status %d . largest_query_length = %lu\n", c, mysrvc->address, mysrvc->port, mysrvc->status, c->largest_query_length); + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Destroying MySQL_Connection %p, server %s:%d with status %d . largest_query_length = %lu\n", c, mysrvc->address, mysrvc->port, (int)mysrvc->status, c->largest_query_length); delete c; goto __exit_push_MyConn_to_pool; } @@ -2364,7 +2364,7 @@ void MySQL_HostGroups_Manager::push_MyConn_to_pool(MySQL_Connection *c, bool _lo if (GloMTH == NULL) { goto __exit_push_MyConn_to_pool; } if (c->local_stmts->get_num_backend_stmts() > (unsigned int)GloMTH->variables.max_stmts_per_connection) { // Check if the connection has too many prepared statements // Log debug information about destroying the connection due to too many prepared statements - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Destroying MySQL_Connection %p, server %s:%d with status %d because has too many prepared statements\n", c, mysrvc->address, mysrvc->port, mysrvc->status); + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Destroying MySQL_Connection %p, server %s:%d with status %d because has too many prepared statements\n", c, mysrvc->address, mysrvc->port, (int)mysrvc->status); // delete c; mysrvc->ConnectionsUsed->add(c); // Add the connection back to the list of used connections destroy_MyConn_from_pool(c, false); // Destroy the connection from the pool @@ -2374,12 +2374,12 @@ void MySQL_HostGroups_Manager::push_MyConn_to_pool(MySQL_Connection *c, bool _lo } } else { // Log debug information about destroying the connection - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Destroying MySQL_Connection %p, server %s:%d with status %d\n", c, mysrvc->address, mysrvc->port, mysrvc->status); + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Destroying MySQL_Connection %p, server %s:%d with status %d\n", c, mysrvc->address, mysrvc->port, (int)mysrvc->status); delete c; // Destroy the connection } } else { // Log debug information about destroying the connection - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Destroying MySQL_Connection %p, server %s:%d with status %d\n", c, mysrvc->address, mysrvc->port, mysrvc->status); + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Destroying MySQL_Connection %p, server %s:%d with status %d\n", c, mysrvc->address, mysrvc->port, (int)mysrvc->status); delete c; // Destroy the connection } @@ -2808,7 +2808,7 @@ void MySQL_HostGroups_Manager::group_replication_lag_action_set_server_status(My MySrvC *mysrvc=(MySrvC *)myhgc->mysrvs->servers->index(j); proxy_debug( PROXY_DEBUG_MONITOR, 6, "Server 'MySrvC' - address: %s, port: %d, status: %d\n", mysrvc->address, - mysrvc->port, mysrvc->status + mysrvc->port, (int)mysrvc->status ); if (strcmp(mysrvc->address,address)==0 && mysrvc->port==port) { @@ -3386,7 +3386,7 @@ void MySQL_HostGroups_Manager::p_update_connection_pool() { // proxysql_connection_pool_status metric p_update_connection_pool_update_gauge(endpoint_id, common_labels, - status.p_connection_pool_status_map, mysrvc->status + 1, p_hg_dyn_gauge::connection_pool_status); + status.p_connection_pool_status_map, ((int)mysrvc->status) + 1, p_hg_dyn_gauge::connection_pool_status); } } @@ -3469,7 +3469,7 @@ SQLite3_result * MySQL_HostGroups_Manager::SQL3_Connection_Pool(bool _reset, int pta[1]=strdup(mysrvc->address); sprintf(buf,"%d", mysrvc->port); pta[2]=strdup(buf); - switch (mysrvc->status) { + switch ((int)mysrvc->status) { case 0: pta[3]=strdup("ONLINE"); break; @@ -4057,7 +4057,7 @@ bool MySQL_HostGroups_Manager::shun_and_killall(char *hostname, int port) { for (j=0; jmysrvs->idx(j); if (mysrvc->port==port && strcmp(mysrvc->address,hostname)==0) { - switch (mysrvc->status) { + switch ((MySerStatus)mysrvc->status) { case MYSQL_SERVER_STATUS_SHUNNED: if (mysrvc->shunned_automatic==false) { break; @@ -7284,12 +7284,12 @@ MySrvC* MySQL_HostGroups_Manager::HostGroup_Server_Mapping::insert_HGM(unsigned if (!ret_srv) { if (GloMTH->variables.hostgroup_manager_verbose) { - proxy_info("Creating new server in HG %d : %s:%d , gtid_port=%d, weight=%ld, status=%d\n", hostgroup_id, srv->address, srv->port, srv->gtid_port, srv->weight, srv->status); + proxy_info("Creating new server in HG %d : %s:%d , gtid_port=%d, weight=%ld, status=%d\n", hostgroup_id, srv->address, srv->port, srv->gtid_port, srv->weight, (int)srv->status); } - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 5, "Adding new server %s:%d , weight=%ld, status=%d, mem_ptr=%p into hostgroup=%d\n", srv->address, srv->port, srv->weight, srv->status, srv, hostgroup_id); + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 5, "Adding new server %s:%d , weight=%ld, status=%d, mem_ptr=%p into hostgroup=%d\n", srv->address, srv->port, srv->weight, (int)srv->status, srv, hostgroup_id); - ret_srv = new MySrvC(srv->address, srv->port, srv->gtid_port, srv->weight, srv->status, srv->compression, + ret_srv = new MySrvC(srv->address, srv->port, srv->gtid_port, srv->weight, (MySerStatus)srv->status, srv->compression, srv->max_connections, srv->max_replication_lag, srv->use_ssl, (srv->max_latency_us / 1000), srv->comment); myhgc->mysrvs->add(ret_srv); diff --git a/lib/MySrvC.cpp b/lib/MySrvC.cpp index bf6c94487a..162cb36f2a 100644 --- a/lib/MySrvC.cpp +++ b/lib/MySrvC.cpp @@ -201,7 +201,7 @@ MySrvStatus::MySrvStatus(MySrvC* _mysrvc) : mysrvc(_mysrvc) {} MySrvStatus::~MySrvStatus() { if (mysrvc->myhgc) mysrvc->myhgc->refresh_online_server_count(); } -MySrvStatus& MySrvStatus::operator=(enum MySerStatus _status) { +MySrvStatus& MySrvStatus::operator=(MySerStatus _status) { status = _status; if (mysrvc->myhgc) mysrvc->myhgc->refresh_online_server_count(); return *this; diff --git a/lib/MySrvConnList.cpp b/lib/MySrvConnList.cpp index abe0c44ee7..ace65635e2 100644 --- a/lib/MySrvConnList.cpp +++ b/lib/MySrvConnList.cpp @@ -40,7 +40,7 @@ MySrvConnList::~MySrvConnList() { } void MySrvConnList::drop_all_connections() { - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Dropping all connections (%u total) on MySrvConnList %p for server %s:%d , hostgroup=%d , status=%d\n", conns_length(), this, mysrvc->address, mysrvc->port, mysrvc->myhgc->hid, mysrvc->status); + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Dropping all connections (%u total) on MySrvConnList %p for server %s:%d , hostgroup=%d , status=%d\n", conns_length(), this, mysrvc->address, mysrvc->port, mysrvc->myhgc->hid, (int)mysrvc->status); while (conns_length()) { MySQL_Connection *conn=(MySQL_Connection *)conns->remove_index_fast(0); delete conn; diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index c763179092..e360f9ad60 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -2035,7 +2035,7 @@ bool MySQL_Connection::IsServerOffline() { bool ret=false; if (parent==NULL) return ret; - server_status=parent->status; // we copy it here to avoid race condition. The caller will see this + server_status=(MySerStatus)parent->status; // we copy it here to avoid race condition. The caller will see this if ( (server_status==MYSQL_SERVER_STATUS_OFFLINE_HARD) // the server is OFFLINE as specific by the user || @@ -2059,7 +2059,7 @@ int MySQL_Connection::async_query(short event, char *stmt, unsigned long length, PROXY_TRACE2(); assert(mysql); assert(ret_mysql); - server_status=parent->status; // we copy it here to avoid race condition. The caller will see this + server_status=(MySerStatus)parent->status; // we copy it here to avoid race condition. The caller will see this if (IsServerOffline()) return -1; @@ -2198,7 +2198,7 @@ int MySQL_Connection::async_change_user(short event) { PROXY_TRACE(); assert(mysql); assert(ret_mysql); - server_status=parent->status; // we copy it here to avoid race condition. The caller will see this + server_status=(MySerStatus)parent->status; // we copy it here to avoid race condition. The caller will see this if (IsServerOffline()) return -1; @@ -2245,7 +2245,7 @@ int MySQL_Connection::async_select_db(short event) { PROXY_TRACE(); assert(mysql); assert(ret_mysql); - server_status=parent->status; // we copy it here to avoid race condition. The caller will see this + server_status=(MySerStatus)parent->status; // we copy it here to avoid race condition. The caller will see this if (IsServerOffline()) return -1; @@ -2286,7 +2286,7 @@ int MySQL_Connection::async_set_autocommit(short event, bool ac) { PROXY_TRACE(); assert(mysql); assert(ret_mysql); - server_status=parent->status; // we copy it here to avoid race condition. The caller will see this + server_status=(MySerStatus)parent->status; // we copy it here to avoid race condition. The caller will see this if (IsServerOffline()) return -1; @@ -2329,7 +2329,7 @@ int MySQL_Connection::async_set_names(short event, unsigned int c) { PROXY_TRACE(); assert(mysql); assert(ret_mysql); - server_status=parent->status; // we copy it here to avoid race condition. The caller will see this + server_status=(MySerStatus)parent->status; // we copy it here to avoid race condition. The caller will see this if (IsServerOffline()) return -1; @@ -2372,7 +2372,7 @@ int MySQL_Connection::async_set_option(short event, bool mask) { PROXY_TRACE(); assert(mysql); assert(ret_mysql); - server_status=parent->status; // we copy it here to avoid race condition. The caller will see this + server_status=(MySerStatus)parent->status; // we copy it here to avoid race condition. The caller will see this if (IsServerOffline()) return -1; @@ -2854,7 +2854,7 @@ int MySQL_Connection::async_send_simple_command(short event, char *stmt, unsigne PROXY_TRACE(); assert(mysql); assert(ret_mysql); - server_status=parent->status; // we copy it here to avoid race condition. The caller will see this + server_status=(MySerStatus)parent->status; // we copy it here to avoid race condition. The caller will see this if ( (parent->status==MYSQL_SERVER_STATUS_OFFLINE_HARD) // the server is OFFLINE as specific by the user || From d3a90a8ef69ad956fe7638075ea674ef2d023c36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Wed, 24 Apr 2024 09:29:47 +0000 Subject: [PATCH 24/79] Some refactoring of ProxySQL_Admin Related to flush_XXXXX_variables___database_to_runtime(). Added three new functions then used by the various main functions(): * flush_GENERIC_variables__retrieve__database_to_runtime() * flush_GENERIC_variables__checksum__database_to_runtime() * flush_GENERIC_variables__process__database_to_runtime() --- include/proxysql_admin.h | 12 + lib/ProxySQL_Admin.cpp | 466 ++++++++++++++------------------------- 2 files changed, 179 insertions(+), 299 deletions(-) diff --git a/include/proxysql_admin.h b/include/proxysql_admin.h index 01112cd164..b4620876d6 100644 --- a/include/proxysql_admin.h +++ b/include/proxysql_admin.h @@ -340,6 +340,18 @@ class ProxySQL_Admin { void flush_mysql_variables___runtime_to_database(SQLite3DB *db, bool replace, bool del, bool onlyifempty, bool runtime=false, bool use_lock=true); void flush_mysql_variables___database_to_runtime(SQLite3DB *db, bool replace, const std::string& checksum = "", const time_t epoch = 0); + void flush_GENERIC_variables__checksum__database_to_runtime(const std::string& modname, const std::string& checksum, const time_t epoch); + bool flush_GENERIC_variables__retrieve__database_to_runtime(const std::string& modname, char* &error, int& cols, int& affected_rows, SQLite3_result* &resultset); + void flush_GENERIC_variables__process__database_to_runtime( + const std::string& modname, SQLite3DB *db, SQLite3_result* resultset, + const bool& lock, const bool& replace, + const std::unordered_set& variables_read_only, + const std::unordered_set& variables_to_delete_silently, + const std::unordered_set& variables_deprecated, + const std::unordered_set& variables_special_values, + std::function special_variable_action = nullptr + ); + char **get_variables_list(); bool set_variable(char *name, char *value, bool lock = true); void flush_admin_variables___database_to_runtime(SQLite3DB *db, bool replace, const std::string& checksum = "", const time_t epoch = 0, bool lock = true); diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index ae9b3e4502..63b2fe41f5 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -7383,6 +7383,93 @@ void ProxySQL_Admin::load_http_server() { } } + +bool ProxySQL_Admin::flush_GENERIC_variables__retrieve__database_to_runtime(const std::string& modname, char* &error, int& cols, int& affected_rows, SQLite3_result* &resultset) { + string q = "SELECT substr(variable_name," + to_string(modname.length()+2) + ") vn, variable_value FROM global_variables WHERE variable_name LIKE '" + modname + "-%'"; + admindb->execute_statement(q.c_str(), &error , &cols , &affected_rows , &resultset); + if (error) { + proxy_error("Error on %s : %s\n", q.c_str(), error); + free(error); + return false; + } + return true; +} + +void ProxySQL_Admin::flush_GENERIC_variables__process__database_to_runtime( + const string& modname, SQLite3DB *db, SQLite3_result* resultset, + const bool& lock, const bool& replace, + const std::unordered_set& variables_read_only, + const std::unordered_set& variables_to_delete_silently, + const std::unordered_set& variables_deprecated, + const std::unordered_set& variables_special_values, + std::function special_variable_action +) { + for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { + SQLite3_row *r=*it; + bool rc = false; + if (modname == "admin") { + rc = set_variable(r->fields[0],r->fields[1], lock); + } else if (modname == "mysql") { + rc = GloMTH->set_variable(r->fields[0],r->fields[1]); + } else if (modname == "sqliteserver") { + rc = GloSQLite3Server->set_variable(r->fields[0],r->fields[1]); + } else if (modname == "clickhouse") { + rc = GloClickHouseServer->set_variable(r->fields[0],r->fields[1]); + } else if (modname == "ldap") { + rc = GloMyLdapAuth->set_variable(r->fields[0],r->fields[1]); + } + const string v = string(r->fields[0]); + if (rc==false) { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Impossible to set variable %s with value \"%s\"\n", r->fields[0],r->fields[1]); + if (replace) { + char *val = NULL; + if (modname == "admin") { + val = get_variable(r->fields[0]); + } else if (modname == "mysql") { + val = GloMTH->get_variable(r->fields[0]); + } else if (modname == "sqliteserver") { + val = GloSQLite3Server->get_variable(r->fields[0]); + } else if (modname == "clickhouse") { + val = GloClickHouseServer->get_variable(r->fields[0]); + } else if (modname == "ldap") { + val = GloMyLdapAuth->get_variable(r->fields[0]); + } + char q[1000]; + if (val) { + if (variables_read_only.count(v) > 0) { + proxy_warning("Impossible to set read-only variable %s with value \"%s\". Resetting to current \"%s\".\n", r->fields[0],r->fields[1], val); + } else { + proxy_warning("Impossible to set variable %s with value \"%s\". Resetting to current \"%s\".\n", r->fields[0],r->fields[1], val); + } + sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"%s-%s\",\"%s\")", modname.c_str(), r->fields[0],val); + db->execute(q); + free(val); + } else { + if (variables_to_delete_silently.count(v) > 0) { + sprintf(q,"DELETE FROM disk.global_variables WHERE variable_name=\"%s-%s\"", modname.c_str(), r->fields[0]); + db->execute(q); + } else if (variables_deprecated.count(v) > 0) { + proxy_error("Global variable %s-%s is deprecated.\n", modname.c_str(), r->fields[0]); + sprintf(q,"DELETE FROM disk.global_variables WHERE variable_name=\"%s-%s\"", modname.c_str(), r->fields[0]); + db->execute(q); + } else { + proxy_warning("Impossible to set not existing variable %s with value \"%s\". Deleting. If the variable name is correct, this version doesn't support it\n", r->fields[0],r->fields[1]); + } + sprintf(q,"DELETE FROM global_variables WHERE variable_name=\"%s-%s\"", modname.c_str(), r->fields[0]); + db->execute(q); + } + } + } else { + proxy_debug(PROXY_DEBUG_ADMIN, 4, "Set variable %s with value \"%s\"\n", r->fields[0],r->fields[1]); + if (variables_special_values.count(v) > 0) { + if (special_variable_action != nullptr) { + special_variable_action(v, r->fields[1], db); + } + } + } + } +} + void ProxySQL_Admin::flush_admin_variables___database_to_runtime( SQLite3DB *db, bool replace, const string& checksum, const time_t epoch, bool lock ) { @@ -7391,83 +7478,19 @@ void ProxySQL_Admin::flush_admin_variables___database_to_runtime( int cols=0; int affected_rows=0; SQLite3_result *resultset=NULL; - char *q=(char *)"SELECT substr(variable_name,7) vn, variable_value FROM global_variables WHERE variable_name LIKE 'admin-%'"; - admindb->execute_statement(q, &error , &cols , &affected_rows , &resultset); - if (error) { - proxy_error("Error on %s : %s\n", q, error); - return; - } else { + if (flush_GENERIC_variables__retrieve__database_to_runtime("admin", error, cols, affected_rows, resultset) == true) { wrlock(); - for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { - SQLite3_row *r=*it; - bool rc=set_variable(r->fields[0],r->fields[1], lock); - if (rc==false) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Impossible to set variable %s with value \"%s\"\n", r->fields[0],r->fields[1]); - if (replace) { - char *val=get_variable(r->fields[0]); - char q[1000]; - if (val) { - if (strcmp(r->fields[0],(char *)"version")) { - proxy_warning("Impossible to set variable %s with value \"%s\". Resetting to current \"%s\".\n", r->fields[0],r->fields[1], val); - } - sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"admin-%s\",\"%s\")",r->fields[0],val); - db->execute(q); - } else { - if (strcmp(r->fields[0],(char *)"debug")==0) { - sprintf(q,"DELETE FROM disk.global_variables WHERE variable_name=\"admin-%s\"",r->fields[0]); - db->execute(q); - } else { - proxy_warning("Impossible to set not existing variable %s with value \"%s\". Deleting. If the variable name is correct, this version doesn't support it\n", r->fields[0],r->fields[1]); - } - sprintf(q,"DELETE FROM global_variables WHERE variable_name=\"admin-%s\"",r->fields[0]); - db->execute(q); - } - free(val); - } - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Set variable %s with value \"%s\"\n", r->fields[0],r->fields[1]); - } - } + flush_GENERIC_variables__process__database_to_runtime("admin", db, resultset, lock, replace, {"version"}, {"debug"}, {}, {}); //commit(); NOT IMPLEMENTED // Checksums are always generated - 'admin-checksum_*' deprecated + { - pthread_mutex_lock(&GloVars.checksum_mutex); // generate checksum for cluster + pthread_mutex_lock(&GloVars.checksum_mutex); flush_admin_variables___runtime_to_database(admindb, false, false, false, true); - char *error=NULL; - int cols=0; - int affected_rows=0; - SQLite3_result *resultset=NULL; - std::string q; - if (GloVars.cluster_sync_interfaces) { - q="SELECT variable_name, variable_value FROM runtime_global_variables WHERE variable_name LIKE 'admin-\%' ORDER BY variable_name"; - } else { - q="SELECT variable_name, variable_value FROM runtime_global_variables WHERE variable_name LIKE 'admin-\%' AND variable_name NOT IN " + string(CLUSTER_SYNC_INTERFACES_ADMIN) + " ORDER BY variable_name"; - } - admindb->execute_statement(q.c_str(), &error , &cols , &affected_rows , &resultset); - 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.admin_variables.set_checksum(buf); - GloVars.checksums_values.admin_variables.version++; - time_t t = time(NULL); - if (epoch != 0 && checksum != "" && GloVars.checksums_values.admin_variables.checksum == checksum) { - GloVars.checksums_values.admin_variables.epoch = epoch; - } else { - GloVars.checksums_values.admin_variables.epoch = t; - } - GloVars.epoch_version = t; - GloVars.generate_global_checksum(); - GloVars.checksums_values.updates_cnt++; + flush_GENERIC_variables__checksum__database_to_runtime("admin", checksum, epoch); pthread_mutex_unlock(&GloVars.checksum_mutex); - proxy_info( - "Computed checksum for 'LOAD ADMIN VARIABLES TO RUNTIME' was '%s', with epoch '%llu'\n", - GloVars.checksums_values.admin_variables.checksum, GloVars.checksums_values.admin_variables.epoch - ); - delete resultset; } wrunlock(); { @@ -7480,82 +7503,88 @@ void ProxySQL_Admin::flush_admin_variables___database_to_runtime( if (resultset) delete resultset; } +void ProxySQL_Admin::flush_GENERIC_variables__checksum__database_to_runtime(const string& modname, const string& checksum, const time_t epoch) { + char *error=NULL; + int cols=0; + int affected_rows=0; + SQLite3_result *resultset=NULL; + std::string q; + q="SELECT variable_name, variable_value FROM runtime_global_variables WHERE variable_name LIKE '" + modname + "-\%' "; + if (modname == "mysql") { + q += " AND variable_name NOT IN ('mysql-threads')"; + if (GloVars.cluster_sync_interfaces == false) { + q += " AND variable_name NOT IN " + string(CLUSTER_SYNC_INTERFACES_MYSQL); + } + } else if (modname == "admin") { + if (GloVars.cluster_sync_interfaces == false) { + q += " AND variable_name NOT IN " + string(CLUSTER_SYNC_INTERFACES_ADMIN); + } + } + q += " ORDER BY variable_name"; + admindb->execute_statement(q.c_str(), &error , &cols , &affected_rows , &resultset); + 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]); + ProxySQL_Checksum_Value *checkvar = NULL; + if (modname == "admin") { + checkvar = &GloVars.checksums_values.admin_variables; + } else if (modname == "mysql") { + checkvar = &GloVars.checksums_values.mysql_variables; + } + assert(checkvar != NULL); + checkvar->set_checksum(buf); + checkvar->version++; + time_t t = time(NULL); + if (epoch != 0 && checksum != "" && checkvar->checksum == checksum) { + checkvar->epoch = epoch; + } else { + checkvar->epoch = t; + } + GloVars.epoch_version = t; + GloVars.generate_global_checksum(); + GloVars.checksums_values.updates_cnt++; + string modnameupper = modname; + for (char &c : modnameupper) { c = std::toupper(c); } + proxy_info( + "Computed checksum for 'LOAD %s VARIABLES TO RUNTIME' was '%s', with epoch '%llu'\n", + modnameupper.c_str(), checkvar->checksum, checkvar->epoch + ); + delete resultset; +} + void ProxySQL_Admin::flush_mysql_variables___database_to_runtime(SQLite3DB *db, bool replace, const std::string& checksum, const time_t epoch) { proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing MySQL variables. Replace:%d\n", replace); char *error=NULL; int cols=0; int affected_rows=0; SQLite3_result *resultset=NULL; - char *q=(char *)"SELECT substr(variable_name,7) vn, variable_value FROM global_variables WHERE variable_name LIKE 'mysql-%'"; - admindb->execute_statement(q, &error , &cols , &affected_rows , &resultset); - if (error) { - proxy_error("Error on %s : %s\n", q, error); - return; - } else { + if (flush_GENERIC_variables__retrieve__database_to_runtime("mysql", error, cols, affected_rows, resultset) == true) { GloMTH->wrlock(); char * previous_default_charset = GloMTH->get_variable_string((char *)"default_charset"); char * previous_default_collation_connection = GloMTH->get_variable_string((char *)"default_collation_connection"); assert(previous_default_charset); assert(previous_default_collation_connection); - for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { - SQLite3_row *r=*it; - const char *value = r->fields[1]; - bool rc=GloMTH->set_variable(r->fields[0],value); - if (rc==false) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Impossible to set variable %s with value \"%s\"\n", r->fields[0],value); - if (replace) { - char *val=GloMTH->get_variable(r->fields[0]); - char q[1000]; - if (val) { - if (strcmp(val,value)) { - proxy_warning("Impossible to set variable %s with value \"%s\". Resetting to current \"%s\".\n", r->fields[0],value, val); - sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-%s\",\"%s\")",r->fields[0],val); - db->execute(q); - } - free(val); - } else { - if (strcmp(r->fields[0],(char *)"session_debug")==0) { - sprintf(q,"DELETE FROM disk.global_variables WHERE variable_name=\"mysql-%s\"",r->fields[0]); - db->execute(q); - } else { - if (strcmp(r->fields[0],(char *)"forward_autocommit")==0) { - if (strcasecmp(value,"true")==0 || strcasecmp(value,"1")==0) { - proxy_error("Global variable mysql-forward_autocommit is deprecated. See issue #3253\n"); - } - sprintf(q,"DELETE FROM disk.global_variables WHERE variable_name=\"mysql-%s\"",r->fields[0]); - db->execute(q); - } else { - proxy_warning("Impossible to set not existing variable %s with value \"%s\". Deleting. If the variable name is correct, this version doesn't support it\n", r->fields[0],r->fields[1]); - } - } - sprintf(q,"DELETE FROM global_variables WHERE variable_name=\"mysql-%s\"",r->fields[0]); + flush_GENERIC_variables__process__database_to_runtime("mysql", db, resultset, false, replace, {}, {"session_debug"}, {"forward_autocommit"}, + {"default_collation_connection", "default_charset", "show_processlist_extended"}, + [](const std::string& varname, const char *varvalue, SQLite3DB* db) { + if (varname == "default_collation_connection" || varname == "default_charset") { + char *val=GloMTH->get_variable((char *)varname.c_str()); + if (val) { + if (strcmp(val,varvalue)) { + char q[1000]; + proxy_warning("Variable %s with value \"%s\" is being replaced with value \"%s\".\n", varname.c_str(), varvalue, val); + sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-%s\",\"%s\")", varname.c_str() ,val); db->execute(q); } + free(val); } - } else { - if ( - (strcmp(r->fields[0],"default_collation_connection")==0) - || (strcmp(r->fields[0],"default_charset")==0) - ) { - char *val=GloMTH->get_variable(r->fields[0]); - char q[1000]; - if (val) { - if (strcmp(val,value)) { - proxy_warning("Variable %s with value \"%s\" is being replaced with value \"%s\".\n", r->fields[0],value, val); - sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"mysql-%s\",\"%s\")",r->fields[0],val); - db->execute(q); - } - free(val); - } - } - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Set variable %s with value \"%s\"\n", r->fields[0],value); - if (strcmp(r->fields[0],(char *)"show_processlist_extended")==0) { - variables.mysql_show_processlist_extended = atoi(value); - } + } else if (varname == "show_processlist_extended") { + GloAdmin->variables.mysql_show_processlist_extended = atoi(varvalue); } -// } - } - + } + ); char q[1000]; char * default_charset = GloMTH->get_variable_string((char *)"default_charset"); char * default_collation_connection = GloMTH->get_variable_string((char *)"default_collation_connection"); @@ -7630,47 +7659,15 @@ void ProxySQL_Admin::flush_mysql_variables___database_to_runtime(SQLite3DB *db, GloMTH->commit(); GloMTH->wrunlock(); - // Checksums are always generated - 'admin-checksum_*' deprecated { // NOTE: 'GloMTH->wrunlock()' should have been called before this point to avoid possible // deadlocks. See issue #3847. pthread_mutex_lock(&GloVars.checksum_mutex); // generate checksum for cluster flush_mysql_variables___runtime_to_database(admindb, false, false, false, true, true); - char *error=NULL; - int cols=0; - int affected_rows=0; - SQLite3_result *resultset=NULL; - std::string q; - q = "SELECT variable_name, variable_value FROM runtime_global_variables WHERE variable_name LIKE 'mysql-\%' AND variable_name NOT IN ('mysql-threads')"; - if (GloVars.cluster_sync_interfaces == false) { - q += " AND variable_name NOT IN " + string(CLUSTER_SYNC_INTERFACES_MYSQL); - } - q += " ORDER BY variable_name"; - admindb->execute_statement(q.c_str(), &error , &cols , &affected_rows , &resultset); - 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_variables.set_checksum(buf); - GloVars.checksums_values.mysql_variables.version++; - time_t t = time(NULL); - if (epoch != 0 && checksum != "" && GloVars.checksums_values.mysql_variables.checksum == checksum) { - GloVars.checksums_values.mysql_variables.epoch = epoch; - } else { - GloVars.checksums_values.mysql_variables.epoch = t; - } - GloVars.epoch_version = t; - GloVars.generate_global_checksum(); - GloVars.checksums_values.updates_cnt++; + flush_GENERIC_variables__checksum__database_to_runtime("mysql", checksum, epoch); pthread_mutex_unlock(&GloVars.checksum_mutex); - delete resultset; } - proxy_info( - "Computed checksum for 'LOAD MYSQL VARIABLES TO RUNTIME' was '%s', with epoch '%llu'\n", - GloVars.checksums_values.mysql_variables.checksum, GloVars.checksums_values.mysql_variables.epoch - ); } if (resultset) delete resultset; } @@ -7688,43 +7685,9 @@ void ProxySQL_Admin::flush_sqliteserver_variables___database_to_runtime(SQLite3D int cols=0; int affected_rows=0; SQLite3_result *resultset=NULL; - char *q=(char *)"SELECT substr(variable_name,14) vn, variable_value FROM global_variables WHERE variable_name LIKE 'sqliteserver-%'"; - admindb->execute_statement(q, &error , &cols , &affected_rows , &resultset); - if (error) { - proxy_error("Error on %s : %s\n", q, error); - return; - } else { + if (flush_GENERIC_variables__retrieve__database_to_runtime("sqliteserver", error, cols, affected_rows, resultset) == true) { GloSQLite3Server->wrlock(); - for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { - SQLite3_row *r=*it; - bool rc=GloSQLite3Server->set_variable(r->fields[0],r->fields[1]); - if (rc==false) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Impossible to set variable %s with value \"%s\"\n", r->fields[0],r->fields[1]); - if (replace) { - char *val=GloSQLite3Server->get_variable(r->fields[0]); - char q[1000]; - if (val) { - if (strcmp(val,r->fields[1])) { - proxy_warning("Impossible to set variable %s with value \"%s\". Resetting to current \"%s\".\n", r->fields[0],r->fields[1], val); - sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"sqliteserver-%s\",\"%s\")",r->fields[0],val); - db->execute(q); - } - free(val); - } else { - if (strcmp(r->fields[0],(char *)"session_debug")==0) { - sprintf(q,"DELETE FROM disk.global_variables WHERE variable_name=\"sqliteserver-%s\"",r->fields[0]); - db->execute(q); - } else { - proxy_warning("Impossible to set not existing variable %s with value \"%s\". Deleting. If the variable name is correct, this version doesn't support it\n", r->fields[0],r->fields[1]); - } - sprintf(q,"DELETE FROM global_variables WHERE variable_name=\"sqliteserver-%s\"",r->fields[0]); - db->execute(q); - } - } - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Set variable %s with value \"%s\"\n", r->fields[0],r->fields[1]); - } - } + flush_GENERIC_variables__process__database_to_runtime("sqliteserver", db, resultset, false, replace, {}, {"session_debug"}, {}, {}); //GloClickHouse->commit(); GloSQLite3Server->wrunlock(); } @@ -7813,43 +7776,9 @@ void ProxySQL_Admin::flush_clickhouse_variables___database_to_runtime(SQLite3DB int cols=0; int affected_rows=0; SQLite3_result *resultset=NULL; - char *q=(char *)"SELECT substr(variable_name,12) vn, variable_value FROM global_variables WHERE variable_name LIKE 'clickhouse-%'"; - admindb->execute_statement(q, &error , &cols , &affected_rows , &resultset); - if (error) { - proxy_error("Error on %s : %s\n", q, error); - return; - } else { + if (flush_GENERIC_variables__retrieve__database_to_runtime("clickhouse", error, cols, affected_rows, resultset) == true) { GloClickHouseServer->wrlock(); - for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { - SQLite3_row *r=*it; - bool rc=GloClickHouseServer->set_variable(r->fields[0],r->fields[1]); - if (rc==false) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Impossible to set variable %s with value \"%s\"\n", r->fields[0],r->fields[1]); - if (replace) { - char *val=GloClickHouseServer->get_variable(r->fields[0]); - char q[1000]; - if (val) { - if (strcmp(val,r->fields[1])) { - proxy_warning("Impossible to set variable %s with value \"%s\". Resetting to current \"%s\".\n", r->fields[0],r->fields[1], val); - sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"clickhouse-%s\",\"%s\")",r->fields[0],val); - db->execute(q); - } - free(val); - } else { - if (strcmp(r->fields[0],(char *)"session_debug")==0) { - sprintf(q,"DELETE FROM disk.global_variables WHERE variable_name=\"clickhouse-%s\"",r->fields[0]); - db->execute(q); - } else { - proxy_warning("Impossible to set not existing variable %s with value \"%s\". Deleting. If the variable name is correct, this version doesn't support it\n", r->fields[0],r->fields[1]); - } - sprintf(q,"DELETE FROM global_variables WHERE variable_name=\"clickhouse-%s\"",r->fields[0]); - db->execute(q); - } - } - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Set variable %s with value \"%s\"\n", r->fields[0],r->fields[1]); - } - } + flush_GENERIC_variables__process__database_to_runtime("clickhouse", db, resultset, false, replace, {}, {"session_debug"}, {}, {}); //GloClickHouse->commit(); GloClickHouseServer->wrunlock(); } @@ -8338,44 +8267,9 @@ void ProxySQL_Admin::flush_ldap_variables___database_to_runtime(SQLite3DB *db, b int cols=0; int affected_rows=0; SQLite3_result *resultset=NULL; - char *q=(char *)"SELECT substr(variable_name,6) vn, variable_value FROM global_variables WHERE variable_name LIKE 'ldap-%'"; - admindb->execute_statement(q, &error , &cols , &affected_rows , &resultset); - if (error) { - proxy_error("Error on %s : %s\n", q, error); - free(error); //fix a memory leak when call admindb->execute_statement function - return; - } else { + if (flush_GENERIC_variables__retrieve__database_to_runtime("ldap", error, cols, affected_rows, resultset) == true) { GloMyLdapAuth->wrlock(); - for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { - SQLite3_row *r=*it; - bool rc=GloMyLdapAuth->set_variable(r->fields[0],r->fields[1]); - if (rc==false) { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Impossible to set variable %s with value \"%s\"\n", r->fields[0],r->fields[1]); - if (replace) { - char *val=GloMyLdapAuth->get_variable(r->fields[0]); - char q[1000]; - if (val) { - if (strcmp(val,r->fields[1])) { - proxy_warning("Impossible to set variable %s with value \"%s\". Resetting to current \"%s\".\n", r->fields[0],r->fields[1], val); - sprintf(q,"INSERT OR REPLACE INTO global_variables VALUES(\"ldap-%s\",\"%s\")",r->fields[0],val); - db->execute(q); - } - free(val); - } else { - if (strcmp(r->fields[0],(char *)"session_debug")==0) { - sprintf(q,"DELETE FROM disk.global_variables WHERE variable_name=\"ldap-%s\"",r->fields[0]); - db->execute(q); - } else { - proxy_warning("Impossible to set not existing variable %s with value \"%s\". Deleting. If the variable name is correct, this version doesn't support it\n", r->fields[0],r->fields[1]); - } - sprintf(q,"DELETE FROM global_variables WHERE variable_name=\"ldap-%s\"",r->fields[0]); - db->execute(q); - } - } - } else { - proxy_debug(PROXY_DEBUG_ADMIN, 4, "Set variable %s with value \"%s\"\n", r->fields[0],r->fields[1]); - } - } + flush_GENERIC_variables__process__database_to_runtime("admin", db, resultset, false, replace, {}, {}, {}, {}); GloMyLdapAuth->wrunlock(); // Checksums are always generated - 'admin-checksum_*' deprecated @@ -8383,35 +8277,9 @@ void ProxySQL_Admin::flush_ldap_variables___database_to_runtime(SQLite3DB *db, b pthread_mutex_lock(&GloVars.checksum_mutex); // generate checksum for cluster flush_ldap_variables___runtime_to_database(admindb, false, false, false, true); - char *error=NULL; - int cols=0; - int affected_rows=0; - SQLite3_result *resultset=NULL; - char *q=(char *)"SELECT variable_name, variable_value FROM runtime_global_variables WHERE variable_name LIKE 'ldap-\%' ORDER BY variable_name"; - admindb->execute_statement(q, &error , &cols , &affected_rows , &resultset); - 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.ldap_variables.set_checksum(buf); - GloVars.checksums_values.ldap_variables.version++; - time_t t = time(NULL); - if (epoch != 0 && checksum != "" && GloVars.checksums_values.ldap_variables.checksum == checksum) { - GloVars.checksums_values.ldap_variables.epoch = epoch; - } else { - GloVars.checksums_values.ldap_variables.epoch = t; - } - GloVars.epoch_version = t; - GloVars.generate_global_checksum(); - GloVars.checksums_values.updates_cnt++; + flush_GENERIC_variables__checksum__database_to_runtime("ldap", checksum, epoch); pthread_mutex_unlock(&GloVars.checksum_mutex); - delete resultset; } - proxy_info( - "Computed checksum for 'LOAD LDAP VARIABLES TO RUNTIME' was '%s', with epoch '%llu'\n", - GloVars.checksums_values.ldap_variables.checksum, GloVars.checksums_values.ldap_variables.epoch - ); } if (resultset) delete resultset; } From e4fd764d86f0d6392ec8dcfa20714a12480996f3 Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Wed, 24 Apr 2024 16:22:26 +0500 Subject: [PATCH 25/79] Added is_locked flag within '#ifdef DEBUG' conditional block --- include/MySQL_HostGroups_Manager.h | 4 +++- lib/MyHGC.cpp | 4 ++-- lib/MySQL_HostGroups_Manager.cpp | 4 ++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/include/MySQL_HostGroups_Manager.h b/include/MySQL_HostGroups_Manager.h index 6d14670b70..b5d3cb854c 100644 --- a/include/MySQL_HostGroups_Manager.h +++ b/include/MySQL_HostGroups_Manager.h @@ -972,7 +972,9 @@ class MySQL_HostGroups_Manager { void init(); void wrlock(); void wrunlock(); - bool is_locked = false; // will be removed after testing +#ifdef DEBUG + bool is_locked = false; +#endif int servers_add(SQLite3_result *resultset); /** * @brief Generates a new global checksum for module 'mysql_servers_v2' using the provided hash. diff --git a/lib/MyHGC.cpp b/lib/MyHGC.cpp index bc347af9cc..72dd3f564c 100644 --- a/lib/MyHGC.cpp +++ b/lib/MyHGC.cpp @@ -392,9 +392,9 @@ MySrvC *MyHGC::get_random_MySrvC(char * gtid_uuid, uint64_t gtid_trxid, int max_ void MyHGC::refresh_online_server_count() { if (__sync_fetch_and_add(&glovars.shutdown, 0) != 0) return; - +#ifdef DEBUG assert(MyHGM->is_locked); - +#endif unsigned int online_servers_count = 0; for (unsigned int i = 0; i < mysrvs->servers->len; i++) { MySrvC* mysrvc = (MySrvC*)mysrvs->servers->index(i); diff --git a/lib/MySQL_HostGroups_Manager.cpp b/lib/MySQL_HostGroups_Manager.cpp index eb7705e990..9dae056eb7 100644 --- a/lib/MySQL_HostGroups_Manager.cpp +++ b/lib/MySQL_HostGroups_Manager.cpp @@ -724,7 +724,9 @@ void MySQL_HostGroups_Manager::wrlock() { #else spin_wrlock(&rwlock); #endif +#ifdef DEBUG is_locked = true; +#endif } void MySQL_HostGroups_Manager::p_update_mysql_error_counter(p_mysql_error_type err_type, unsigned int hid, char* address, uint16_t port, unsigned int code) { @@ -759,7 +761,9 @@ void MySQL_HostGroups_Manager::p_update_mysql_error_counter(p_mysql_error_type e } void MySQL_HostGroups_Manager::wrunlock() { +#ifdef DEBUG is_locked = false; +#endif #ifdef MHM_PTHREAD_MUTEX pthread_mutex_unlock(&lock); #else From b295aca4ab5b061c3d70bf29fc66a50fa090c40d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Thu, 25 Apr 2024 08:31:34 +0000 Subject: [PATCH 26/79] Handle clickhouse in flush_GENERIC_variables__process__database_to_runtime() --- lib/ProxySQL_Admin.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index 63b2fe41f5..39e4175201 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -7413,8 +7413,10 @@ void ProxySQL_Admin::flush_GENERIC_variables__process__database_to_runtime( rc = GloMTH->set_variable(r->fields[0],r->fields[1]); } else if (modname == "sqliteserver") { rc = GloSQLite3Server->set_variable(r->fields[0],r->fields[1]); +#ifdef PROXYSQLCLICKHOUSE } else if (modname == "clickhouse") { rc = GloClickHouseServer->set_variable(r->fields[0],r->fields[1]); +#endif // PROXYSQLCLICKHOUSE } else if (modname == "ldap") { rc = GloMyLdapAuth->set_variable(r->fields[0],r->fields[1]); } @@ -7429,8 +7431,10 @@ void ProxySQL_Admin::flush_GENERIC_variables__process__database_to_runtime( val = GloMTH->get_variable(r->fields[0]); } else if (modname == "sqliteserver") { val = GloSQLite3Server->get_variable(r->fields[0]); +#ifdef PROXYSQLCLICKHOUSE } else if (modname == "clickhouse") { val = GloClickHouseServer->get_variable(r->fields[0]); +#endif // PROXYSQLCLICKHOUSE } else if (modname == "ldap") { val = GloMyLdapAuth->get_variable(r->fields[0]); } From 557b39912da3fbf749e5c1d4cda6b6e0647b5bdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Sat, 27 Apr 2024 08:49:17 +0000 Subject: [PATCH 27/79] Spit of MySQL_Session::get_pkts_from_client() Split of get_pkts_from_client() into smaller functions --- include/MySQL_Session.h | 10 + lib/MySQL_Session.cpp | 552 ++++++++++++++++++++++------------------ 2 files changed, 316 insertions(+), 246 deletions(-) diff --git a/include/MySQL_Session.h b/include/MySQL_Session.h index d7b434e09c..13d5409944 100644 --- a/include/MySQL_Session.h +++ b/include/MySQL_Session.h @@ -229,7 +229,17 @@ class MySQL_Session * performing any processing on received client packets. */ void housekeeping_before_pkts(); + int get_pkts_from_client(bool&, PtrSize_t&); + + // GPFC_ functions are subfunctions of get_pkts_from_client() + int GPFC_Statuses2(bool&, PtrSize_t&); + void GPFC_DetectedMultiPacket_SetDDS(); + int GPFC_WaitingClientData_FastForwardSession(PtrSize_t&); + void GPFC_PreparedStatements(PtrSize_t&, unsigned char); + void GPFC_Replication_SwitchToFastForward(PtrSize_t&, unsigned char); + bool GPFC_QueryUSE(PtrSize_t&, int&); + void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_STMT_RESET(PtrSize_t&); void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_STMT_CLOSE(PtrSize_t&); void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_STMT_SEND_LONG_DATA(PtrSize_t&); diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 4156b58a49..cd7636ce44 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -4145,6 +4145,293 @@ void MySQL_Session::handler___status_WAITING_CLIENT_DATA___default() { } } +int MySQL_Session::GPFC_Statuses2(bool& wrong_pass, PtrSize_t& pkt) { + int handler_ret = 0; + switch (status) { + case WAITING_CLIENT_DATA: + // this case should handled directly into get_pkts_from_client() + assert(0); + break; + case CONNECTING_CLIENT: + switch (client_myds->DSS) { + case STATE_SERVER_HANDSHAKE: + handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(&pkt, &wrong_pass); + break; + case STATE_SSL_INIT: + handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(&pkt, &wrong_pass); + break; + default: + proxy_error("Detected not valid state client state: %d\n", client_myds->DSS); + handler_ret = -1; //close connection + return handler_ret; + break; + } + break; + case FAST_FORWARD: + mybe->server_myds->PSarrayOUT->add(pkt.ptr, pkt.size); + break; + // This state is required because it covers the following situation: + // 1. A new connection is created by a client and the 'FAST_FORWARD' mode is enabled. + // 2. The first packet received for this connection isn't a whole packet, i.e, it's either + // split into multiple packets, or it doesn't fit 'queueIN' size (typically + // QUEUE_T_DEFAULT_SIZE). + // 3. Session is still in 'CONNECTING_SERVER' state, BUT further packets remain to be received + // from the initial split packet. + // + // Because of this, packets received during 'CONNECTING_SERVER' when the previous state is + // 'FAST_FORWARD' should be pushed to 'PSarrayOUT'. + case CONNECTING_SERVER: + if (previous_status.empty() == false && previous_status.top() == FAST_FORWARD) { + mybe->server_myds->PSarrayOUT->add(pkt.ptr, pkt.size); + break; + } + case session_status___NONE: + default: + handler___status_NONE_or_default(pkt); + handler_ret = -1; + return handler_ret; + break; + } + return handler_ret; +} + +void MySQL_Session::GPFC_DetectedMultiPacket_SetDDS() { + // this is handled only for real traffic, not mirror + switch (client_myds->DSS) { // real traffic only + case STATE_SLEEP: + client_myds->DSS=STATE_SLEEP_MULTI_PACKET; + break; + case STATE_SLEEP_MULTI_PACKET: + break; + default: + // LCOV_EXCL_START + assert(0); + break; + // LCOV_EXCL_STOP + } +} + +int MySQL_Session::GPFC_WaitingClientData_FastForwardSession(PtrSize_t& pkt) { + int handler_ret = 0; + // If this is a 'fast_forward' session that hasn't yet received a backend connection, we don't + // forward 'COM_QUIT' packets, since this will make the act of obtaining a connection pointless. + // Instead, we intercept the 'COM_QUIT' packet and end the 'MySQL_Session'. + unsigned char command = *(static_cast(pkt.ptr)+sizeof(mysql_hdr)); + if (command == _MYSQL_COM_QUIT) { + proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Got COM_QUIT packet\n"); + if (GloMyLogger) { GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_QUIT, this, NULL); } + l_free(pkt.size,pkt.ptr); + handler_ret = -1; + return handler_ret; + } + + mybe=find_or_create_backend(current_hostgroup); // set a backend + mybe->server_myds->reinit_queues(); // reinitialize the queues in the myds . By default, they are not active + mybe->server_myds->PSarrayOUT->add(pkt.ptr, pkt.size); // move the first packet + previous_status.push(FAST_FORWARD); // next status will be FAST_FORWARD . Now we need a connection + + // If this is a 'fast_forward' session, we impose the 'connect_timeout' prior to actually getting the + // connection from the 'connection_pool'. This is used to ensure that we kill the session if + // 'CONNECTING_SERVER' isn't completed before this timeout expiring. For example, if 'max_connections' + // is reached for the target hostgroup. + if (mybe->server_myds->max_connect_time == 0) { + uint64_t connect_timeout = + mysql_thread___connect_timeout_server < mysql_thread___connect_timeout_server_max ? + mysql_thread___connect_timeout_server_max : mysql_thread___connect_timeout_server; + mybe->server_myds->max_connect_time = thread->curtime + connect_timeout * 1000; + } + // Impose the same connection retrying policy as done for regular connections during + // 'MYSQL_CON_QUERY'. + mybe->server_myds->connect_retries_on_failure = mysql_thread___connect_retries_on_failure; + // 'CurrentQuery' isn't used for 'FAST_FORWARD' but we update it for using it as a session + // startup time for when a fast_forward session has attempted to obtain a connection. + CurrentQuery.start_time=thread->curtime; + + //NEXT_IMMEDIATE(CONNECTING_SERVER); // we create a connection . next status will be FAST_FORWARD + // we can't use NEXT_IMMEDIATE() inside get_pkts_from_client() + // instead we set status to CONNECTING_SERVER and return 0 + // when we exit from get_pkts_from_client() we expect the label "handler_again" + set_status(CONNECTING_SERVER); + + return handler_ret; +} + + +void MySQL_Session::GPFC_PreparedStatements(PtrSize_t& pkt, unsigned char c) { + switch ((enum_mysql_command)c) { + case _MYSQL_COM_STMT_PREPARE: + if (GloMyLdapAuth) { + if (session_type==PROXYSQL_SESSION_MYSQL) { + if (mysql_thread___add_ldap_user_comment && strlen(mysql_thread___add_ldap_user_comment)) { + add_ldap_comment_to_pkt(&pkt); + } + } + } + handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_STMT_PREPARE(pkt); + break; + case _MYSQL_COM_STMT_EXECUTE: + handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_STMT_EXECUTE(pkt); + break; + case _MYSQL_COM_STMT_RESET: + handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_STMT_RESET(pkt); + break; + case _MYSQL_COM_STMT_CLOSE: + handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_STMT_CLOSE(pkt); + break; + case _MYSQL_COM_STMT_SEND_LONG_DATA: + handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_STMT_SEND_LONG_DATA(pkt); + break; + default: + // LCOV_EXCL_START + assert(0); + break; + // LCOV_EXCL_STOP + } +} + +void MySQL_Session::GPFC_Replication_SwitchToFastForward(PtrSize_t& pkt, unsigned char c) { + // In this switch we handle commands that download binlog events from MySQL + // servers. For these commands a lot of the features provided by ProxySQL + // aren't useful, like multiplexing, query parsing, etc. For this reason, + // ProxySQL enables fast_forward when it receives these commands.  + { + // we use a switch to write the command in the info message + std::string q = "Received command "; + switch ((enum_mysql_command)c) { + case _MYSQL_COM_BINLOG_DUMP: + q += "MYSQL_COM_BINLOG_DUMP"; + break; + case _MYSQL_COM_BINLOG_DUMP_GTID: + q += "MYSQL_COM_BINLOG_DUMP_GTID"; + break; + case _MYSQL_COM_REGISTER_SLAVE: + q += "MYSQL_COM_REGISTER_SLAVE"; + break; + default: + assert(0); + break; + }; + // we add the client details in the info message + if (client_myds && client_myds->addr.addr) { + q += " from client " + std::string(client_myds->addr.addr) + ":" + std::to_string(client_myds->addr.port); + } + q += " . Changing session fast_forward to true"; + proxy_info("%s\n", q.c_str()); + } + session_fast_forward = true; + + if (client_myds->PSarrayIN->len) { + proxy_error("UNEXPECTED PACKET FROM CLIENT -- PLEASE REPORT A BUG\n"); + assert(0); + } + client_myds->PSarrayIN->add(pkt.ptr, pkt.size); + + // The following code prepares the session as if it was configured with fast + // forward before receiving the command. This way the state machine will + // handle the command automatically. + current_hostgroup = previous_hostgroup; + mybe = find_or_create_backend(current_hostgroup); // set a backend + mybe->server_myds->reinit_queues(); // reinitialize the queues in the myds . By default, they are not active + // We reinitialize the 'wait_until' since this session shouldn't wait for processing as + // we are now transitioning to 'FAST_FORWARD'. + mybe->server_myds->wait_until = 0; + if (mybe->server_myds->DSS==STATE_NOT_INITIALIZED) { + // NOTE: This section is entirely borrowed from 'STATE_SLEEP' for 'session_fast_forward'. + // Check comments there for extra information. + // ============================================================================= + if (mybe->server_myds->max_connect_time == 0) { + uint64_t connect_timeout = + mysql_thread___connect_timeout_server < mysql_thread___connect_timeout_server_max ? + mysql_thread___connect_timeout_server_max : mysql_thread___connect_timeout_server; + mybe->server_myds->max_connect_time = thread->curtime + connect_timeout * 1000; + } + mybe->server_myds->connect_retries_on_failure = mysql_thread___connect_retries_on_failure; + CurrentQuery.start_time=thread->curtime; + // ============================================================================= + + // we don't have a connection + previous_status.push(FAST_FORWARD); // next status will be FAST_FORWARD + set_status(CONNECTING_SERVER); // now we need a connection + } else { + // In case of having a connection, we need to make user to reset the state machine + // for current server 'MySQL_Data_Stream', setting it outside of any state handled + // by 'mariadb' library. Otherwise 'MySQL_Thread' will threat this + // 'MySQL_Data_Stream' as library handled. + mybe->server_myds->DSS = STATE_READY; + // myds needs to have encrypted value set correctly + { + MySQL_Data_Stream * myds = mybe->server_myds; + MySQL_Connection * myconn = myds->myconn; + assert(myconn != NULL); + // PMC-10005 + // if backend connection uses SSL we will set + // encrypted = true and we will start using the SSL structure + // directly from P_MARIADB_TLS structure. + MYSQL *mysql = myconn->mysql; + if (mysql && myconn->ret_mysql) { + if (mysql->options.use_ssl == 1) { + P_MARIADB_TLS * matls = (P_MARIADB_TLS *)mysql->net.pvio->ctls; + if (matls != NULL) { + myds->encrypted = true; + myds->ssl = (SSL *)matls->ssl; + myds->rbio_ssl = BIO_new(BIO_s_mem()); + myds->wbio_ssl = BIO_new(BIO_s_mem()); + SSL_set_bio(myds->ssl, myds->rbio_ssl, myds->wbio_ssl); + } else { + // if mysql->options.use_ssl == 1 but matls == NULL + // it means that ProxySQL tried to use SSL to connect to the backend + // but the backend didn't support SSL + } + } + } + } + set_status(FAST_FORWARD); // we can set status to FAST_FORWARD + } +} + +bool MySQL_Session::GPFC_QueryUSE(PtrSize_t& pkt, int& handler_ret) { + bool need_break = false; + // This block was moved from 'handler_special_queries' to support + // handling of 'USE' statements which are preceded by a comment. + // For more context check issue: #3493. + if (session_type != PROXYSQL_SESSION_CLICKHOUSE) { + const char *qd = CurrentQuery.get_digest_text(); + bool use_db_query = false; + + if (qd != NULL) { + if ( + (strncasecmp((char *)"USE",qd,3)==0) + && + ( + (strncasecmp((char *)"USE ",qd,4)==0) + || + (strncasecmp((char *)"USE`",qd,4)==0) + ) + ) { + use_db_query = true; + } + } else { + if (pkt.size > (5+4) && strncasecmp((char *)"USE ", (char *)pkt.ptr+5, 4) == 0) { + use_db_query = true; + } + } + + if (use_db_query) { + handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_USE_DB(&pkt); + + if (mirror == false) { + need_break = true; + return need_break; + } else { + handler_ret = -1; + return need_break; + } + } + } + + return need_break; +} + int MySQL_Session::get_pkts_from_client(bool& wrong_pass, PtrSize_t& pkt) { int handler_ret = 0; unsigned char c; @@ -4160,39 +4447,9 @@ int MySQL_Session::get_pkts_from_client(bool& wrong_pass, PtrSize_t& pkt) { client_myds->PSarrayIN->remove_index(0,&pkt); } switch (status) { - - case CONNECTING_CLIENT: - switch (client_myds->DSS) { - case STATE_SERVER_HANDSHAKE: - handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(&pkt, &wrong_pass); - break; - case STATE_SSL_INIT: - handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(&pkt, &wrong_pass); - break; - default: - proxy_error("Detected not valid state client state: %d\n", client_myds->DSS); - handler_ret = -1; //close connection - return handler_ret; - break; - } - break; - case WAITING_CLIENT_DATA: - // this is handled only for real traffic, not mirror - if (pkt.size==(0xFFFFFF+sizeof(mysql_hdr))) { - // we are handling a multi-packet - switch (client_myds->DSS) { // real traffic only - case STATE_SLEEP: - client_myds->DSS=STATE_SLEEP_MULTI_PACKET; - break; - case STATE_SLEEP_MULTI_PACKET: - break; - default: - // LCOV_EXCL_START - assert(0); - break; - // LCOV_EXCL_STOP - } + if (pkt.size==(0xFFFFFF+sizeof(mysql_hdr))) { // we are handling a multi-packet + GPFC_DetectedMultiPacket_SetDDS(); } switch (client_myds->DSS) { case STATE_SLEEP_MULTI_PACKET: @@ -4219,51 +4476,13 @@ int MySQL_Session::get_pkts_from_client(bool& wrong_pass, PtrSize_t& pkt) { } } proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session=%p , client_myds=%p . Statuses: WAITING_CLIENT_DATA - STATE_SLEEP\n", this, client_myds); - if (session_fast_forward==true) { // if it is fast forward - // If this is a 'fast_forward' session that hasn't yet received a backend connection, we don't - // forward 'COM_QUIT' packets, since this will make the act of obtaining a connection pointless. - // Instead, we intercept the 'COM_QUIT' packet and end the 'MySQL_Session'. - unsigned char command = *(static_cast(pkt.ptr)+sizeof(mysql_hdr)); - if (command == _MYSQL_COM_QUIT) { - proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Got COM_QUIT packet\n"); - if (GloMyLogger) { GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_QUIT, this, NULL); } - l_free(pkt.size,pkt.ptr); - handler_ret = -1; - return handler_ret; - } - - mybe=find_or_create_backend(current_hostgroup); // set a backend - mybe->server_myds->reinit_queues(); // reinitialize the queues in the myds . By default, they are not active - mybe->server_myds->PSarrayOUT->add(pkt.ptr, pkt.size); // move the first packet - previous_status.push(FAST_FORWARD); // next status will be FAST_FORWARD . Now we need a connection - - // If this is a 'fast_forward' session, we impose the 'connect_timeout' prior to actually getting the - // connection from the 'connection_pool'. This is used to ensure that we kill the session if - // 'CONNECTING_SERVER' isn't completed before this timeout expiring. For example, if 'max_connections' - // is reached for the target hostgroup. - if (mybe->server_myds->max_connect_time == 0) { - uint64_t connect_timeout = - mysql_thread___connect_timeout_server < mysql_thread___connect_timeout_server_max ? - mysql_thread___connect_timeout_server_max : mysql_thread___connect_timeout_server; - mybe->server_myds->max_connect_time = thread->curtime + connect_timeout * 1000; - } - // Impose the same connection retrying policy as done for regular connections during - // 'MYSQL_CON_QUERY'. - mybe->server_myds->connect_retries_on_failure = mysql_thread___connect_retries_on_failure; - // 'CurrentQuery' isn't used for 'FAST_FORWARD' but we update it for using it as a session - // startup time for when a fast_forward session has attempted to obtain a connection. - CurrentQuery.start_time=thread->curtime; - { - //NEXT_IMMEDIATE(CONNECTING_SERVER); // we create a connection . next status will be FAST_FORWARD - // we can't use NEXT_IMMEDIATE() inside get_pkts_from_client() - // instead we set status to CONNECTING_SERVER and return 0 - // when we exit from get_pkts_from_client() we expect the label "handler_again" - set_status(CONNECTING_SERVER); - return 0; - } + if (session_fast_forward==true) { // if it is fast forward + handler_ret = GPFC_WaitingClientData_FastForwardSession(pkt); + return handler_ret; } c=*((unsigned char *)pkt.ptr+sizeof(mysql_hdr)); + if (session_type == PROXYSQL_SESSION_CLICKHOUSE) { if ((enum_mysql_command)c == _MYSQL_COM_INIT_DB) { handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_INIT_DB_replace_CLICKHOUSE(pkt); @@ -4321,44 +4540,16 @@ int MySQL_Session::get_pkts_from_client(bool& wrong_pass, PtrSize_t& pkt) { (begint.tv_sec*1000000000+begint.tv_nsec); } assert(qpo); // GloQPro->process_mysql_query() should always return a qpo - // This block was moved from 'handler_special_queries' to support - // handling of 'USE' statements which are preceded by a comment. - // For more context check issue: #3493. - // =================================================== - if (session_type != PROXYSQL_SESSION_CLICKHOUSE) { - const char *qd = CurrentQuery.get_digest_text(); - bool use_db_query = false; - - if (qd != NULL) { - if ( - (strncasecmp((char *)"USE",qd,3)==0) - && - ( - (strncasecmp((char *)"USE ",qd,4)==0) - || - (strncasecmp((char *)"USE`",qd,4)==0) - ) - ) { - use_db_query = true; - } - } else { - if (pkt.size > (5+4) && strncasecmp((char *)"USE ", (char *)pkt.ptr+5, 4) == 0) { - use_db_query = true; - } - } - - if (use_db_query) { - handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_USE_DB(&pkt); - if (mirror == false) { - break; - } else { - handler_ret = -1; - return handler_ret; - } + { + bool need_break = GPFC_QueryUSE(pkt, handler_ret); + if (handler_ret == -1) { + return handler_ret; + } else if (need_break == true) { + break; } } - // =================================================== + if (qpo->max_lag_ms >= 0) { thread->status_variables.stvar[st_var_queries_with_max_lag_ms]++; } @@ -4464,128 +4655,16 @@ int MySQL_Session::get_pkts_from_client(bool& wrong_pass, PtrSize_t& pkt) { } break; case _MYSQL_COM_STMT_PREPARE: - if (GloMyLdapAuth) { - if (session_type==PROXYSQL_SESSION_MYSQL) { - if (mysql_thread___add_ldap_user_comment && strlen(mysql_thread___add_ldap_user_comment)) { - add_ldap_comment_to_pkt(&pkt); - } - } - } - handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_STMT_PREPARE(pkt); - break; case _MYSQL_COM_STMT_EXECUTE: - handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_STMT_EXECUTE(pkt); - break; case _MYSQL_COM_STMT_RESET: - handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_STMT_RESET(pkt); - break; case _MYSQL_COM_STMT_CLOSE: - handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_STMT_CLOSE(pkt); - break; case _MYSQL_COM_STMT_SEND_LONG_DATA: - handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_STMT_SEND_LONG_DATA(pkt); + GPFC_PreparedStatements(pkt, c); break; case _MYSQL_COM_BINLOG_DUMP: case _MYSQL_COM_BINLOG_DUMP_GTID: case _MYSQL_COM_REGISTER_SLAVE: - // In this switch we handle commands that download binlog events from MySQL - // servers. For these commands a lot of the features provided by ProxySQL - // aren't useful, like multiplexing, query parsing, etc. For this reason, - // ProxySQL enables fast_forward when it receives these commands.  - { - // we use a switch to write the command in the info message - std::string q = "Received command "; - switch ((enum_mysql_command)c) { - case _MYSQL_COM_BINLOG_DUMP: - q += "MYSQL_COM_BINLOG_DUMP"; - break; - case _MYSQL_COM_BINLOG_DUMP_GTID: - q += "MYSQL_COM_BINLOG_DUMP_GTID"; - break; - case _MYSQL_COM_REGISTER_SLAVE: - q += "MYSQL_COM_REGISTER_SLAVE"; - break; - default: - assert(0); - break; - }; - // we add the client details in the info message - if (client_myds && client_myds->addr.addr) { - q += " from client " + std::string(client_myds->addr.addr) + ":" + std::to_string(client_myds->addr.port); - } - q += " . Changing session fast_forward to true"; - proxy_info("%s\n", q.c_str()); - } - session_fast_forward = true; - - if (client_myds->PSarrayIN->len) { - proxy_error("UNEXPECTED PACKET FROM CLIENT -- PLEASE REPORT A BUG\n"); - assert(0); - } - client_myds->PSarrayIN->add(pkt.ptr, pkt.size); - - // The following code prepares the session as if it was configured with fast - // forward before receiving the command. This way the state machine will - // handle the command automatically. - current_hostgroup = previous_hostgroup; - mybe = find_or_create_backend(current_hostgroup); // set a backend - mybe->server_myds->reinit_queues(); // reinitialize the queues in the myds . By default, they are not active - // We reinitialize the 'wait_until' since this session shouldn't wait for processing as - // we are now transitioning to 'FAST_FORWARD'. - mybe->server_myds->wait_until = 0; - if (mybe->server_myds->DSS==STATE_NOT_INITIALIZED) { - // NOTE: This section is entirely borrowed from 'STATE_SLEEP' for 'session_fast_forward'. - // Check comments there for extra information. - // ============================================================================= - if (mybe->server_myds->max_connect_time == 0) { - uint64_t connect_timeout = - mysql_thread___connect_timeout_server < mysql_thread___connect_timeout_server_max ? - mysql_thread___connect_timeout_server_max : mysql_thread___connect_timeout_server; - mybe->server_myds->max_connect_time = thread->curtime + connect_timeout * 1000; - } - mybe->server_myds->connect_retries_on_failure = mysql_thread___connect_retries_on_failure; - CurrentQuery.start_time=thread->curtime; - // ============================================================================= - - // we don't have a connection - previous_status.push(FAST_FORWARD); // next status will be FAST_FORWARD - set_status(CONNECTING_SERVER); // now we need a connection - } else { - // In case of having a connection, we need to make user to reset the state machine - // for current server 'MySQL_Data_Stream', setting it outside of any state handled - // by 'mariadb' library. Otherwise 'MySQL_Thread' will threat this - // 'MySQL_Data_Stream' as library handled. - mybe->server_myds->DSS = STATE_READY; - // myds needs to have encrypted value set correctly - { - MySQL_Data_Stream * myds = mybe->server_myds; - MySQL_Connection * myconn = myds->myconn; - assert(myconn != NULL); - // PMC-10005 - // if backend connection uses SSL we will set - // encrypted = true and we will start using the SSL structure - // directly from P_MARIADB_TLS structure. - MYSQL *mysql = myconn->mysql; - if (mysql && myconn->ret_mysql) { - if (mysql->options.use_ssl == 1) { - P_MARIADB_TLS * matls = (P_MARIADB_TLS *)mysql->net.pvio->ctls; - if (matls != NULL) { - myds->encrypted = true; - myds->ssl = (SSL *)matls->ssl; - myds->rbio_ssl = BIO_new(BIO_s_mem()); - myds->wbio_ssl = BIO_new(BIO_s_mem()); - SSL_set_bio(myds->ssl, myds->rbio_ssl, myds->wbio_ssl); - } else { - // if mysql->options.use_ssl == 1 but matls == NULL - // it means that ProxySQL tried to use SSL to connect to the backend - // but the backend didn't support SSL - } - } - } - } - set_status(FAST_FORWARD); // we can set status to FAST_FORWARD - } - + GPFC_Replication_SwitchToFastForward(pkt, c); break; case _MYSQL_COM_QUIT: proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Got COM_QUIT packet\n"); @@ -4616,30 +4695,11 @@ int MySQL_Session::get_pkts_from_client(bool& wrong_pass, PtrSize_t& pkt) { break; } break; - case FAST_FORWARD: - mybe->server_myds->PSarrayOUT->add(pkt.ptr, pkt.size); - break; - // This state is required because it covers the following situation: - // 1. A new connection is created by a client and the 'FAST_FORWARD' mode is enabled. - // 2. The first packet received for this connection isn't a whole packet, i.e, it's either - // split into multiple packets, or it doesn't fit 'queueIN' size (typically - // QUEUE_T_DEFAULT_SIZE). - // 3. Session is still in 'CONNECTING_SERVER' state, BUT further packets remain to be received - // from the initial split packet. - // - // Because of this, packets received during 'CONNECTING_SERVER' when the previous state is - // 'FAST_FORWARD' should be pushed to 'PSarrayOUT'. - case CONNECTING_SERVER: - if (previous_status.empty() == false && previous_status.top() == FAST_FORWARD) { - mybe->server_myds->PSarrayOUT->add(pkt.ptr, pkt.size); - break; - } - case session_status___NONE: default: - handler___status_NONE_or_default(pkt); - handler_ret = -1; - return handler_ret; - break; + handler_ret = GPFC_Statuses2(wrong_pass, pkt); + if (handler_ret != 0) { + return handler_ret; + } } } return handler_ret; From c2f9df4182c9c44f98c633c52b6a79645fad7ac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Sat, 27 Apr 2024 15:33:25 +0000 Subject: [PATCH 28/79] Splitting MySQL_Session::handler() --- include/MySQL_Session.h | 5 + lib/MySQL_Session.cpp | 266 ++++++++++++++++++++++------------------ 2 files changed, 150 insertions(+), 121 deletions(-) diff --git a/include/MySQL_Session.h b/include/MySQL_Session.h index 13d5409944..3152b3ae44 100644 --- a/include/MySQL_Session.h +++ b/include/MySQL_Session.h @@ -202,7 +202,9 @@ class MySQL_Session void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY___create_mirror_session(); int handler_again___status_PINGING_SERVER(); int handler_again___status_RESETTING_CONNECTION(); + bool handler_again___status_SHOW_WARNINGS(MySQL_Data_Stream *, bool); void handler_again___new_thread_to_kill_connection(); + void handler_KillConnectionIfNeeded(); bool handler_again___verify_init_connect(); bool handler_again___verify_ldap_user_variable(); @@ -210,6 +212,7 @@ class MySQL_Session bool handler_again___verify_backend_session_track_gtids(); bool handler_again___verify_backend_multi_statement(); bool handler_again___verify_backend_user_schema(); + bool handler_again___verify_multiple_variables(MySQL_Connection *); bool handler_again___status_SETTING_INIT_CONNECT(int *); bool handler_again___status_SETTING_LDAP_USER_VARIABLE(int *); bool handler_again___status_SETTING_SQL_MODE(int *); @@ -221,6 +224,7 @@ class MySQL_Session bool handler_again___status_CHANGING_AUTOCOMMIT(int *); bool handler_again___status_SETTING_MULTI_STMT(int *_rc); bool handler_again___multiple_statuses(int *rc); + void init(); void reset(); void add_ldap_comment_to_pkt(PtrSize_t *); @@ -259,6 +263,7 @@ class MySQL_Session int RunQuery(MySQL_Data_Stream *myds, MySQL_Connection *myconn); void handler___status_WAITING_CLIENT_DATA(); void handler_rc0_Process_GTID(MySQL_Connection *myconn); + void handler_rc0_RefreshActiveTransactions(MySQL_Connection* myconn); void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_INIT_DB_replace_CLICKHOUSE(PtrSize_t& pkt); void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY___not_mysql(PtrSize_t& pkt); bool handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_detect_SQLi(); diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index cd7636ce44..3338c17c19 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -2460,6 +2460,54 @@ bool MySQL_Session::handler_again___verify_backend_session_track_gtids() { return ret; } + +bool MySQL_Session::handler_again___verify_multiple_variables(MySQL_Connection* myconn) { + for (auto i = 0; i < SQL_NAME_LAST_LOW_WM; i++) { + auto client_hash = client_myds->myconn->var_hash[i]; +#ifdef DEBUG + if (GloVars.global.gdbg) { + switch (i) { + case SQL_CHARACTER_SET: + case SQL_SET_NAMES: + case SQL_CHARACTER_SET_RESULTS: + case SQL_CHARACTER_SET_CONNECTION: + case SQL_CHARACTER_SET_CLIENT: + case SQL_COLLATION_CONNECTION: + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 7, "Session %p , variable %s has value %s\n" , this, mysql_tracked_variables[i].set_variable_name , client_myds->myconn->variables[i].value); + default: + break; + } + } +#endif // DEBUG + if (client_hash) { + auto server_hash = myconn->var_hash[i]; + if (client_hash != server_hash) { + if(!myconn->var_absent[i] && mysql_variables.verify_variable(this, i)) { + return true; + } + } + } + } + MySQL_Connection *c_con = client_myds->myconn; + vector::const_iterator it_c = c_con->dynamic_variables_idx.begin(); // client connection iterator + for ( ; it_c != c_con->dynamic_variables_idx.end() ; it_c++) { + auto i = *it_c; + auto client_hash = c_con->var_hash[i]; + auto server_hash = myconn->var_hash[i]; + if (client_hash != server_hash) { + if( + !myconn->var_absent[i] + && + mysql_variables.verify_variable(this, i) + ) { + return true; + } + } + } + return false; +} + + /** * @brief Verifies and sets the ldap_user_variable option for the backend connection. * @@ -5209,6 +5257,55 @@ void MySQL_Session::handler_rc0_Process_GTID(MySQL_Connection *myconn) { } } +void MySQL_Session::handler_KillConnectionIfNeeded() { + if ( // two conditions + // If the server connection is in a non-idle state (ASYNC_IDLE), and the current time is greater than or equal to mybe->server_myds->wait_until + // This indicates that the server is taking too long to respond. + (mybe->server_myds->myconn && mybe->server_myds->myconn->async_state_machine!=ASYNC_IDLE && mybe->server_myds->wait_until && thread->curtime >= mybe->server_myds->wait_until) + || + // If the session has been marked as killed by an admin. + (killed==true) + ) { // Logging and Action + // we only log in case on timing out here. Logging for 'killed' is done in the places that hold that contextual information. + if (mybe->server_myds->myconn && (mybe->server_myds->myconn->async_state_machine != ASYNC_IDLE) && mybe->server_myds->wait_until && (thread->curtime >= mybe->server_myds->wait_until)) { + std::string query {}; + + if (CurrentQuery.stmt_info == NULL) { // text protocol + query = std::string { mybe->server_myds->myconn->query.ptr, mybe->server_myds->myconn->query.length }; + } else { // prepared statement + query = std::string { CurrentQuery.stmt_info->query, CurrentQuery.stmt_info->query_length }; + } + + std::string client_addr { "" }; + int client_port = 0; + + if (client_myds) { + client_addr = client_myds->addr.addr ? client_myds->addr.addr : ""; + client_port = client_myds->addr.port; + } + // it logs a warning message indicating the query that caused the timeout, along with client details. + proxy_warning( + "Killing connection %s:%d because query '%s' from client '%s':%d timed out.\n", + mybe->server_myds->myconn->parent->address, + mybe->server_myds->myconn->parent->port, + query.c_str(), + client_addr.c_str(), + client_port + ); + } + // it calls handler_again___new_thread_to_kill_connection() to initiate the killing of the connection associated with the session that timed out. + handler_again___new_thread_to_kill_connection(); + } +} + +void MySQL_Session::handler_rc0_RefreshActiveTransactions(MySQL_Connection* myconn) { + if ((myconn->mysql->server_status & SERVER_STATUS_IN_TRANS) == 0) { // there is no transaction on the backend connection + active_transactions = NumActiveTransactions(); // we check all the hostgroups/backends + if (active_transactions == 0) + transaction_started_at = 0; // reset it + } +} + int MySQL_Session::handler() { int handler_ret = 0; bool prepared_stmt_with_no_params = false; @@ -5312,44 +5409,8 @@ int MySQL_Session::handler() { // set max_connect_time to zero, indicating no timeout mybe->server_myds->max_connect_time=0; } - if ( // two conditions - // If the server connection is in a non-idle state (ASYNC_IDLE), and the current time is greater than or equal to mybe->server_myds->wait_until - // This indicates that the server is taking too long to respond. - (mybe->server_myds->myconn && mybe->server_myds->myconn->async_state_machine!=ASYNC_IDLE && mybe->server_myds->wait_until && thread->curtime >= mybe->server_myds->wait_until) - || - // If the session has been marked as killed by an admin. - (killed==true) - ) { // Logging and Action - // we only log in case on timing out here. Logging for 'killed' is done in the places that hold that contextual information. - if (mybe->server_myds->myconn && (mybe->server_myds->myconn->async_state_machine != ASYNC_IDLE) && mybe->server_myds->wait_until && (thread->curtime >= mybe->server_myds->wait_until)) { - std::string query {}; - - if (CurrentQuery.stmt_info == NULL) { // text protocol - query = std::string { mybe->server_myds->myconn->query.ptr, mybe->server_myds->myconn->query.length }; - } else { // prepared statement - query = std::string { CurrentQuery.stmt_info->query, CurrentQuery.stmt_info->query_length }; - } - - std::string client_addr { "" }; - int client_port = 0; + handler_KillConnectionIfNeeded(); - if (client_myds) { - client_addr = client_myds->addr.addr ? client_myds->addr.addr : ""; - client_port = client_myds->addr.port; - } - // it logs a warning message indicating the query that caused the timeout, along with client details. - proxy_warning( - "Killing connection %s:%d because query '%s' from client '%s':%d timed out.\n", - mybe->server_myds->myconn->parent->address, - mybe->server_myds->myconn->parent->port, - query.c_str(), - client_addr.c_str(), - client_port - ); - } - // it calls handler_again___new_thread_to_kill_connection() to initiate the killing of the connection associated with the session that timed out. - handler_again___new_thread_to_kill_connection(); - } // checks if the backend MySQL server associated with the session has been initialized (STATE_NOT_INITIALIZED) if (mybe->server_myds->DSS==STATE_NOT_INITIALIZED) { // we don't have a backend yet @@ -5399,47 +5460,8 @@ int MySQL_Session::handler() { goto handler_again; } - for (auto i = 0; i < SQL_NAME_LAST_LOW_WM; i++) { - auto client_hash = client_myds->myconn->var_hash[i]; -#ifdef DEBUG - if (GloVars.global.gdbg) { - switch (i) { - case SQL_CHARACTER_SET: - case SQL_SET_NAMES: - case SQL_CHARACTER_SET_RESULTS: - case SQL_CHARACTER_SET_CONNECTION: - case SQL_CHARACTER_SET_CLIENT: - case SQL_COLLATION_CONNECTION: - proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 7, "Session %p , variable %s has value %s\n" , this, mysql_tracked_variables[i].set_variable_name , client_myds->myconn->variables[i].value); - default: - break; - } - } -#endif // DEBUG - if (client_hash) { - auto server_hash = myconn->var_hash[i]; - if (client_hash != server_hash) { - if(!myconn->var_absent[i] && mysql_variables.verify_variable(this, i)) { - goto handler_again; - } - } - } - } - MySQL_Connection *c_con = client_myds->myconn; - vector::const_iterator it_c = c_con->dynamic_variables_idx.begin(); // client connection iterator - for ( ; it_c != c_con->dynamic_variables_idx.end() ; it_c++) { - auto i = *it_c; - auto client_hash = c_con->var_hash[i]; - auto server_hash = myconn->var_hash[i]; - if (client_hash != server_hash) { - if( - !myconn->var_absent[i] - && - mysql_variables.verify_variable(this, i) - ) { - goto handler_again; - } - } + if (handler_again___verify_multiple_variables(myconn) == true) { + goto handler_again; } if (locked_on_hostgroup != -1) { @@ -5508,11 +5530,7 @@ int MySQL_Session::handler() { if (rc==0) { if (active_transactions != 0) { // run this only if currently we think there is a transaction - if ((myconn->mysql->server_status & SERVER_STATUS_IN_TRANS) == 0) { // there is no transaction on the backend connection - active_transactions = NumActiveTransactions(); // we check all the hostgroups/backends - if (active_transactions == 0) - transaction_started_at = 0; // reset it - } + handler_rc0_RefreshActiveTransactions(myconn); } handler_rc0_Process_GTID(myconn); @@ -5681,44 +5699,10 @@ int MySQL_Session::handler() { break; case SHOW_WARNINGS: - // Performs a 'SHOW WARNINGS' query over the current backend connection and returns the connection back - // to the connection pool when finished. Actual logging of received warnings is performed in - // 'MySQL_Connection' while processing 'ASYNC_USE_RESULT_CONT'. - { - MySQL_Data_Stream *myds=mybe->server_myds; - MySQL_Connection *myconn=myds->myconn; - - // Setting POLLOUT is required just in case this state has been reached when 'RunQuery' from - // 'PROCESSING_QUERY' state has immediately return. This is because in case 'mysql_real_query_start' - // immediately returns with '0' the session is never processed again by 'MySQL_Thread', and 'revents' is - // never updated with the result of polling through the 'MySQL_Thread::mypolls'. - myds->revents |= POLLOUT; - - int rc = myconn->async_query( - mybe->server_myds->revents,(char *)"SHOW WARNINGS", strlen((char *)"SHOW WARNINGS") - ); - if (rc == 0 || rc == -1) { - // Cleanup the connection resulset from 'SHOW WARNINGS' for the next query. - if (myconn->MyRS != NULL) { - delete myconn->MyRS; - myconn->MyRS = NULL; - } - - if (rc == -1) { - int myerr = mysql_errno(myconn->mysql); - proxy_error( - "'SHOW WARNINGS' failed to be executed over backend connection with error: '%d'\n", myerr - ); - } - - RequestEnd(myds); - finishQuery(myds,myconn,prepared_stmt_with_no_params); - - handler_ret = 0; - return handler_ret; - } else { - goto handler_again; - } + if (handler_again___status_SHOW_WARNINGS(mybe->server_myds, prepared_stmt_with_no_params) == true) { + goto handler_again; + } else { + handler_ret = 0; return handler_ret; } break; @@ -5773,6 +5757,46 @@ int MySQL_Session::handler() { } // end ::handler() + +bool MySQL_Session::handler_again___status_SHOW_WARNINGS(MySQL_Data_Stream* myds, bool prepared_stmt_with_no_params) { + // Performs a 'SHOW WARNINGS' query over the current backend connection and returns the connection back + // to the connection pool when finished. Actual logging of received warnings is performed in + // 'MySQL_Connection' while processing 'ASYNC_USE_RESULT_CONT'. + MySQL_Connection *myconn=myds->myconn; + + // Setting POLLOUT is required just in case this state has been reached when 'RunQuery' from + // 'PROCESSING_QUERY' state has immediately return. This is because in case 'mysql_real_query_start' + // immediately returns with '0' the session is never processed again by 'MySQL_Thread', and 'revents' is + // never updated with the result of polling through the 'MySQL_Thread::mypolls'. + myds->revents |= POLLOUT; + + int rc = myconn->async_query( + myds->revents,(char *)"SHOW WARNINGS", strlen((char *)"SHOW WARNINGS") + ); + if (rc == 0 || rc == -1) { + // Cleanup the connection resulset from 'SHOW WARNINGS' for the next query. + if (myconn->MyRS != NULL) { + delete myconn->MyRS; + myconn->MyRS = NULL; + } + + if (rc == -1) { + int myerr = mysql_errno(myconn->mysql); + proxy_error( + "'SHOW WARNINGS' failed to be executed over backend connection with error: '%d'\n", myerr + ); + } + + RequestEnd(myds); + finishQuery(myds,myconn,prepared_stmt_with_no_params); + + return false; + } else { + return true; // goto handler_again + } + return false; // default +} + /** * @brief Handle multiple session statuses. * From 52bc6939d400bf3003b483139c381c6627ad6430 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Sat, 27 Apr 2024 15:34:52 +0000 Subject: [PATCH 29/79] Removing label __exit_DSS__STATE_NOT_INITIALIZED Removed label __exit_DSS__STATE_NOT_INITIALIZED from MySQL_Session::handler() . After refactoring, it seems that this label wasn't used from a long time. --- lib/MySQL_Session.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 3338c17c19..6ff1a7c74b 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -5674,7 +5674,7 @@ int MySQL_Session::handler() { } } - goto __exit_DSS__STATE_NOT_INITIALIZED; + //goto __exit_DSS__STATE_NOT_INITIALIZED; } @@ -5711,8 +5711,8 @@ int MySQL_Session::handler() { int rc=0; if (handler_again___status_CONNECTING_SERVER(&rc)) goto handler_again; // we changed status - if (rc==1) //handler_again___status_CONNECTING_SERVER returns 1 - goto __exit_DSS__STATE_NOT_INITIALIZED; + //if (rc==1) //handler_again___status_CONNECTING_SERVER returns 1 + // goto __exit_DSS__STATE_NOT_INITIALIZED; } break; case session_status___NONE: @@ -5731,7 +5731,7 @@ int MySQL_Session::handler() { } -__exit_DSS__STATE_NOT_INITIALIZED: +//__exit_DSS__STATE_NOT_INITIALIZED: #ifdef DEBUG From dc2e9fae601f26b77c14748dbaa4e847ca1ce2fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Sat, 27 Apr 2024 15:46:08 +0000 Subject: [PATCH 30/79] Further code cleanup Initial split of MySQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE() --- include/MySQL_Session.h | 1 + lib/MySQL_Session.cpp | 131 ++++++++++++++++++++-------------------- 2 files changed, 68 insertions(+), 64 deletions(-) diff --git a/include/MySQL_Session.h b/include/MySQL_Session.h index 3152b3ae44..2a593d3ce5 100644 --- a/include/MySQL_Session.h +++ b/include/MySQL_Session.h @@ -122,6 +122,7 @@ class MySQL_Session private: //int handler_ret; void handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(PtrSize_t *, bool *); + void handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE_WrongCredentials(PtrSize_t *, bool *); // void handler___status_CHANGING_USER_CLIENT___STATE_CLIENT_HANDSHAKE(PtrSize_t *, bool *); diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 6ff1a7c74b..b649a201db 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -5841,6 +5841,72 @@ bool MySQL_Session::handler_again___multiple_statuses(int *rc) { return ret; } +void MySQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE_WrongCredentials(PtrSize_t *pkt, bool *wrong_pass) { + l_free(pkt->size,pkt->ptr); + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session=%p , DS=%p . Wrong credentials for frontend: disconnecting\n", this, client_myds); + *wrong_pass=true; + // FIXME: this should become close connection + client_myds->setDSS_STATE_QUERY_SENT_NET(); + char *client_addr=NULL; + if (client_myds->client_addr && client_myds->myconn->userinfo->username) { + char buf[512]; + switch (client_myds->client_addr->sa_family) { + case AF_INET: { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)client_myds->client_addr; + if (ipv4->sin_port) { + inet_ntop(client_myds->client_addr->sa_family, &ipv4->sin_addr, buf, INET_ADDRSTRLEN); + client_addr = strdup(buf); + } else { + client_addr = strdup((char *)"localhost"); + } + break; + } + case AF_INET6: { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)client_myds->client_addr; + inet_ntop(client_myds->client_addr->sa_family, &ipv6->sin6_addr, buf, INET6_ADDRSTRLEN); + client_addr = strdup(buf); + break; + } + default: + client_addr = strdup((char *)"localhost"); + break; + } + } else { + client_addr = strdup((char *)""); + } + if (client_myds->myconn->userinfo->username) { + char *_s=(char *)malloc(strlen(client_myds->myconn->userinfo->username)+100+strlen(client_addr)); + //uint8_t _pid = 2; + //if (client_myds->switching_auth_stage) _pid+=2; + //if (is_encrypted) _pid++; + uint8_t _pid = client_myds->pkt_sid; _pid++; +#ifdef DEBUG + if (client_myds->myconn->userinfo->password) { + char *tmp_pass=strdup(client_myds->myconn->userinfo->password); + int lpass = strlen(tmp_pass); + for (int i=2; imyconn->userinfo->username, client_addr, tmp_pass); + free(tmp_pass); + } else { + proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session=%p , DS=%p . Error: Access denied for user '%s'@'%s' . No password. Disconnecting\n", this, client_myds, client_myds->myconn->userinfo->username, client_addr); + } +#endif // DEBUG + sprintf(_s,"ProxySQL Error: Access denied for user '%s'@'%s' (using password: %s)", client_myds->myconn->userinfo->username, client_addr, (client_myds->myconn->userinfo->password ? "YES" : "NO")); + client_myds->myprot.generate_pkt_ERR(true,NULL,NULL, _pid, 1045,(char *)"28000", _s, true); + proxy_error("ProxySQL Error: Access denied for user '%s'@'%s' (using password: %s)\n", client_myds->myconn->userinfo->username, client_addr, (client_myds->myconn->userinfo->password ? "YES" : "NO")); + free(_s); + __sync_fetch_and_add(&MyHGM->status.access_denied_wrong_password, 1); + } + if (client_addr) { + free(client_addr); + } + GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_ERR, this, NULL); + __sync_add_and_fetch(&MyHGM->status.client_connections_aborted,1); + client_myds->DSS=STATE_SLEEP; +} + void MySQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(PtrSize_t *pkt, bool *wrong_pass) { bool is_encrypted = client_myds->encrypted; bool handshake_response_return = client_myds->myprot.process_pkt_handshake_response((unsigned char *)pkt->ptr,pkt->size); @@ -6086,71 +6152,8 @@ void MySQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE( } } } else { - l_free(pkt->size,pkt->ptr); - proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session=%p , DS=%p . Wrong credentials for frontend: disconnecting\n", this, client_myds); - *wrong_pass=true; - // FIXME: this should become close connection - client_myds->setDSS_STATE_QUERY_SENT_NET(); - char *client_addr=NULL; - if (client_myds->client_addr && client_myds->myconn->userinfo->username) { - char buf[512]; - switch (client_myds->client_addr->sa_family) { - case AF_INET: { - struct sockaddr_in *ipv4 = (struct sockaddr_in *)client_myds->client_addr; - if (ipv4->sin_port) { - inet_ntop(client_myds->client_addr->sa_family, &ipv4->sin_addr, buf, INET_ADDRSTRLEN); - client_addr = strdup(buf); - } else { - client_addr = strdup((char *)"localhost"); - } - break; - } - case AF_INET6: { - struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)client_myds->client_addr; - inet_ntop(client_myds->client_addr->sa_family, &ipv6->sin6_addr, buf, INET6_ADDRSTRLEN); - client_addr = strdup(buf); - break; - } - default: - client_addr = strdup((char *)"localhost"); - break; - } - } else { - client_addr = strdup((char *)""); - } - if (client_myds->myconn->userinfo->username) { - char *_s=(char *)malloc(strlen(client_myds->myconn->userinfo->username)+100+strlen(client_addr)); - //uint8_t _pid = 2; - //if (client_myds->switching_auth_stage) _pid+=2; - //if (is_encrypted) _pid++; - uint8_t _pid = client_myds->pkt_sid; _pid++; -#ifdef DEBUG - if (client_myds->myconn->userinfo->password) { - char *tmp_pass=strdup(client_myds->myconn->userinfo->password); - int lpass = strlen(tmp_pass); - for (int i=2; imyconn->userinfo->username, client_addr, tmp_pass); - free(tmp_pass); - } else { - proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session=%p , DS=%p . Error: Access denied for user '%s'@'%s' . No password. Disconnecting\n", this, client_myds, client_myds->myconn->userinfo->username, client_addr); - } -#endif // DEBUG - sprintf(_s,"ProxySQL Error: Access denied for user '%s'@'%s' (using password: %s)", client_myds->myconn->userinfo->username, client_addr, (client_myds->myconn->userinfo->password ? "YES" : "NO")); - client_myds->myprot.generate_pkt_ERR(true,NULL,NULL, _pid, 1045,(char *)"28000", _s, true); - proxy_error("ProxySQL Error: Access denied for user '%s'@'%s' (using password: %s)\n", client_myds->myconn->userinfo->username, client_addr, (client_myds->myconn->userinfo->password ? "YES" : "NO")); - free(_s); - __sync_fetch_and_add(&MyHGM->status.access_denied_wrong_password, 1); - } - if (client_addr) { - free(client_addr); - } - GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_ERR, this, NULL); - __sync_add_and_fetch(&MyHGM->status.client_connections_aborted,1); - client_myds->DSS=STATE_SLEEP; + handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE_WrongCredentials(pkt, wrong_pass); } - if (mysql_thread___client_host_cache_size) { GloMTH->update_client_host_cache(client_myds->client_addr, handshake_err); } From 73f8ad0a35bb4450e7120eb545d685084538c685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Sun, 28 Apr 2024 04:29:30 +0000 Subject: [PATCH 31/79] Code split related to generate_proxysql_internal_session_json --- include/MySQL_Data_Stream.h | 2 + include/mysql_connection.h | 5 +- include/query_processor.h | 16 +++- lib/MySQL_Session.cpp | 167 ++++-------------------------------- lib/mysql_connection.cpp | 110 +++++++++++++++++++++--- lib/mysql_data_stream.cpp | 69 +++++++++++++++ 6 files changed, 203 insertions(+), 166 deletions(-) diff --git a/include/MySQL_Data_Stream.h b/include/MySQL_Data_Stream.h index 489c9cf974..1ce5fed802 100644 --- a/include/MySQL_Data_Stream.h +++ b/include/MySQL_Data_Stream.h @@ -272,5 +272,7 @@ class MySQL_Data_Stream void destroy_queues(); bool data_in_rbio(); + + void get_client_myds_info_json(json&); }; #endif /* __CLASS_MYSQL_DATA_STREAM_H */ diff --git a/include/mysql_connection.h b/include/mysql_connection.h index 350a8940ca..9fb33c86fc 100644 --- a/include/mysql_connection.h +++ b/include/mysql_connection.h @@ -26,7 +26,7 @@ class MySQLServers_SslParams; class Variable { public: char *value = (char*)""; - void fill_server_internal_session(json &j, int conn_num, int idx); + void fill_server_internal_session(json &j, int idx); void fill_client_internal_session(json &j, int idx); }; @@ -254,5 +254,8 @@ class MySQL_Connection { unsigned int number_of_matching_session_variables(const MySQL_Connection *client_conn, unsigned int& not_matching); unsigned long get_mysql_thread_id() { return mysql ? mysql->thread_id : 0; } static void set_ssl_params(MYSQL *mysql, MySQLServers_SslParams *ssl_params); + + void get_mysql_info_json(json&); + void get_backend_conn_info_json(json&); }; #endif /* __CLASS_MYSQL_CONNECTION_H */ diff --git a/include/query_processor.h b/include/query_processor.h index 3d7d8d1561..9d62330291 100644 --- a/include/query_processor.h +++ b/include/query_processor.h @@ -10,7 +10,7 @@ #define DIGEST_STATS_FAST_MINSIZE 100000 #define DIGEST_STATS_FAST_THREADS 4 - +#include "../deps/json/json.hpp" #include "khash.h" @@ -204,6 +204,20 @@ class Query_Processor_Output { free(comment); } } + void get_info_json(nlohmann::json& j) { + j["create_new_connection"] = create_new_conn; + j["reconnect"] = reconnect; + j["sticky_conn"] = sticky_conn; + j["cache_timeout"] = cache_timeout; + j["cache_ttl"] = cache_ttl; + j["delay"] = delay; + j["destination_hostgroup"] = destination_hostgroup; + j["firewall_whitelist_mode"] = firewall_whitelist_mode; + j["multiplex"] = multiplex; + j["timeout"] = timeout; + j["retries"] = retries; + j["max_lag_ms"] = max_lag_ms; + } }; static char *commands_counters_desc[MYSQL_COM_QUERY___NONE]; diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index b649a201db..01bc41d286 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -1379,6 +1379,9 @@ void MySQL_Session::generate_proxysql_internal_session_json(json &j) { j["warning_in_hg"] = warning_in_hg; j["gtid"]["hid"] = gtid_hid; j["gtid"]["last"] = ( strlen(gtid_buf) ? gtid_buf : "" ); + json& jqpo = j["qpo"]; + qpo->get_info_json(jqpo); +/* j["qpo"]["create_new_connection"] = qpo->create_new_conn; j["qpo"]["reconnect"] = qpo->reconnect; j["qpo"]["sticky_conn"] = qpo->sticky_conn; @@ -1391,80 +1394,19 @@ void MySQL_Session::generate_proxysql_internal_session_json(json &j) { j["qpo"]["timeout"] = qpo->timeout; j["qpo"]["retries"] = qpo->retries; j["qpo"]["max_lag_ms"] = qpo->max_lag_ms; +*/ j["default_schema"] = ( default_schema ? default_schema : "" ); j["user_attributes"] = ( user_attributes ? user_attributes : "" ); j["transaction_persistent"] = transaction_persistent; if (client_myds != NULL) { // only if client_myds is defined - j["client"]["stream"]["pkts_recv"] = client_myds->pkts_recv; - j["client"]["stream"]["pkts_sent"] = client_myds->pkts_sent; - j["client"]["stream"]["bytes_recv"] = client_myds->bytes_info.bytes_recv; - j["client"]["stream"]["bytes_sent"] = client_myds->bytes_info.bytes_sent; - j["client"]["client_addr"]["address"] = ( client_myds->addr.addr ? client_myds->addr.addr : "" ); - j["client"]["client_addr"]["port"] = client_myds->addr.port; - j["client"]["proxy_addr"]["address"] = ( client_myds->proxy_addr.addr ? client_myds->proxy_addr.addr : "" ); - j["client"]["proxy_addr"]["port"] = client_myds->proxy_addr.port; - j["client"]["encrypted"] = client_myds->encrypted; - if (client_myds->encrypted) { - const SSL_CIPHER *cipher = SSL_get_current_cipher(client_myds->ssl); - if (cipher) { - const char * name = SSL_CIPHER_get_name(cipher); - if (name) { - j["client"]["ssl_cipher"] = name; - } - } - } - j["client"]["DSS"] = client_myds->DSS; - j["client"]["switching_auth_sent"] = client_myds->switching_auth_sent; - j["client"]["switching_auth_type"] = client_myds->switching_auth_type; - j["client"]["prot"]["sent_auth_plugin_id"] = client_myds->myprot.sent_auth_plugin_id; - j["client"]["prot"]["auth_plugin_id"] = client_myds->myprot.auth_plugin_id; - switch (client_myds->myprot.auth_plugin_id) { - case AUTH_MYSQL_NATIVE_PASSWORD: - j["client"]["prot"]["auth_plugin"] = "mysql_native_password"; - break; - case AUTH_MYSQL_CLEAR_PASSWORD: - j["client"]["prot"]["auth_plugin"] = "mysql_clear_password"; - break; - case AUTH_MYSQL_CACHING_SHA2_PASSWORD: - j["client"]["prot"]["auth_plugin"] = "caching_sha2_password"; - break; - default: - break; - } - if (client_myds->myconn != NULL) { // only if myconn is defined - if (client_myds->myconn->userinfo != NULL) { // only if userinfo is defined - j["client"]["userinfo"]["username"] = ( client_myds->myconn->userinfo->username ? client_myds->myconn->userinfo->username : "" ); - j["client"]["userinfo"]["schemaname"] = ( client_myds->myconn->userinfo->schemaname ? client_myds->myconn->userinfo->schemaname : "" ); -#ifdef DEBUG - j["client"]["userinfo"]["password"] = ( client_myds->myconn->userinfo->password ? client_myds->myconn->userinfo->password : "" ); -#endif - } - j["conn"]["session_track_gtids"] = ( client_myds->myconn->options.session_track_gtids ? client_myds->myconn->options.session_track_gtids : "") ; - for (auto idx = 0; idx < SQL_NAME_LAST_LOW_WM; idx++) { - client_myds->myconn->variables[idx].fill_client_internal_session(j, idx); - } - { - MySQL_Connection *c = client_myds->myconn; - for (std::vector::const_iterator it_c = c->dynamic_variables_idx.begin(); it_c != c->dynamic_variables_idx.end(); it_c++) { - c->variables[*it_c].fill_client_internal_session(j, *it_c); - } - } - - j["conn"]["autocommit"] = ( client_myds->myconn->options.autocommit ? "ON" : "OFF" ); - j["conn"]["client_flag"]["value"] = client_myds->myconn->options.client_flag; - j["conn"]["client_flag"]["client_found_rows"] = (client_myds->myconn->options.client_flag & CLIENT_FOUND_ROWS ? 1 : 0); - j["conn"]["client_flag"]["client_multi_statements"] = (client_myds->myconn->options.client_flag & CLIENT_MULTI_STATEMENTS ? 1 : 0); - j["conn"]["client_flag"]["client_multi_results"] = (client_myds->myconn->options.client_flag & CLIENT_MULTI_RESULTS ? 1 : 0); - j["conn"]["client_flag"]["client_deprecate_eof"] = (client_myds->myconn->options.client_flag & CLIENT_DEPRECATE_EOF ? 1 : 0); - j["conn"]["no_backslash_escapes"] = client_myds->myconn->options.no_backslash_escapes; - j["conn"]["status"]["compression"] = client_myds->myconn->get_status(STATUS_MYSQL_CONNECTION_COMPRESSION); - j["conn"]["ps"]["client_stmt_to_global_ids"] = client_myds->myconn->local_stmts->client_stmt_to_global_ids; - } + json& jclient = j["client"]; + client_myds->get_client_myds_info_json(jclient); } for (unsigned int i=0; ilen; i++) { MySQL_Backend *_mybe = NULL; _mybe=(MySQL_Backend *)mybes->index(i); //unsigned int i = _mybe->hostgroup_id; + j["backends"][i] = {}; j["backends"][i]["hostgroup_id"] = _mybe->hostgroup_id; j["backends"][i]["gtid"] = ( strlen(_mybe->gtid_uuid) ? _mybe->gtid_uuid : "" ); if (_mybe->server_myds) { @@ -1482,86 +1424,8 @@ void MySQL_Session::generate_proxysql_internal_session_json(json &j) { j["backends"][i]["stream"]["bytes_sent"] = _myds->bytes_info.bytes_sent; j["backends"][i]["stream"]["DSS"] = _myds->DSS; if (_myds->myconn) { - MySQL_Connection * _myconn = _myds->myconn; - for (auto idx = 0; idx < SQL_NAME_LAST_LOW_WM; idx++) { - _myconn->variables[idx].fill_server_internal_session(j, i, idx); - } - for (std::vector::const_iterator it_c = _myconn->dynamic_variables_idx.begin(); it_c != _myconn->dynamic_variables_idx.end(); it_c++) { - _myconn->variables[*it_c].fill_server_internal_session(j, i, *it_c); - } - sprintf(buff,"%p",_myconn); - j["backends"][i]["conn"]["address"] = buff; - j["backends"][i]["conn"]["auto_increment_delay_token"] = _myconn->auto_increment_delay_token; - j["backends"][i]["conn"]["bytes_recv"] = _myconn->bytes_info.bytes_recv; - j["backends"][i]["conn"]["bytes_sent"] = _myconn->bytes_info.bytes_sent; - j["backends"][i]["conn"]["questions"] = _myconn->statuses.questions; - j["backends"][i]["conn"]["myconnpoll_get"] = _myconn->statuses.myconnpoll_get; - j["backends"][i]["conn"]["myconnpoll_put"] = _myconn->statuses.myconnpoll_put; - //j["backend"][i]["conn"]["charset"] = _myds->myconn->options.charset; // not used for backend - j["backends"][i]["conn"]["session_track_gtids"] = ( _myconn->options.session_track_gtids ? _myconn->options.session_track_gtids : "") ; - j["backends"][i]["conn"]["init_connect"] = ( _myconn->options.init_connect ? _myconn->options.init_connect : ""); - j["backends"][i]["conn"]["init_connect_sent"] = _myds->myconn->options.init_connect_sent; - j["backends"][i]["conn"]["autocommit"] = ( _myds->myconn->options.autocommit ? "ON" : "OFF" ); - j["backends"][i]["conn"]["last_set_autocommit"] = _myds->myconn->options.last_set_autocommit; - j["backends"][i]["conn"]["no_backslash_escapes"] = _myconn->options.no_backslash_escapes; - j["backends"][i]["conn"]["status"]["get_lock"] = _myconn->get_status(STATUS_MYSQL_CONNECTION_GET_LOCK); - j["backends"][i]["conn"]["status"]["lock_tables"] = _myconn->get_status(STATUS_MYSQL_CONNECTION_LOCK_TABLES); - j["backends"][i]["conn"]["status"]["has_savepoint"] = _myconn->get_status(STATUS_MYSQL_CONNECTION_HAS_SAVEPOINT); - j["backends"][i]["conn"]["status"]["temporary_table"] = _myconn->get_status(STATUS_MYSQL_CONNECTION_TEMPORARY_TABLE); - j["backends"][i]["conn"]["status"]["user_variable"] = _myconn->get_status(STATUS_MYSQL_CONNECTION_USER_VARIABLE); - j["backends"][i]["conn"]["status"]["found_rows"] = _myconn->get_status(STATUS_MYSQL_CONNECTION_FOUND_ROWS); - j["backends"][i]["conn"]["status"]["no_multiplex"] = _myconn->get_status(STATUS_MYSQL_CONNECTION_NO_MULTIPLEX); - j["backends"][i]["conn"]["status"]["no_multiplex_HG"] = _myconn->get_status(STATUS_MYSQL_CONNECTION_NO_MULTIPLEX_HG); - j["backends"][i]["conn"]["status"]["compression"] = _myconn->get_status(STATUS_MYSQL_CONNECTION_COMPRESSION); - j["backends"][i]["conn"]["status"]["prepared_statement"] = _myconn->get_status(STATUS_MYSQL_CONNECTION_PREPARED_STATEMENT); - j["backends"][i]["conn"]["status"]["has_warnings"] = _myconn->get_status(STATUS_MYSQL_CONNECTION_HAS_WARNINGS); - j["backends"][i]["conn"]["warning_count"] = _myconn->warning_count; - { - // MultiplexDisabled : status returned by MySQL_Connection::MultiplexDisabled(); - // MultiplexDisabled_ext : status returned by MySQL_Connection::MultiplexDisabled() || MySQL_Connection::isActiveTransaction() - bool multiplex_disabled = _myconn->MultiplexDisabled(); - j["backends"][i]["conn"]["MultiplexDisabled"] = multiplex_disabled; - if (multiplex_disabled == false) { - if (_myconn->IsActiveTransaction() == true) { - multiplex_disabled = true; - } - } - j["backends"][i]["conn"]["MultiplexDisabled_ext"] = multiplex_disabled; - } - j["backends"][i]["conn"]["ps"]["backend_stmt_to_global_ids"] = _myconn->local_stmts->backend_stmt_to_global_ids; - j["backends"][i]["conn"]["ps"]["global_stmt_to_backend_ids"] = _myconn->local_stmts->global_stmt_to_backend_ids; - j["backends"][i]["conn"]["client_flag"]["value"] = _myconn->options.client_flag; - j["backends"][i]["conn"]["client_flag"]["client_found_rows"] = (_myconn->options.client_flag & CLIENT_FOUND_ROWS ? 1 : 0); - j["backends"][i]["conn"]["client_flag"]["client_multi_statements"] = (_myconn->options.client_flag & CLIENT_MULTI_STATEMENTS ? 1 : 0); - j["backends"][i]["conn"]["client_flag"]["client_deprecate_eof"] = (_myconn->options.client_flag & CLIENT_DEPRECATE_EOF ? 1 : 0); - if (_myconn->mysql && _myconn->ret_mysql) { - MYSQL * _my = _myconn->mysql; - sprintf(buff,"%p",_my); - j["backends"][i]["conn"]["mysql"]["address"] = buff; - j["backends"][i]["conn"]["mysql"]["host"] = ( _my->host ? _my->host : "" ); - j["backends"][i]["conn"]["mysql"]["host_info"] = ( _my->host_info ? _my->host_info : "" ); - j["backends"][i]["conn"]["mysql"]["port"] = _my->port; - j["backends"][i]["conn"]["mysql"]["server_version"] = ( _my->server_version ? _my->server_version : "" ); - j["backends"][i]["conn"]["mysql"]["user"] = ( _my->user ? _my->user : "" ); - j["backends"][i]["conn"]["mysql"]["unix_socket"] = (_my->unix_socket ? _my->unix_socket : ""); - j["backends"][i]["conn"]["mysql"]["db"] = (_my->db ? _my->db : ""); - j["backends"][i]["conn"]["mysql"]["affected_rows"] = _my->affected_rows; - j["backends"][i]["conn"]["mysql"]["insert_id"] = _my->insert_id; - j["backends"][i]["conn"]["mysql"]["thread_id"] = _my->thread_id; - j["backends"][i]["conn"]["mysql"]["server_status"] = _my->server_status; - j["backends"][i]["conn"]["mysql"]["charset"] = _my->charset->nr; - j["backends"][i]["conn"]["mysql"]["charset_name"] = _my->charset->csname; - //j["backends"][i]["conn"]["mysql"][""] = _my->; - //j["backends"][i]["conn"]["mysql"][""] = _my->; - j["backends"][i]["conn"]["mysql"]["options"]["charset_name"] = ( _my->options.charset_name ? _my->options.charset_name : "" ); - j["backends"][i]["conn"]["mysql"]["options"]["use_ssl"] = _my->options.use_ssl; - j["backends"][i]["conn"]["mysql"]["net"]["last_errno"] = _my->net.last_errno; - j["backends"][i]["conn"]["mysql"]["net"]["fd"] = _my->net.fd; - j["backends"][i]["conn"]["mysql"]["net"]["max_packet_size"] = _my->net.max_packet_size; - j["backends"][i]["conn"]["mysql"]["net"]["sqlstate"] = _my->net.sqlstate; - //j["backends"][i]["conn"]["mysql"]["net"][""] = _my->net.; - //j["backends"][i]["conn"]["mysql"]["net"][""] = _my->net.; - } + json& jc = j["backends"][i]["conn"]; + _myds->myconn->get_backend_conn_info_json(jc); } } } @@ -1635,17 +1499,18 @@ bool MySQL_Session::handler_special_queries_STATUS(PtrSize_t *pkt) { string vals[4]; json j = {}; + json& jc = j["conn"]; MySQL_Connection *conn = client_myds->myconn; // here we do a bit back and forth to and from JSON to reuse existing code instead of writing new code. // This is not great for performance, but this query is rarely executed. - conn->variables[SQL_CHARACTER_SET_CLIENT].fill_client_internal_session(j, SQL_CHARACTER_SET_CLIENT); - conn->variables[SQL_CHARACTER_SET_CONNECTION].fill_client_internal_session(j, SQL_CHARACTER_SET_CONNECTION); - conn->variables[SQL_CHARACTER_SET_DATABASE].fill_client_internal_session(j, SQL_CHARACTER_SET_DATABASE); + conn->variables[SQL_CHARACTER_SET_CLIENT].fill_client_internal_session(jc, SQL_CHARACTER_SET_CLIENT); + conn->variables[SQL_CHARACTER_SET_CONNECTION].fill_client_internal_session(jc, SQL_CHARACTER_SET_CONNECTION); + conn->variables[SQL_CHARACTER_SET_DATABASE].fill_client_internal_session(jc, SQL_CHARACTER_SET_DATABASE); - vals[0] = j["conn"][mysql_tracked_variables[SQL_CHARACTER_SET_CLIENT].internal_variable_name]; - vals[1] = j["conn"][mysql_tracked_variables[SQL_CHARACTER_SET_CONNECTION].internal_variable_name]; + vals[0] = jc[mysql_tracked_variables[SQL_CHARACTER_SET_CLIENT].internal_variable_name]; + vals[1] = jc[mysql_tracked_variables[SQL_CHARACTER_SET_CONNECTION].internal_variable_name]; vals[2] = string(mysql_thread___default_variables[SQL_CHARACTER_SET]); - vals[3] = j["conn"][mysql_tracked_variables[SQL_CHARACTER_SET_DATABASE].internal_variable_name]; + vals[3] = jc[mysql_tracked_variables[SQL_CHARACTER_SET_DATABASE].internal_variable_name]; const char *pta[4]; for (int i=0; i<4; i++) { diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index 37c24b2db2..3f0a8ef69f 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -109,7 +109,7 @@ extern char * binary_sha1; #include "proxysql_find_charset.h" -void Variable::fill_server_internal_session(json &j, int conn_num, int idx) { +void Variable::fill_server_internal_session(json &j, int idx) { if (idx == SQL_CHARACTER_SET_RESULTS || idx == SQL_CHARACTER_SET_CLIENT || idx == SQL_CHARACTER_SET_DATABASE) { const MARIADB_CHARSET_INFO *ci = NULL; if (!value) { @@ -120,9 +120,9 @@ void Variable::fill_server_internal_session(json &j, int conn_num, int idx) { if (!ci) { if (idx == SQL_CHARACTER_SET_RESULTS && (!strcasecmp("NULL", value) || !strcasecmp("binary", value))) { if (!strcasecmp("NULL", value)) { - j["backends"][conn_num]["conn"][mysql_tracked_variables[idx].internal_variable_name] = ""; + j[mysql_tracked_variables[idx].internal_variable_name] = ""; } else { - j["backends"][conn_num]["conn"][mysql_tracked_variables[idx].internal_variable_name] = value; + j[mysql_tracked_variables[idx].internal_variable_name] = value; } } else { // LCOV_EXCL_START @@ -131,7 +131,7 @@ void Variable::fill_server_internal_session(json &j, int conn_num, int idx) { // LCOV_EXCL_STOP } } else { - j["backends"][conn_num]["conn"][mysql_tracked_variables[idx].internal_variable_name] = std::string((ci && ci->csname)?ci->csname:""); + j[mysql_tracked_variables[idx].internal_variable_name] = std::string((ci && ci->csname)?ci->csname:""); } } else if (idx == SQL_CHARACTER_SET_CONNECTION) { const MARIADB_CHARSET_INFO *ci = NULL; @@ -140,7 +140,7 @@ void Variable::fill_server_internal_session(json &j, int conn_num, int idx) { else ci = proxysql_find_charset_nr(atoi(value)); - j["backends"][conn_num]["conn"][mysql_tracked_variables[idx].internal_variable_name] = std::string((ci && ci->csname)?ci->csname:""); + j[mysql_tracked_variables[idx].internal_variable_name] = std::string((ci && ci->csname)?ci->csname:""); } else if (idx == SQL_COLLATION_CONNECTION) { const MARIADB_CHARSET_INFO *ci = NULL; if (!value) @@ -148,7 +148,7 @@ void Variable::fill_server_internal_session(json &j, int conn_num, int idx) { else ci = proxysql_find_charset_nr(atoi(value)); - j["backends"][conn_num]["conn"][mysql_tracked_variables[idx].internal_variable_name] = std::string((ci && ci->name)?ci->name:""); + j[mysql_tracked_variables[idx].internal_variable_name] = std::string((ci && ci->name)?ci->name:""); /* // NOTE: it seems we treat SQL_LOG_BIN in a special way // it doesn't seem necessary @@ -159,7 +159,7 @@ void Variable::fill_server_internal_session(json &j, int conn_num, int idx) { j["backends"][conn_num]["conn"][mysql_tracked_variables[idx].internal_variable_name] = std::string(!strcmp("1",value)?"ON":"OFF"); */ } else { - j["backends"][conn_num]["conn"][mysql_tracked_variables[idx].internal_variable_name] = std::string(value?value:""); + j[mysql_tracked_variables[idx].internal_variable_name] = std::string(value?value:""); } } @@ -174,9 +174,9 @@ void Variable::fill_client_internal_session(json &j, int idx) { if (!ci) { if (idx == SQL_CHARACTER_SET_RESULTS && (!strcasecmp("NULL", value) || !strcasecmp("binary", value))) { if (!strcasecmp("NULL", value)) { - j["conn"][mysql_tracked_variables[idx].internal_variable_name] = ""; + j[mysql_tracked_variables[idx].internal_variable_name] = ""; } else { - j["conn"][mysql_tracked_variables[idx].internal_variable_name] = value; + j[mysql_tracked_variables[idx].internal_variable_name] = value; } } else { // LCOV_EXCL_START @@ -185,7 +185,7 @@ void Variable::fill_client_internal_session(json &j, int idx) { // LCOV_EXCL_STOP } } else { - j["conn"][mysql_tracked_variables[idx].internal_variable_name] = (ci && ci->csname)?ci->csname:""; + j[mysql_tracked_variables[idx].internal_variable_name] = (ci && ci->csname)?ci->csname:""; } } else if (idx == SQL_CHARACTER_SET_CONNECTION) { const MARIADB_CHARSET_INFO *ci = NULL; @@ -193,14 +193,14 @@ void Variable::fill_client_internal_session(json &j, int idx) { ci = proxysql_find_charset_collate(mysql_tracked_variables[idx].default_value); else ci = proxysql_find_charset_nr(atoi(value)); - j["conn"][mysql_tracked_variables[idx].internal_variable_name] = (ci && ci->csname)?ci->csname:""; + j[mysql_tracked_variables[idx].internal_variable_name] = (ci && ci->csname)?ci->csname:""; } else if (idx == SQL_COLLATION_CONNECTION) { const MARIADB_CHARSET_INFO *ci = NULL; if (!value) ci = proxysql_find_charset_collate(mysql_tracked_variables[idx].default_value); else ci = proxysql_find_charset_nr(atoi(value)); - j["conn"][mysql_tracked_variables[idx].internal_variable_name] = (ci && ci->name)?ci->name:""; + j[mysql_tracked_variables[idx].internal_variable_name] = (ci && ci->name)?ci->name:""; /* // NOTE: it seems we treat SQL_LOG_BIN in a special way // it doesn't seem necessary @@ -211,7 +211,7 @@ void Variable::fill_client_internal_session(json &j, int idx) { j["conn"][mysql_tracked_variables[idx].internal_variable_name] = !strcmp("1", value)?"ON":"OFF"; */ } else { - j["conn"][mysql_tracked_variables[idx].internal_variable_name] = value?value:""; + j[mysql_tracked_variables[idx].internal_variable_name] = value?value:""; } } @@ -3008,3 +3008,87 @@ void MySQL_Connection::set_ssl_params(MYSQL *mysql, MySQLServers_SslParams *ssl_ ( ssl_params->ssl_crlpath.length() > 0 ? ssl_params->ssl_crlpath.c_str() : NULL ) ); } } + +void MySQL_Connection::get_mysql_info_json(json& j) { + char buff[32]; + sprintf(buff,"%p",mysql); + j["address"] = buff; + j["host"] = ( mysql->host ? mysql->host : "" ); + j["host_info"] = ( mysql->host_info ? mysql->host_info : "" ); + j["port"] = mysql->port; + j["server_version"] = ( mysql->server_version ? mysql->server_version : "" ); + j["user"] = ( mysql->user ? mysql->user : "" ); + j["unix_socket"] = (mysql->unix_socket ? mysql->unix_socket : ""); + j["db"] = (mysql->db ? mysql->db : ""); + j["affected_rows"] = mysql->affected_rows; + j["insert_id"] = mysql->insert_id; + j["thread_id"] = mysql->thread_id; + j["server_status"] = mysql->server_status; + j["charset"] = mysql->charset->nr; + j["charset_name"] = mysql->charset->csname; + j["options"]["charset_name"] = ( mysql->options.charset_name ? mysql->options.charset_name : "" ); + j["options"]["use_ssl"] = mysql->options.use_ssl; + j["net"]["last_errno"] = mysql->net.last_errno; + j["net"]["fd"] = mysql->net.fd; + j["net"]["max_packet_size"] = mysql->net.max_packet_size; + j["net"]["sqlstate"] = mysql->net.sqlstate; +} + +void MySQL_Connection::get_backend_conn_info_json(json& j) { + for (auto idx = 0; idx < SQL_NAME_LAST_LOW_WM; idx++) { + variables[idx].fill_server_internal_session(j, idx); + } + for (std::vector::const_iterator it_c = dynamic_variables_idx.begin(); it_c != dynamic_variables_idx.end(); it_c++) { + variables[*it_c].fill_server_internal_session(j, *it_c); + } + char buff[32]; + sprintf(buff,"%p", this); + j["address"] = buff; + j["auto_increment_delay_token"] = auto_increment_delay_token; + j["bytes_recv"] = bytes_info.bytes_recv; + j["bytes_sent"] = bytes_info.bytes_sent; + j["questions"] = statuses.questions; + j["myconnpoll_get"] = statuses.myconnpoll_get; + j["myconnpoll_put"] = statuses.myconnpoll_put; + j["session_track_gtids"] = ( options.session_track_gtids ? options.session_track_gtids : "") ; + j["init_connect"] = ( options.init_connect ? options.init_connect : ""); + j["init_connect_sent"] = options.init_connect_sent; + j["autocommit"] = ( options.autocommit ? "ON" : "OFF" ); + j["last_set_autocommit"] = options.last_set_autocommit; + j["no_backslash_escapes"] = options.no_backslash_escapes; + j["warning_count"] = warning_count; + json& js = j["status"]; + js["get_lock"] = get_status(STATUS_MYSQL_CONNECTION_GET_LOCK); + js["lock_tables"] = get_status(STATUS_MYSQL_CONNECTION_LOCK_TABLES); + js["has_savepoint"] = get_status(STATUS_MYSQL_CONNECTION_HAS_SAVEPOINT); + js["temporary_table"] = get_status(STATUS_MYSQL_CONNECTION_TEMPORARY_TABLE); + js["user_variable"] = get_status(STATUS_MYSQL_CONNECTION_USER_VARIABLE); + js["found_rows"] = get_status(STATUS_MYSQL_CONNECTION_FOUND_ROWS); + js["no_multiplex"] = get_status(STATUS_MYSQL_CONNECTION_NO_MULTIPLEX); + js["no_multiplex_HG"] = get_status(STATUS_MYSQL_CONNECTION_NO_MULTIPLEX_HG); + js["compression"] = get_status(STATUS_MYSQL_CONNECTION_COMPRESSION); + js["prepared_statement"] = get_status(STATUS_MYSQL_CONNECTION_PREPARED_STATEMENT); + js["has_warnings"] = get_status(STATUS_MYSQL_CONNECTION_HAS_WARNINGS); + { + // MultiplexDisabled : status returned by MySQL_Connection::MultiplexDisabled(); + // MultiplexDisabled_ext : status returned by MySQL_Connection::MultiplexDisabled() || MySQL_Connection::isActiveTransaction() + bool multiplex_disabled = MultiplexDisabled(); + j["MultiplexDisabled"] = multiplex_disabled; + if (multiplex_disabled == false) { + if (IsActiveTransaction() == true) { + multiplex_disabled = true; + } + } + j["MultiplexDisabled_ext"] = multiplex_disabled; + } + j["ps"]["backend_stmt_to_global_ids"] = local_stmts->backend_stmt_to_global_ids; + j["ps"]["global_stmt_to_backend_ids"] = local_stmts->global_stmt_to_backend_ids; + j["client_flag"]["value"] = options.client_flag; + j["client_flag"]["client_found_rows"] = (options.client_flag & CLIENT_FOUND_ROWS ? 1 : 0); + j["client_flag"]["client_multi_statements"] = (options.client_flag & CLIENT_MULTI_STATEMENTS ? 1 : 0); + j["client_flag"]["client_deprecate_eof"] = (options.client_flag & CLIENT_DEPRECATE_EOF ? 1 : 0); + if (mysql && ret_mysql) { + json& jcm = j["mysql"]; + get_mysql_info_json(jcm); + } +} diff --git a/lib/mysql_data_stream.cpp b/lib/mysql_data_stream.cpp index f658992d21..8cb21c6fa5 100644 --- a/lib/mysql_data_stream.cpp +++ b/lib/mysql_data_stream.cpp @@ -1549,3 +1549,72 @@ bool MySQL_Data_Stream::data_in_rbio() { } return false; } + +void MySQL_Data_Stream::get_client_myds_info_json(json& j) { + j["stream"]["pkts_recv"] = pkts_recv; + j["stream"]["pkts_sent"] = pkts_sent; + j["stream"]["bytes_recv"] = bytes_info.bytes_recv; + j["stream"]["bytes_sent"] = bytes_info.bytes_sent; + j["client_addr"]["address"] = ( addr.addr ? addr.addr : "" ); + j["client_addr"]["port"] = addr.port; + j["proxy_addr"]["address"] = ( proxy_addr.addr ? proxy_addr.addr : "" ); + j["proxy_addr"]["port"] = proxy_addr.port; + j["encrypted"] = encrypted; + if (encrypted) { + const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl); + if (cipher) { + const char * name = SSL_CIPHER_get_name(cipher); + if (name) { + j["ssl_cipher"] = name; + } + } + } + j["DSS"] = DSS; + j["switching_auth_sent"] = switching_auth_sent; + j["switching_auth_type"] = switching_auth_type; + j["prot"]["sent_auth_plugin_id"] = myprot.sent_auth_plugin_id; + j["prot"]["auth_plugin_id"] = myprot.auth_plugin_id; + + switch (myprot.auth_plugin_id) { + case AUTH_MYSQL_NATIVE_PASSWORD: + j["prot"]["auth_plugin"] = "mysql_native_password"; + break; + case AUTH_MYSQL_CLEAR_PASSWORD: + j["prot"]["auth_plugin"] = "mysql_clear_password"; + break; + case AUTH_MYSQL_CACHING_SHA2_PASSWORD: + j["prot"]["auth_plugin"] = "caching_sha2_password"; + break; + default: + break; + } + if (myconn != NULL) { // only if myconn is defined + json& jc = j["conn"]; + if (myconn->userinfo != NULL) { // only if userinfo is defined + j["userinfo"]["username"] = ( myconn->userinfo->username ? myconn->userinfo->username : "" ); + j["userinfo"]["schemaname"] = ( myconn->userinfo->schemaname ? myconn->userinfo->schemaname : "" ); +#ifdef DEBUG + j["userinfo"]["password"] = ( myconn->userinfo->password ? myconn->userinfo->password : "" ); +#endif + } + jc["session_track_gtids"] = ( myconn->options.session_track_gtids ? myconn->options.session_track_gtids : "") ; + for (auto idx = 0; idx < SQL_NAME_LAST_LOW_WM; idx++) { + myconn->variables[idx].fill_client_internal_session(jc, idx); + } + { + for (std::vector::const_iterator it_c = myconn->dynamic_variables_idx.begin(); it_c != myconn->dynamic_variables_idx.end(); it_c++) { + myconn->variables[*it_c].fill_client_internal_session(jc, *it_c); + } + } + + jc["autocommit"] = ( myconn->options.autocommit ? "ON" : "OFF" ); + jc["client_flag"]["value"] = myconn->options.client_flag; + jc["client_flag"]["client_found_rows"] = (myconn->options.client_flag & CLIENT_FOUND_ROWS ? 1 : 0); + jc["client_flag"]["client_multi_statements"] = (myconn->options.client_flag & CLIENT_MULTI_STATEMENTS ? 1 : 0); + jc["client_flag"]["client_multi_results"] = (myconn->options.client_flag & CLIENT_MULTI_RESULTS ? 1 : 0); + jc["client_flag"]["client_deprecate_eof"] = (myconn->options.client_flag & CLIENT_DEPRECATE_EOF ? 1 : 0); + jc["no_backslash_escapes"] = myconn->options.no_backslash_escapes; + jc["status"]["compression"] = myconn->get_status(STATUS_MYSQL_CONNECTION_COMPRESSION); + jc["ps"]["client_stmt_to_global_ids"] = myconn->local_stmts->client_stmt_to_global_ids; + } +} From 678c470b3eb0bf4d19922a642d17861a2234d12a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Sun, 28 Apr 2024 14:35:35 +0000 Subject: [PATCH 32/79] Fix out after refactoring generate_proxysql_internal_session_json() --- lib/MySQL_Session.cpp | 17 +--------- lib/mysql_data_stream.cpp | 67 ++++++++++++++++++++------------------- 2 files changed, 35 insertions(+), 49 deletions(-) diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 01bc41d286..7463536988 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -1381,26 +1381,11 @@ void MySQL_Session::generate_proxysql_internal_session_json(json &j) { j["gtid"]["last"] = ( strlen(gtid_buf) ? gtid_buf : "" ); json& jqpo = j["qpo"]; qpo->get_info_json(jqpo); -/* - j["qpo"]["create_new_connection"] = qpo->create_new_conn; - j["qpo"]["reconnect"] = qpo->reconnect; - j["qpo"]["sticky_conn"] = qpo->sticky_conn; - j["qpo"]["cache_timeout"] = qpo->cache_timeout; - j["qpo"]["cache_ttl"] = qpo->cache_ttl; - j["qpo"]["delay"] = qpo->delay; - j["qpo"]["destination_hostgroup"] = qpo->destination_hostgroup; - j["qpo"]["firewall_whitelist_mode"] = qpo->firewall_whitelist_mode; - j["qpo"]["multiplex"] = qpo->multiplex; - j["qpo"]["timeout"] = qpo->timeout; - j["qpo"]["retries"] = qpo->retries; - j["qpo"]["max_lag_ms"] = qpo->max_lag_ms; -*/ j["default_schema"] = ( default_schema ? default_schema : "" ); j["user_attributes"] = ( user_attributes ? user_attributes : "" ); j["transaction_persistent"] = transaction_persistent; if (client_myds != NULL) { // only if client_myds is defined - json& jclient = j["client"]; - client_myds->get_client_myds_info_json(jclient); + client_myds->get_client_myds_info_json(j); } for (unsigned int i=0; ilen; i++) { MySQL_Backend *_mybe = NULL; diff --git a/lib/mysql_data_stream.cpp b/lib/mysql_data_stream.cpp index 8cb21c6fa5..2407f3b7ee 100644 --- a/lib/mysql_data_stream.cpp +++ b/lib/mysql_data_stream.cpp @@ -1551,15 +1551,17 @@ bool MySQL_Data_Stream::data_in_rbio() { } void MySQL_Data_Stream::get_client_myds_info_json(json& j) { - j["stream"]["pkts_recv"] = pkts_recv; - j["stream"]["pkts_sent"] = pkts_sent; - j["stream"]["bytes_recv"] = bytes_info.bytes_recv; - j["stream"]["bytes_sent"] = bytes_info.bytes_sent; - j["client_addr"]["address"] = ( addr.addr ? addr.addr : "" ); - j["client_addr"]["port"] = addr.port; - j["proxy_addr"]["address"] = ( proxy_addr.addr ? proxy_addr.addr : "" ); - j["proxy_addr"]["port"] = proxy_addr.port; - j["encrypted"] = encrypted; + json& jc1 = j["client"]; + json& jc2 = j["conn"]; + jc1["stream"]["pkts_recv"] = pkts_recv; + jc1["stream"]["pkts_sent"] = pkts_sent; + jc1["stream"]["bytes_recv"] = bytes_info.bytes_recv; + jc1["stream"]["bytes_sent"] = bytes_info.bytes_sent; + jc1["client_addr"]["address"] = ( addr.addr ? addr.addr : "" ); + jc1["client_addr"]["port"] = addr.port; + jc1["proxy_addr"]["address"] = ( proxy_addr.addr ? proxy_addr.addr : "" ); + jc1["proxy_addr"]["port"] = proxy_addr.port; + jc1["encrypted"] = encrypted; if (encrypted) { const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl); if (cipher) { @@ -1569,52 +1571,51 @@ void MySQL_Data_Stream::get_client_myds_info_json(json& j) { } } } - j["DSS"] = DSS; - j["switching_auth_sent"] = switching_auth_sent; - j["switching_auth_type"] = switching_auth_type; - j["prot"]["sent_auth_plugin_id"] = myprot.sent_auth_plugin_id; - j["prot"]["auth_plugin_id"] = myprot.auth_plugin_id; + jc1["DSS"] = DSS; + jc1["switching_auth_sent"] = switching_auth_sent; + jc1["switching_auth_type"] = switching_auth_type; + jc1["prot"]["sent_auth_plugin_id"] = myprot.sent_auth_plugin_id; + jc1["prot"]["auth_plugin_id"] = myprot.auth_plugin_id; switch (myprot.auth_plugin_id) { case AUTH_MYSQL_NATIVE_PASSWORD: - j["prot"]["auth_plugin"] = "mysql_native_password"; + jc1["prot"]["auth_plugin"] = "mysql_native_password"; break; case AUTH_MYSQL_CLEAR_PASSWORD: - j["prot"]["auth_plugin"] = "mysql_clear_password"; + jc1["prot"]["auth_plugin"] = "mysql_clear_password"; break; case AUTH_MYSQL_CACHING_SHA2_PASSWORD: - j["prot"]["auth_plugin"] = "caching_sha2_password"; + jc1["prot"]["auth_plugin"] = "caching_sha2_password"; break; default: break; } if (myconn != NULL) { // only if myconn is defined - json& jc = j["conn"]; if (myconn->userinfo != NULL) { // only if userinfo is defined - j["userinfo"]["username"] = ( myconn->userinfo->username ? myconn->userinfo->username : "" ); - j["userinfo"]["schemaname"] = ( myconn->userinfo->schemaname ? myconn->userinfo->schemaname : "" ); + jc1["userinfo"]["username"] = ( myconn->userinfo->username ? myconn->userinfo->username : "" ); + jc1["userinfo"]["schemaname"] = ( myconn->userinfo->schemaname ? myconn->userinfo->schemaname : "" ); #ifdef DEBUG - j["userinfo"]["password"] = ( myconn->userinfo->password ? myconn->userinfo->password : "" ); + jc1["userinfo"]["password"] = ( myconn->userinfo->password ? myconn->userinfo->password : "" ); #endif } - jc["session_track_gtids"] = ( myconn->options.session_track_gtids ? myconn->options.session_track_gtids : "") ; + jc2["session_track_gtids"] = ( myconn->options.session_track_gtids ? myconn->options.session_track_gtids : "") ; for (auto idx = 0; idx < SQL_NAME_LAST_LOW_WM; idx++) { - myconn->variables[idx].fill_client_internal_session(jc, idx); + myconn->variables[idx].fill_client_internal_session(jc2, idx); } { for (std::vector::const_iterator it_c = myconn->dynamic_variables_idx.begin(); it_c != myconn->dynamic_variables_idx.end(); it_c++) { - myconn->variables[*it_c].fill_client_internal_session(jc, *it_c); + myconn->variables[*it_c].fill_client_internal_session(jc2, *it_c); } } - jc["autocommit"] = ( myconn->options.autocommit ? "ON" : "OFF" ); - jc["client_flag"]["value"] = myconn->options.client_flag; - jc["client_flag"]["client_found_rows"] = (myconn->options.client_flag & CLIENT_FOUND_ROWS ? 1 : 0); - jc["client_flag"]["client_multi_statements"] = (myconn->options.client_flag & CLIENT_MULTI_STATEMENTS ? 1 : 0); - jc["client_flag"]["client_multi_results"] = (myconn->options.client_flag & CLIENT_MULTI_RESULTS ? 1 : 0); - jc["client_flag"]["client_deprecate_eof"] = (myconn->options.client_flag & CLIENT_DEPRECATE_EOF ? 1 : 0); - jc["no_backslash_escapes"] = myconn->options.no_backslash_escapes; - jc["status"]["compression"] = myconn->get_status(STATUS_MYSQL_CONNECTION_COMPRESSION); - jc["ps"]["client_stmt_to_global_ids"] = myconn->local_stmts->client_stmt_to_global_ids; + jc2["autocommit"] = ( myconn->options.autocommit ? "ON" : "OFF" ); + jc2["client_flag"]["value"] = myconn->options.client_flag; + jc2["client_flag"]["client_found_rows"] = (myconn->options.client_flag & CLIENT_FOUND_ROWS ? 1 : 0); + jc2["client_flag"]["client_multi_statements"] = (myconn->options.client_flag & CLIENT_MULTI_STATEMENTS ? 1 : 0); + jc2["client_flag"]["client_multi_results"] = (myconn->options.client_flag & CLIENT_MULTI_RESULTS ? 1 : 0); + jc2["client_flag"]["client_deprecate_eof"] = (myconn->options.client_flag & CLIENT_DEPRECATE_EOF ? 1 : 0); + jc2["no_backslash_escapes"] = myconn->options.no_backslash_escapes; + jc2["status"]["compression"] = myconn->get_status(STATUS_MYSQL_CONNECTION_COMPRESSION); + jc2["ps"]["client_stmt_to_global_ids"] = myconn->local_stmts->client_stmt_to_global_ids; } } From 56f1350347a6ae418c42feaf89ac9d175d11681d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Mon, 29 Apr 2024 04:48:36 +0000 Subject: [PATCH 33/79] Optimization on multiple_prepared_statements-t multiple_prepared_statements-t.cpp is now multi-threaded --- .../tests/multiple_prepared_statements-t.cpp | 287 ++++++++++++++---- 1 file changed, 220 insertions(+), 67 deletions(-) diff --git a/test/tap/tests/multiple_prepared_statements-t.cpp b/test/tap/tests/multiple_prepared_statements-t.cpp index ea3bed6035..d88fad720b 100644 --- a/test/tap/tests/multiple_prepared_statements-t.cpp +++ b/test/tap/tests/multiple_prepared_statements-t.cpp @@ -42,9 +42,12 @@ inline unsigned long long monotonic_time() { return (((unsigned long long) ts.tv_sec) * 1000000) + (ts.tv_nsec / 1000); } +#define NTHREADS 5 #define NCONNS 6 #define NPREP 15000 #define PROGRESS 2000 + +pthread_mutex_t mtx[NCONNS]; MYSQL* conns[NCONNS]; int ids[NCONNS*NPREP]; MYSQL_STMT * stmts[NCONNS*NPREP]; @@ -125,6 +128,163 @@ int execute_stmt(int idx) { return 0; } +void * prepare_thread(void *arg) { + int thread_id = *(int *)arg; + for (int i=0; i Date: Mon, 29 Apr 2024 06:48:33 +0000 Subject: [PATCH 34/79] Better randomize in set_testing-240-t --- test/tap/tests/set_testing-240-t.cpp | 5 +++-- test/tap/tests/set_testing-240.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/test/tap/tests/set_testing-240-t.cpp b/test/tap/tests/set_testing-240-t.cpp index cacf8c0052..f5e157dfaa 100644 --- a/test/tap/tests/set_testing-240-t.cpp +++ b/test/tap/tests/set_testing-240-t.cpp @@ -116,7 +116,8 @@ std::unordered_map vars_counters; */ void * my_conn_thread(void *arg) { - g_seed = time(NULL) ^ getpid() ^ pthread_self(); + g_seed = monotonic_time() * pthread_self() + monotonic_time(); + srand(g_seed); unsigned int select_OK=0; unsigned int select_ERR=0; int i, j; @@ -168,7 +169,7 @@ void * my_conn_thread(void *arg) { int fr = rand(); int r1=fr%count; //int r2=fastrand()%testCases.size(); - int r2=rand()%testCases.size(); + int r2=(fastrand() + (RAND_MAX * fastrand())) %testCases.size(); if (j%queries_per_connections==0) { mysql_idx=r1; diff --git a/test/tap/tests/set_testing-240.h b/test/tap/tests/set_testing-240.h index 60254da37e..68b42f0016 100644 --- a/test/tap/tests/set_testing-240.h +++ b/test/tap/tests/set_testing-240.h @@ -146,7 +146,7 @@ struct cpu_timer inline int fastrand() { - g_seed = (214014*g_seed+2531011); + g_seed = (214013*g_seed+2531011); return (g_seed>>16)&0x7FFF; } From 13c5ed3f968a40579e5c04a493c0ccf2f69ce8ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Mon, 29 Apr 2024 07:15:28 +0000 Subject: [PATCH 35/79] Increase sleep time in kill_connection3-t --- test/tap/tests/kill_connection3-t.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tap/tests/kill_connection3-t.cpp b/test/tap/tests/kill_connection3-t.cpp index ef28ac6cb6..1c023ffb5a 100644 --- a/test/tap/tests/kill_connection3-t.cpp +++ b/test/tap/tests/kill_connection3-t.cpp @@ -191,7 +191,7 @@ int main(int argc, char** argv) { rc = run_q(proxysql_admin, s.c_str()); ok(rc == 0 , "%s" , s.c_str()); } - sleep(1); + sleep(3); for (int i = 0; i < NUM_CONNS ; i++) { MYSQL * mysql = conns[i]; int rc = run_q(mysql, "DO 1"); From 828ee01cdc4b7dbedc2faca394a5b31a71c664a5 Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Mon, 29 Apr 2024 12:33:37 +0500 Subject: [PATCH 36/79] Replaced monotonic_time() with time() --- include/MySQL_HostGroups_Manager.h | 2 +- lib/MyHGC.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/MySQL_HostGroups_Manager.h b/include/MySQL_HostGroups_Manager.h index b5d3cb854c..2909522fe6 100644 --- a/include/MySQL_HostGroups_Manager.h +++ b/include/MySQL_HostGroups_Manager.h @@ -316,7 +316,7 @@ class MyHGC { // MySQL Host Group Container public: unsigned int hid; std::atomic num_online_servers; - unsigned long long last_log_time_num_online_servers; + time_t last_log_time_num_online_servers; unsigned long long current_time_now; uint32_t new_connections_now; MySrvList *mysrvs; diff --git a/lib/MyHGC.cpp b/lib/MyHGC.cpp index 9919683cbf..66f296fd99 100644 --- a/lib/MyHGC.cpp +++ b/lib/MyHGC.cpp @@ -410,10 +410,10 @@ void MyHGC::refresh_online_server_count() { } void MyHGC::log_num_online_server_count_error() { - const unsigned long long curtime = monotonic_time(); + const time_t curtime = time(NULL); // if this is the first time the method is called or if more than 10 seconds have passed since the last log if (last_log_time_num_online_servers == 0 || - (curtime - last_log_time_num_online_servers > 10 * 1000 * 1000)) { + ((curtime - last_log_time_num_online_servers) > 10)) { last_log_time_num_online_servers = curtime; proxy_error( "Number of online servers detected in a hostgroup exceeds the configured maximum online servers. hostgroup:%u, num_online_servers:%u, max_online_servers:%u\n", From c7cf5ba344e0938085da3627e314da1dd0447090 Mon Sep 17 00:00:00 2001 From: Rahim Kanji Date: Mon, 29 Apr 2024 16:50:28 +0500 Subject: [PATCH 37/79] Removed MySrvStatus class --- include/MySQL_HostGroups_Manager.h | 39 ++------- lib/MyHGC.cpp | 12 +-- lib/MySQL_HostGroups_Manager.cpp | 132 ++++++++++++++--------------- lib/MySQL_Thread.cpp | 2 +- lib/MySrvC.cpp | 13 +-- lib/MySrvConnList.cpp | 2 +- lib/mysql_connection.cpp | 20 ++--- 7 files changed, 93 insertions(+), 127 deletions(-) diff --git a/include/MySQL_HostGroups_Manager.h b/include/MySQL_HostGroups_Manager.h index 2909522fe6..fa86935907 100644 --- a/include/MySQL_HostGroups_Manager.h +++ b/include/MySQL_HostGroups_Manager.h @@ -193,39 +193,6 @@ class MySrvConnList { MySQL_Connection *index(unsigned int); }; -class MySrvC; -class MySrvStatus { -public: - explicit MySrvStatus(MySrvC* _mysrvc); - ~MySrvStatus(); - - MySrvStatus& operator=(MySerStatus _status); - - inline - bool operator==(MySerStatus _status) const { - return status == _status; - } - - inline - bool operator!=(MySerStatus _status) const { - return status!=_status; - } - - inline - explicit operator MySerStatus() const { - return status; - } - - inline - explicit operator int() const { - return (int)status; - } - -private: - MySrvC *mysrvc; - enum MySerStatus status; -}; - class MySrvC { // MySQL Server Container public: MyHGC *myhgc; @@ -234,7 +201,6 @@ class MySrvC { // MySQL Server Container uint16_t gtid_port; uint16_t flags; int64_t weight; - MySrvStatus status; unsigned int compression; int64_t max_connections; unsigned int aws_aurora_current_lag_us; @@ -296,6 +262,11 @@ class MySrvC { // MySQL Server Container max_connections_used = connections_used; return max_connections_used; } + void set_status(MySerStatus _status); + inline + MySerStatus get_status() const { return status; } +private: + enum MySerStatus status; }; class MySrvList { // MySQL Server List diff --git a/lib/MyHGC.cpp b/lib/MyHGC.cpp index 66f296fd99..72ce81f746 100644 --- a/lib/MyHGC.cpp +++ b/lib/MyHGC.cpp @@ -77,7 +77,7 @@ MySrvC *MyHGC::get_random_MySrvC(char * gtid_uuid, uint64_t gtid_trxid, int max_ //int j=0; for (j=0; jidx(j); - if (mysrvc->status==MYSQL_SERVER_STATUS_ONLINE) { // consider this server only if ONLINE + if (mysrvc->get_status() == MYSQL_SERVER_STATUS_ONLINE) { // consider this server only if ONLINE if (mysrvc->myhgc->num_online_servers.load(std::memory_order_relaxed) <= mysrvc->myhgc->attributes.max_num_online_servers) { // number of online servers in HG is within configured range if (mysrvc->ConnectionsUsed->conns_length() < mysrvc->max_connections) { // consider this server only if didn't reach max_connections if (mysrvc->current_latency_us < (mysrvc->max_latency_us ? mysrvc->max_latency_us : mysql_thread___default_max_latency_ms*1000)) { // consider the host only if not too far @@ -113,7 +113,7 @@ MySrvC *MyHGC::get_random_MySrvC(char * gtid_uuid, uint64_t gtid_trxid, int max_ mysrvc->myhgc->log_num_online_server_count_error(); } } else { - if (mysrvc->status==MYSQL_SERVER_STATUS_SHUNNED) { + if (mysrvc->get_status() == MYSQL_SERVER_STATUS_SHUNNED) { // try to recover shunned servers if (mysrvc->shunned_automatic && mysql_thread___shun_recovery_time_sec) { time_t t; @@ -138,7 +138,7 @@ MySrvC *MyHGC::get_random_MySrvC(char * gtid_uuid, uint64_t gtid_trxid, int max_ proxy_info("Unshunning server %s:%d.\n", mysrvc->address, mysrvc->port); } #endif - mysrvc->status=MYSQL_SERVER_STATUS_ONLINE; + mysrvc->set_status(MYSQL_SERVER_STATUS_ONLINE); mysrvc->shunned_automatic=false; mysrvc->shunned_and_kill_all_connections=false; mysrvc->connect_ERR_at_time_last_detected_error=0; @@ -229,9 +229,9 @@ MySrvC *MyHGC::get_random_MySrvC(char * gtid_uuid, uint64_t gtid_trxid, int max_ } for (j=0; jidx(j); - if (mysrvc->status==MYSQL_SERVER_STATUS_SHUNNED && mysrvc->shunned_automatic==true) { + if (mysrvc->get_status() == MYSQL_SERVER_STATUS_SHUNNED && mysrvc->shunned_automatic == true) { if ((t - mysrvc->time_last_detected_error) > max_wait_sec) { - mysrvc->status=MYSQL_SERVER_STATUS_ONLINE; + mysrvc->set_status(MYSQL_SERVER_STATUS_ONLINE); mysrvc->shunned_automatic=false; mysrvc->connect_ERR_at_time_last_detected_error=0; mysrvc->time_last_detected_error=0; @@ -402,7 +402,7 @@ void MyHGC::refresh_online_server_count() { unsigned int online_servers_count = 0; for (unsigned int i = 0; i < mysrvs->servers->len; i++) { MySrvC* mysrvc = (MySrvC*)mysrvs->servers->index(i); - if (mysrvc->status == MYSQL_SERVER_STATUS_ONLINE) { + if (mysrvc->get_status() == MYSQL_SERVER_STATUS_ONLINE) { online_servers_count++; } } diff --git a/lib/MySQL_HostGroups_Manager.cpp b/lib/MySQL_HostGroups_Manager.cpp index 376f7778eb..be931d31d9 100644 --- a/lib/MySQL_HostGroups_Manager.cpp +++ b/lib/MySQL_HostGroups_Manager.cpp @@ -1292,7 +1292,7 @@ bool MySQL_HostGroups_Manager::commit( long long ptr=atoll(r->fields[0]); proxy_warning("Removed server at address %lld, hostgroup %s, address %s port %s. Setting status OFFLINE HARD and immediately dropping all free connections. Used connections will be dropped when trying to use them\n", ptr, r->fields[1], r->fields[2], r->fields[3]); MySrvC *mysrvc=(MySrvC *)ptr; - mysrvc->status=MYSQL_SERVER_STATUS_OFFLINE_HARD; + mysrvc->set_status(MYSQL_SERVER_STATUS_OFFLINE_HARD); mysrvc->ConnectionsFree->drop_all_connections(); char *q1=(char *)"DELETE FROM mysql_servers WHERE mem_pointer=%lld"; char *q2=(char *)malloc(strlen(q1)+32); @@ -1338,14 +1338,14 @@ bool MySQL_HostGroups_Manager::commit( for (std::vector::iterator it = resultset->rows.begin() ; it != resultset->rows.end(); ++it) { SQLite3_row *r=*it; long long ptr=atoll(r->fields[12]); // increase this index every time a new column is added - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 5, "Server %s:%d , weight=%d, status=%d, mem_pointer=%llu, hostgroup=%d, compression=%d\n", r->fields[1], atoi(r->fields[2]), atoi(r->fields[4]), (MySerStatus) atoi(r->fields[5]), ptr, atoi(r->fields[0]), atoi(r->fields[6])); + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 5, "Server %s:%d , weight=%d, status=%d, mem_pointer=%llu, hostgroup=%d, compression=%d\n", r->fields[1], atoi(r->fields[2]), atoi(r->fields[4]), atoi(r->fields[5]), ptr, atoi(r->fields[0]), atoi(r->fields[6])); //fprintf(stderr,"%lld\n", ptr); if (ptr==0) { if (GloMTH->variables.hostgroup_manager_verbose) { - proxy_info("Creating new server in HG %d : %s:%d , gtid_port=%d, weight=%d, status=%d\n", atoi(r->fields[0]), r->fields[1], atoi(r->fields[2]), atoi(r->fields[3]), atoi(r->fields[4]), (MySerStatus) atoi(r->fields[5])); + proxy_info("Creating new server in HG %d : %s:%d , gtid_port=%d, weight=%d, status=%d\n", atoi(r->fields[0]), r->fields[1], atoi(r->fields[2]), atoi(r->fields[3]), atoi(r->fields[4]), atoi(r->fields[5])); } - MySrvC *mysrvc=new MySrvC(r->fields[1], atoi(r->fields[2]), atoi(r->fields[3]), atoi(r->fields[4]), (MySerStatus) atoi(r->fields[5]), atoi(r->fields[6]), atoi(r->fields[7]), atoi(r->fields[8]), atoi(r->fields[9]), atoi(r->fields[10]), r->fields[11]); // add new fields here if adding more columns in mysql_servers - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 5, "Adding new server %s:%d , weight=%d, status=%d, mem_ptr=%p into hostgroup=%d\n", r->fields[1], atoi(r->fields[2]), atoi(r->fields[4]), (MySerStatus) atoi(r->fields[5]), mysrvc, atoi(r->fields[0])); + MySrvC *mysrvc=new MySrvC(r->fields[1], atoi(r->fields[2]), atoi(r->fields[3]), atoi(r->fields[4]), (MySerStatus)atoi(r->fields[5]), atoi(r->fields[6]), atoi(r->fields[7]), atoi(r->fields[8]), atoi(r->fields[9]), atoi(r->fields[10]), r->fields[11]); // add new fields here if adding more columns in mysql_servers + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 5, "Adding new server %s:%d , weight=%d, status=%d, mem_ptr=%p into hostgroup=%d\n", r->fields[1], atoi(r->fields[2]), atoi(r->fields[4]), atoi(r->fields[5]), mysrvc, atoi(r->fields[0])); add(mysrvc,atoi(r->fields[0])); ptr=(uintptr_t)mysrvc; rc=(*proxy_sqlite3_bind_int64)(statement1, 1, ptr); ASSERT_SQLITE_OK(rc, mydb); @@ -1378,7 +1378,7 @@ bool MySQL_HostGroups_Manager::commit( if (atoi(r->fields[5])!=atoi(r->fields[15])) { bool change_server_status = true; if (GloMTH->variables.evaluate_replication_lag_on_servers_load == 1) { - if (mysrvc->status == MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG && // currently server is shunned due to replication lag + if (mysrvc->get_status() == MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG && // currently server is shunned due to replication lag (MySerStatus)atoi(r->fields[15]) == MYSQL_SERVER_STATUS_ONLINE) { // new server status is online if (mysrvc->cur_replication_lag != -2) { // Master server? Seconds_Behind_Master column is not present const unsigned int new_max_repl_lag = atoi(r->fields[18]); @@ -1392,10 +1392,10 @@ bool MySQL_HostGroups_Manager::commit( } if (change_server_status == true) { if (GloMTH->variables.hostgroup_manager_verbose) - proxy_info("Changing status for server %d:%s:%d (%s:%d) from %d (%d) to %d\n", mysrvc->myhgc->hid, mysrvc->address, mysrvc->port, r->fields[1], atoi(r->fields[2]), atoi(r->fields[5]), (int)mysrvc->status, atoi(r->fields[15])); - mysrvc->status = (MySerStatus)atoi(r->fields[15]); + proxy_info("Changing status for server %d:%s:%d (%s:%d) from %d (%d) to %d\n", mysrvc->myhgc->hid, mysrvc->address, mysrvc->port, r->fields[1], atoi(r->fields[2]), atoi(r->fields[5]), (int)mysrvc->get_status(), atoi(r->fields[15])); + mysrvc->set_status((MySerStatus)atoi(r->fields[15])); } - if (mysrvc->status==MYSQL_SERVER_STATUS_SHUNNED) { + if (mysrvc->get_status() == MYSQL_SERVER_STATUS_SHUNNED) { mysrvc->shunned_automatic=false; } } @@ -1414,11 +1414,11 @@ bool MySQL_HostGroups_Manager::commit( proxy_info("Changing max_replication_lag for server %u:%s:%d (%s:%d) from %d (%d) to %d\n" , mysrvc->myhgc->hid , mysrvc->address, mysrvc->port, r->fields[1], atoi(r->fields[2]), atoi(r->fields[8]) , mysrvc->max_replication_lag , atoi(r->fields[18])); mysrvc->max_replication_lag=atoi(r->fields[18]); if (mysrvc->max_replication_lag == 0) { // we just changed it to 0 - if (mysrvc->status == MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG) { + if (mysrvc->get_status() == MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG) { // the server is currently shunned due to replication lag // but we reset max_replication_lag to 0 // therefore we immediately reset the status too - mysrvc->status = MYSQL_SERVER_STATUS_ONLINE; + mysrvc->set_status(MYSQL_SERVER_STATUS_ONLINE); } } } @@ -1440,7 +1440,7 @@ bool MySQL_HostGroups_Manager::commit( } if (run_update) { rc=(*proxy_sqlite3_bind_int64)(statement2, 1, mysrvc->weight); ASSERT_SQLITE_OK(rc, mydb); - rc=(*proxy_sqlite3_bind_int64)(statement2, 2, (int)mysrvc->status); ASSERT_SQLITE_OK(rc, mydb); + rc=(*proxy_sqlite3_bind_int64)(statement2, 2, (int)mysrvc->get_status()); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement2, 3, mysrvc->compression); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement2, 4, mysrvc->max_connections); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement2, 5, mysrvc->max_replication_lag); ASSERT_SQLITE_OK(rc, mydb); @@ -1690,7 +1690,7 @@ void MySQL_HostGroups_Manager::generate_mysql_gtid_executed_tables() { } if (gtid_is) { gtid_is->active = true; - } else if (mysrvc->status != MYSQL_SERVER_STATUS_OFFLINE_HARD) { + } else if (mysrvc->get_status() != MYSQL_SERVER_STATUS_OFFLINE_HARD) { // we didn't find it. Create it /* struct ev_io *watcher = (struct ev_io *)malloc(sizeof(struct ev_io)); @@ -1745,7 +1745,7 @@ void MySQL_HostGroups_Manager::purge_mysql_servers_table() { MySrvC *mysrvc=NULL; for (unsigned int j=0; jmysrvs->servers->len; j++) { mysrvc=myhgc->mysrvs->idx(j); - if (mysrvc->status==MYSQL_SERVER_STATUS_OFFLINE_HARD) { + if (mysrvc->get_status() == MYSQL_SERVER_STATUS_OFFLINE_HARD) { if (mysrvc->ConnectionsUsed->conns_length()==0 && mysrvc->ConnectionsFree->conns_length()==0) { // no more connections for OFFLINE_HARD server, removing it mysrvc=(MySrvC *)myhgc->mysrvs->servers->remove_index_fast(j); @@ -1800,7 +1800,7 @@ void MySQL_HostGroups_Manager::generate_mysql_servers_table(int *_onlyhg) { mysrvc=myhgc->mysrvs->idx(j); if (mysql_thread___hostgroup_manager_verbose) { char *st; - switch ((int)mysrvc->status) { + switch ((int)mysrvc->get_status()) { case 0: st=(char *)"ONLINE"; break; @@ -1830,7 +1830,7 @@ void MySQL_HostGroups_Manager::generate_mysql_servers_table(int *_onlyhg) { rc=(*proxy_sqlite3_bind_int64)(statement32, (i*13)+3, mysrvc->port); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement32, (i*13)+4, mysrvc->gtid_port); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement32, (i*13)+5, mysrvc->weight); ASSERT_SQLITE_OK(rc, mydb); - rc=(*proxy_sqlite3_bind_int64)(statement32, (i*13)+6, (int)mysrvc->status); ASSERT_SQLITE_OK(rc, mydb); + rc=(*proxy_sqlite3_bind_int64)(statement32, (i*13)+6, (int)mysrvc->get_status()); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement32, (i*13)+7, mysrvc->compression); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement32, (i*13)+8, mysrvc->max_connections); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement32, (i*13)+9, mysrvc->max_replication_lag); ASSERT_SQLITE_OK(rc, mydb); @@ -1853,7 +1853,7 @@ void MySQL_HostGroups_Manager::generate_mysql_servers_table(int *_onlyhg) { rc=(*proxy_sqlite3_bind_int64)(statement1, 3, mysrvc->port); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement1, 4, mysrvc->gtid_port); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement1, 5, mysrvc->weight); ASSERT_SQLITE_OK(rc, mydb); - rc=(*proxy_sqlite3_bind_int64)(statement1, 6, (int)mysrvc->status); ASSERT_SQLITE_OK(rc, mydb); + rc=(*proxy_sqlite3_bind_int64)(statement1, 6, (int)mysrvc->get_status()); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement1, 7, mysrvc->compression); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement1, 8, mysrvc->max_connections); ASSERT_SQLITE_OK(rc, mydb); rc=(*proxy_sqlite3_bind_int64)(statement1, 9, mysrvc->max_replication_lag); ASSERT_SQLITE_OK(rc, mydb); @@ -2341,7 +2341,7 @@ void MySQL_HostGroups_Manager::push_MyConn_to_pool(MySQL_Connection *c, bool _lo mysrvc = static_cast(c->parent); // Log debug information about the connection being returned to the pool - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySQL_Connection %p, server %s:%d with status %d\n", c, mysrvc->address, mysrvc->port, (int)mysrvc->status); + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySQL_Connection %p, server %s:%d with status %d\n", c, mysrvc->address, mysrvc->port, (int)mysrvc->get_status()); // Remove the connection from the list of used connections for the parent server mysrvc->ConnectionsUsed->remove(c); @@ -2353,18 +2353,18 @@ void MySQL_HostGroups_Manager::push_MyConn_to_pool(MySQL_Connection *c, bool _lo // If the largest query length exceeds the threshold, destroy the connection if (c->largest_query_length > (unsigned int)GloMTH->variables.threshold_query_length) { - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Destroying MySQL_Connection %p, server %s:%d with status %d . largest_query_length = %lu\n", c, mysrvc->address, mysrvc->port, (int)mysrvc->status, c->largest_query_length); + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Destroying MySQL_Connection %p, server %s:%d with status %d . largest_query_length = %lu\n", c, mysrvc->address, mysrvc->port, (int)mysrvc->get_status(), c->largest_query_length); delete c; goto __exit_push_MyConn_to_pool; } // If the server is online and the connection is in the idle state - if (mysrvc->status==MYSQL_SERVER_STATUS_ONLINE) { + if (mysrvc->get_status() == MYSQL_SERVER_STATUS_ONLINE) { if (c->async_state_machine==ASYNC_IDLE) { if (GloMTH == NULL) { goto __exit_push_MyConn_to_pool; } if (c->local_stmts->get_num_backend_stmts() > (unsigned int)GloMTH->variables.max_stmts_per_connection) { // Check if the connection has too many prepared statements // Log debug information about destroying the connection due to too many prepared statements - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Destroying MySQL_Connection %p, server %s:%d with status %d because has too many prepared statements\n", c, mysrvc->address, mysrvc->port, (int)mysrvc->status); + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Destroying MySQL_Connection %p, server %s:%d with status %d because has too many prepared statements\n", c, mysrvc->address, mysrvc->port, (int)mysrvc->get_status()); // delete c; mysrvc->ConnectionsUsed->add(c); // Add the connection back to the list of used connections destroy_MyConn_from_pool(c, false); // Destroy the connection from the pool @@ -2374,12 +2374,12 @@ void MySQL_HostGroups_Manager::push_MyConn_to_pool(MySQL_Connection *c, bool _lo } } else { // Log debug information about destroying the connection - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Destroying MySQL_Connection %p, server %s:%d with status %d\n", c, mysrvc->address, mysrvc->port, (int)mysrvc->status); + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Destroying MySQL_Connection %p, server %s:%d with status %d\n", c, mysrvc->address, mysrvc->port, (int)mysrvc->get_status()); delete c; // Destroy the connection } } else { // Log debug information about destroying the connection - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Destroying MySQL_Connection %p, server %s:%d with status %d\n", c, mysrvc->address, mysrvc->port, (int)mysrvc->status); + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Destroying MySQL_Connection %p, server %s:%d with status %d\n", c, mysrvc->address, mysrvc->port, (int)mysrvc->get_status()); delete c; // Destroy the connection } @@ -2445,7 +2445,7 @@ void MySQL_HostGroups_Manager::unshun_server_all_hostgroups(const char * address bool found = false; // was this server already found in this hostgroup? for (j=0; found==false && j<(int)myhgc->mysrvs->cnt(); j++) { MySrvC *mysrvc=(MySrvC *)myhgc->mysrvs->servers->index(j); - if (mysrvc->status==MYSQL_SERVER_STATUS_SHUNNED) { + if (mysrvc->get_status() == MYSQL_SERVER_STATUS_SHUNNED) { // we only care for SHUNNED nodes // Note that we check for address and port only for status==MYSQL_SERVER_STATUS_SHUNNED , // that means that potentially we will pass by the matching node and still looping . @@ -2463,7 +2463,7 @@ void MySQL_HostGroups_Manager::unshun_server_all_hostgroups(const char * address if (GloMTH->variables.hostgroup_manager_verbose >= 3) { proxy_info("Unshunning server %d:%s:%d . time_last_detected_error=%lu\n", mysrvc->myhgc->hid, address, port, mysrvc->time_last_detected_error); } - mysrvc->status=MYSQL_SERVER_STATUS_ONLINE; + mysrvc->set_status(MYSQL_SERVER_STATUS_ONLINE); mysrvc->shunned_automatic=false; mysrvc->shunned_and_kill_all_connections=false; mysrvc->connect_ERR_at_time_last_detected_error=0; @@ -2538,7 +2538,7 @@ MySQL_Connection * MySQL_HostGroups_Manager::get_MyConn_from_pool(unsigned int _ void MySQL_HostGroups_Manager::destroy_MyConn_from_pool(MySQL_Connection *c, bool _lock) { bool to_del=true; // the default, legacy behavior MySrvC *mysrvc=(MySrvC *)c->parent; - if (mysrvc->status==MYSQL_SERVER_STATUS_ONLINE && c->send_quit && queue.size() < __sync_fetch_and_add(&GloMTH->variables.connpoll_reset_queue_length,0)) { + if (mysrvc->get_status() == MYSQL_SERVER_STATUS_ONLINE && c->send_quit && queue.size() < __sync_fetch_and_add(&GloMTH->variables.connpoll_reset_queue_length, 0)) { if (c->async_state_machine==ASYNC_IDLE) { // overall, the backend seems healthy and so it is the connection. Try to reset it int myerr=mysql_errno(c->mysql); @@ -2698,7 +2698,7 @@ void MySQL_HostGroups_Manager::replication_lag_action_inner(MyHGC *myhgc, const MySrvC *mysrvc=(MySrvC *)myhgc->mysrvs->servers->index(j); if (strcmp(mysrvc->address,address)==0 && mysrvc->port==port) { mysrvc->cur_replication_lag = current_replication_lag; - if (mysrvc->status==MYSQL_SERVER_STATUS_ONLINE) { + if (mysrvc->get_status() == MYSQL_SERVER_STATUS_ONLINE) { if ( // (current_replication_lag==-1 ) // || @@ -2712,7 +2712,7 @@ void MySQL_HostGroups_Manager::replication_lag_action_inner(MyHGC *myhgc, const mysrvc->cur_replication_lag_count += 1; if (mysrvc->cur_replication_lag_count >= (unsigned int)mysql_thread___monitor_replication_lag_count) { proxy_warning("Shunning server %s:%d from HG %u with replication lag of %d second, count number: '%d'\n", address, port, myhgc->hid, current_replication_lag, mysrvc->cur_replication_lag_count); - mysrvc->status=MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG; + mysrvc->set_status(MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG); } else { proxy_info( "Not shunning server %s:%d from HG %u with replication lag of %d second, count number: '%d' < replication_lag_count: '%d'\n", @@ -2728,13 +2728,13 @@ void MySQL_HostGroups_Manager::replication_lag_action_inner(MyHGC *myhgc, const mysrvc->cur_replication_lag_count = 0; } } else { - if (mysrvc->status==MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG) { + if (mysrvc->get_status() == MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG) { if ( (current_replication_lag>=0 && ((unsigned int)current_replication_lag <= mysrvc->max_replication_lag)) || (current_replication_lag==-2) // see issue 959 ) { - mysrvc->status=MYSQL_SERVER_STATUS_ONLINE; + mysrvc->set_status(MYSQL_SERVER_STATUS_ONLINE); proxy_warning("Re-enabling server %s:%d from HG %u with replication lag of %d second\n", address, port, myhgc->hid, current_replication_lag); mysrvc->cur_replication_lag_count = 0; } @@ -2808,25 +2808,25 @@ void MySQL_HostGroups_Manager::group_replication_lag_action_set_server_status(My MySrvC *mysrvc=(MySrvC *)myhgc->mysrvs->servers->index(j); proxy_debug( PROXY_DEBUG_MONITOR, 6, "Server 'MySrvC' - address: %s, port: %d, status: %d\n", mysrvc->address, - mysrvc->port, (int)mysrvc->status + mysrvc->port, (int)mysrvc->get_status() ); if (strcmp(mysrvc->address,address)==0 && mysrvc->port==port) { if (enable == true) { - if (mysrvc->status==MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG || mysrvc->status==MYSQL_SERVER_STATUS_SHUNNED) { - mysrvc->status=MYSQL_SERVER_STATUS_ONLINE; + if (mysrvc->get_status() == MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG || mysrvc->get_status() == MYSQL_SERVER_STATUS_SHUNNED) { + mysrvc->set_status(MYSQL_SERVER_STATUS_ONLINE); proxy_info("Re-enabling server %u:%s:%d from replication lag\n", myhgc->hid, address, port); } } else { - if (mysrvc->status==MYSQL_SERVER_STATUS_ONLINE) { + if (mysrvc->get_status()==MYSQL_SERVER_STATUS_ONLINE) { proxy_warning("Shunning 'soft' server %u:%s:%d with replication lag, count number: %d\n", myhgc->hid, address, port, lag_count); - mysrvc->status=MYSQL_SERVER_STATUS_SHUNNED; + mysrvc->set_status(MYSQL_SERVER_STATUS_SHUNNED); } else { - if (mysrvc->status==MYSQL_SERVER_STATUS_SHUNNED) { + if (mysrvc->get_status()==MYSQL_SERVER_STATUS_SHUNNED) { if (lag_count >= ( mysql_thread___monitor_groupreplication_max_transactions_behind_count * 2 )) { proxy_warning("Shunning 'hard' server %u:%s:%d with replication lag, count number: %d\n", myhgc->hid, address, port, lag_count); - mysrvc->status=MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG; + mysrvc->set_status(MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG); } } } @@ -2911,7 +2911,7 @@ void MySQL_HostGroups_Manager::drop_all_idle_connections() { MyHGC *myhgc=(MyHGC *)MyHostGroups->index(i); for (j=0; j<(int)myhgc->mysrvs->cnt(); j++) { MySrvC *mysrvc=(MySrvC *)myhgc->mysrvs->servers->index(j); - if (mysrvc->status!=MYSQL_SERVER_STATUS_ONLINE) { + if (mysrvc->get_status()!=MYSQL_SERVER_STATUS_ONLINE) { proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 5, "Server %s:%d is not online\n", mysrvc->address, mysrvc->port); //__sync_fetch_and_sub(&status.server_connections_connected, mysrvc->ConnectionsFree->conns->len); mysrvc->ConnectionsFree->drop_all_connections(); @@ -3187,7 +3187,7 @@ SQLite3_result * MySQL_HostGroups_Manager::SQL3_Free_Connections() { MyHGC *myhgc=(MyHGC *)MyHostGroups->index(i); for (j=0; j<(int)myhgc->mysrvs->cnt(); j++) { MySrvC *mysrvc=(MySrvC *)myhgc->mysrvs->servers->index(j); - if (mysrvc->status!=MYSQL_SERVER_STATUS_ONLINE) { + if (mysrvc->get_status()!=MYSQL_SERVER_STATUS_ONLINE) { proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 5, "Server %s:%d is not online\n", mysrvc->address, mysrvc->port); mysrvc->ConnectionsFree->drop_all_connections(); } @@ -3386,7 +3386,7 @@ void MySQL_HostGroups_Manager::p_update_connection_pool() { // proxysql_connection_pool_status metric p_update_connection_pool_update_gauge(endpoint_id, common_labels, - status.p_connection_pool_status_map, ((int)mysrvc->status) + 1, p_hg_dyn_gauge::connection_pool_status); + status.p_connection_pool_status_map, ((int)mysrvc->get_status()) + 1, p_hg_dyn_gauge::connection_pool_status); } } @@ -3445,7 +3445,7 @@ SQLite3_result * MySQL_HostGroups_Manager::SQL3_Connection_Pool(bool _reset, int for (j=0; j<(int)myhgc->mysrvs->cnt(); j++) { MySrvC *mysrvc=(MySrvC *)myhgc->mysrvs->servers->index(j); if (hid == NULL) { - if (mysrvc->status!=MYSQL_SERVER_STATUS_ONLINE) { + if (mysrvc->get_status()!=MYSQL_SERVER_STATUS_ONLINE) { proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 5, "Server %s:%d is not online\n", mysrvc->address, mysrvc->port); //__sync_fetch_and_sub(&status.server_connections_connected, mysrvc->ConnectionsFree->conns->len); mysrvc->ConnectionsFree->drop_all_connections(); @@ -3469,7 +3469,7 @@ SQLite3_result * MySQL_HostGroups_Manager::SQL3_Connection_Pool(bool _reset, int pta[1]=strdup(mysrvc->address); sprintf(buf,"%d", mysrvc->port); pta[2]=strdup(buf); - switch ((int)mysrvc->status) { + switch ((int)mysrvc->get_status()) { case 0: pta[3]=strdup("ONLINE"); break; @@ -4057,16 +4057,16 @@ bool MySQL_HostGroups_Manager::shun_and_killall(char *hostname, int port) { for (j=0; jmysrvs->idx(j); if (mysrvc->port==port && strcmp(mysrvc->address,hostname)==0) { - switch ((MySerStatus)mysrvc->status) { + switch ((MySerStatus)mysrvc->get_status()) { case MYSQL_SERVER_STATUS_SHUNNED: if (mysrvc->shunned_automatic==false) { break; } case MYSQL_SERVER_STATUS_ONLINE: - if (mysrvc->status == MYSQL_SERVER_STATUS_ONLINE) { + if (mysrvc->get_status() == MYSQL_SERVER_STATUS_ONLINE) { ret = true; } - mysrvc->status=MYSQL_SERVER_STATUS_SHUNNED; + mysrvc->set_status(MYSQL_SERVER_STATUS_SHUNNED); case MYSQL_SERVER_STATUS_OFFLINE_SOFT: mysrvc->shunned_automatic=true; mysrvc->shunned_and_kill_all_connections=true; @@ -4914,7 +4914,7 @@ void MySQL_HostGroups_Manager::update_group_replication_add_autodiscovered( // the servers to runtime. if (strcmp(mysrvc->address,_host.c_str())==0 && mysrvc->port==_port) { srv_found = true; - if (mysrvc->status == MYSQL_SERVER_STATUS_OFFLINE_HARD) { + if (mysrvc->get_status() == MYSQL_SERVER_STATUS_OFFLINE_HARD) { reset_hg_attrs_server_defaults(mysrvc); update_hg_attrs_server_defaults(mysrvc, mysrvc->myhgc); proxy_info( @@ -4922,7 +4922,7 @@ void MySQL_HostGroups_Manager::update_group_replication_add_autodiscovered( " hostgroup=%d, weight=%ld, max_connections=%ld, use_ssl=%d\n", _host.c_str(), _port, reader_hg, mysrvc->weight, mysrvc->max_connections, mysrvc->use_ssl ); - mysrvc->status = MYSQL_SERVER_STATUS_ONLINE; + mysrvc->set_status(MYSQL_SERVER_STATUS_ONLINE); srv_found_offline = true; } } @@ -6594,22 +6594,22 @@ bool MySQL_HostGroups_Manager::aws_aurora_replication_lag_action(int _whid, int if (strcmp(mysrvc->address,address)==0 && mysrvc->port==port) { // we found the server if (enable==false) { - if (mysrvc->status == MYSQL_SERVER_STATUS_ONLINE) { + if (mysrvc->get_status() == MYSQL_SERVER_STATUS_ONLINE) { if (verbose) { proxy_warning("Shunning server %s:%d from HG %u with replication lag of %f microseconds\n", address, port, myhgc->hid, current_replication_lag_ms); } - mysrvc->status = MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG; + mysrvc->set_status(MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG); } } else { - if (mysrvc->status == MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG) { + if (mysrvc->get_status() == MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG) { if (verbose) { proxy_warning("Re-enabling server %s:%d from HG %u with replication lag of %f microseconds\n", address, port, myhgc->hid, current_replication_lag_ms); } - mysrvc->status = MYSQL_SERVER_STATUS_ONLINE; + mysrvc->set_status(MYSQL_SERVER_STATUS_ONLINE); } } mysrvc->aws_aurora_current_lag_us = current_replication_lag_ms * 1000; - if (mysrvc->status == MYSQL_SERVER_STATUS_ONLINE || mysrvc->status == MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG) { + if (mysrvc->get_status() == MYSQL_SERVER_STATUS_ONLINE || mysrvc->get_status() == MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG) { // we perform check only if ONLINE or lagging if (ret) { if (_whid==(int)myhgc->hid && is_writer==false) { @@ -6638,8 +6638,8 @@ bool MySQL_HostGroups_Manager::aws_aurora_replication_lag_action(int _whid, int if (is_writer==true) if (enable==true) if (_whid==(int)myhgc->hid) - if (mysrvc->status == MYSQL_SERVER_STATUS_OFFLINE_HARD) { - mysrvc->status = MYSQL_SERVER_STATUS_ONLINE; + if (mysrvc->get_status() == MYSQL_SERVER_STATUS_OFFLINE_HARD) { + mysrvc->set_status(MYSQL_SERVER_STATUS_ONLINE); proxy_warning("Re-enabling server %s:%d from HG %u because it is a writer\n", address, port, myhgc->hid); ret = true; } @@ -6686,10 +6686,10 @@ int MySQL_HostGroups_Manager::create_new_server_in_hg( // 'servers_defaults' attributes from its corresponding 'MyHGC'. This way we ensure uniform behavior // of new servers, and 'OFFLINE_HARD' ones when a user update 'servers_defaults' values, and reloads // the servers to runtime. - if (mysrvc && mysrvc->status == MYSQL_SERVER_STATUS_OFFLINE_HARD) { + if (mysrvc && mysrvc->get_status() == MYSQL_SERVER_STATUS_OFFLINE_HARD) { reset_hg_attrs_server_defaults(mysrvc); update_hg_attrs_server_defaults(mysrvc, mysrvc->myhgc); - mysrvc->status = MYSQL_SERVER_STATUS_ONLINE; + mysrvc->set_status(MYSQL_SERVER_STATUS_ONLINE); proxy_info( "Found healthy previously discovered %s node %s:%d as 'OFFLINE_HARD', setting back as 'ONLINE' with:" @@ -6721,7 +6721,7 @@ int MySQL_HostGroups_Manager::remove_server_in_hg(uint32_t hid, const string& ad ); // Set the server status - mysrvc->status=MYSQL_SERVER_STATUS_OFFLINE_HARD; + mysrvc->set_status(MYSQL_SERVER_STATUS_OFFLINE_HARD); mysrvc->ConnectionsFree->drop_all_connections(); // TODO-NOTE: This is only required in case the caller isn't going to perform: @@ -7190,11 +7190,11 @@ void MySQL_HostGroups_Manager::HostGroup_Server_Mapping::copy_if_not_exists(Type for (auto& node : append) { - if (node.srv->status == MYSQL_SERVER_STATUS_SHUNNED || - node.srv->status == MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG) { + if (node.srv->get_status() == MYSQL_SERVER_STATUS_SHUNNED || + node.srv->get_status() == MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG) { // Status updated from "*SHUNNED" to "ONLINE" as "read_only" value was successfully // retrieved from the backend server, indicating server is now online. - node.srv->status = MYSQL_SERVER_STATUS_ONLINE; + node.srv->set_status(MYSQL_SERVER_STATUS_ONLINE); } MySrvC* new_srv = insert_HGM(get_hostgroup_id(dest_type, node), node.srv); @@ -7254,7 +7254,7 @@ MySrvC* MySQL_HostGroups_Manager::HostGroup_Server_Mapping::insert_HGM(unsigned for (uint32_t j = 0; j < myhgc->mysrvs->cnt(); j++) { MySrvC* mysrvc = static_cast(myhgc->mysrvs->servers->index(j)); if (strcmp(mysrvc->address, srv->address) == 0 && mysrvc->port == srv->port) { - if (mysrvc->status == MYSQL_SERVER_STATUS_OFFLINE_HARD) { + if (mysrvc->get_status() == MYSQL_SERVER_STATUS_OFFLINE_HARD) { mysrvc->gtid_port = srv->gtid_port; mysrvc->weight = srv->weight; @@ -7264,7 +7264,7 @@ MySrvC* MySQL_HostGroups_Manager::HostGroup_Server_Mapping::insert_HGM(unsigned mysrvc->use_ssl = srv->use_ssl; mysrvc->max_latency_us = srv->max_latency_us; mysrvc->comment = strdup(srv->comment); - mysrvc->status = MYSQL_SERVER_STATUS_ONLINE; + mysrvc->set_status(MYSQL_SERVER_STATUS_ONLINE); if (GloMTH->variables.hostgroup_manager_verbose) { proxy_info( @@ -7284,12 +7284,12 @@ MySrvC* MySQL_HostGroups_Manager::HostGroup_Server_Mapping::insert_HGM(unsigned if (!ret_srv) { if (GloMTH->variables.hostgroup_manager_verbose) { - proxy_info("Creating new server in HG %d : %s:%d , gtid_port=%d, weight=%ld, status=%d\n", hostgroup_id, srv->address, srv->port, srv->gtid_port, srv->weight, (int)srv->status); + proxy_info("Creating new server in HG %d : %s:%d , gtid_port=%d, weight=%ld, status=%d\n", hostgroup_id, srv->address, srv->port, srv->gtid_port, srv->weight, (int)srv->get_status()); } - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 5, "Adding new server %s:%d , weight=%ld, status=%d, mem_ptr=%p into hostgroup=%d\n", srv->address, srv->port, srv->weight, (int)srv->status, srv, hostgroup_id); + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 5, "Adding new server %s:%d , weight=%ld, status=%d, mem_ptr=%p into hostgroup=%d\n", srv->address, srv->port, srv->weight, (int)srv->get_status(), srv, hostgroup_id); - ret_srv = new MySrvC(srv->address, srv->port, srv->gtid_port, srv->weight, (MySerStatus)srv->status, srv->compression, + ret_srv = new MySrvC(srv->address, srv->port, srv->gtid_port, srv->weight, srv->get_status(), srv->compression, srv->max_connections, srv->max_replication_lag, srv->use_ssl, (srv->max_latency_us / 1000), srv->comment); myhgc->mysrvs->add(ret_srv); @@ -7300,7 +7300,7 @@ MySrvC* MySQL_HostGroups_Manager::HostGroup_Server_Mapping::insert_HGM(unsigned void MySQL_HostGroups_Manager::HostGroup_Server_Mapping::remove_HGM(MySrvC* srv) { proxy_warning("Removed server at address %p, hostgroup %d, address %s port %d. Setting status OFFLINE HARD and immediately dropping all free connections. Used connections will be dropped when trying to use them\n", (void*)srv, srv->myhgc->hid, srv->address, srv->port); - srv->status = MYSQL_SERVER_STATUS_OFFLINE_HARD; + srv->set_status(MYSQL_SERVER_STATUS_OFFLINE_HARD); srv->ConnectionsFree->drop_all_connections(); } diff --git a/lib/MySQL_Thread.cpp b/lib/MySQL_Thread.cpp index 167b2aa733..f6c54f0dbc 100644 --- a/lib/MySQL_Thread.cpp +++ b/lib/MySQL_Thread.cpp @@ -5706,7 +5706,7 @@ void MySQL_Thread::push_MyConn_local(MySQL_Connection *c) { mysrvc=(MySrvC *)c->parent; // reset insert_id #1093 c->mysql->insert_id = 0; - if (mysrvc->status==MYSQL_SERVER_STATUS_ONLINE) { + if (mysrvc->get_status() == MYSQL_SERVER_STATUS_ONLINE) { if (c->async_state_machine==ASYNC_IDLE) { cached_connections->add(c); return; // all went well diff --git a/lib/MySrvC.cpp b/lib/MySrvC.cpp index 07143dcaa5..860ae56efe 100644 --- a/lib/MySrvC.cpp +++ b/lib/MySrvC.cpp @@ -9,11 +9,12 @@ MySrvC::MySrvC( char* add, uint16_t p, uint16_t gp, int64_t _weight, enum MySerStatus _status, unsigned int _compression, int64_t _max_connections, unsigned int _max_replication_lag, int32_t _use_ssl, unsigned int _max_latency_ms, char* _comment -) : status(this) { +) { address=strdup(add); port=p; gtid_port=gp; weight=_weight; + status = _status; compression=_compression; max_connections=_max_connections; max_replication_lag=_max_replication_lag; @@ -39,7 +40,6 @@ MySrvC::MySrvC( comment=strdup(_comment); ConnectionsUsed=new MySrvConnList(this); ConnectionsFree=new MySrvConnList(this); - status = _status; } void MySrvC::connect_error(int err_num, bool get_mutex) { @@ -143,12 +143,7 @@ MySrvC::~MySrvC() { delete ConnectionsFree; } -MySrvStatus::MySrvStatus(MySrvC* _mysrvc) : mysrvc(_mysrvc) {} - -MySrvStatus::~MySrvStatus() { if (mysrvc->myhgc) mysrvc->myhgc->refresh_online_server_count(); } - -MySrvStatus& MySrvStatus::operator=(MySerStatus _status) { +void MySrvC::set_status(MySerStatus _status) { status = _status; - if (mysrvc->myhgc) mysrvc->myhgc->refresh_online_server_count(); - return *this; + if (myhgc)myhgc->refresh_online_server_count(); } diff --git a/lib/MySrvConnList.cpp b/lib/MySrvConnList.cpp index ace65635e2..e2175c830b 100644 --- a/lib/MySrvConnList.cpp +++ b/lib/MySrvConnList.cpp @@ -40,7 +40,7 @@ MySrvConnList::~MySrvConnList() { } void MySrvConnList::drop_all_connections() { - proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Dropping all connections (%u total) on MySrvConnList %p for server %s:%d , hostgroup=%d , status=%d\n", conns_length(), this, mysrvc->address, mysrvc->port, mysrvc->myhgc->hid, (int)mysrvc->status); + proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Dropping all connections (%u total) on MySrvConnList %p for server %s:%d , hostgroup=%d , status=%d\n", conns_length(), this, mysrvc->address, mysrvc->port, mysrvc->myhgc->hid, (int)mysrvc->get_status()); while (conns_length()) { MySQL_Connection *conn=(MySQL_Connection *)conns->remove_index_fast(0); delete conn; diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index e360f9ad60..246481195f 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -2035,7 +2035,7 @@ bool MySQL_Connection::IsServerOffline() { bool ret=false; if (parent==NULL) return ret; - server_status=(MySerStatus)parent->status; // we copy it here to avoid race condition. The caller will see this + server_status=parent->get_status(); // we copy it here to avoid race condition. The caller will see this if ( (server_status==MYSQL_SERVER_STATUS_OFFLINE_HARD) // the server is OFFLINE as specific by the user || @@ -2059,7 +2059,7 @@ int MySQL_Connection::async_query(short event, char *stmt, unsigned long length, PROXY_TRACE2(); assert(mysql); assert(ret_mysql); - server_status=(MySerStatus)parent->status; // we copy it here to avoid race condition. The caller will see this + server_status=parent->get_status(); // we copy it here to avoid race condition. The caller will see this if (IsServerOffline()) return -1; @@ -2198,7 +2198,7 @@ int MySQL_Connection::async_change_user(short event) { PROXY_TRACE(); assert(mysql); assert(ret_mysql); - server_status=(MySerStatus)parent->status; // we copy it here to avoid race condition. The caller will see this + server_status=parent->get_status(); // we copy it here to avoid race condition. The caller will see this if (IsServerOffline()) return -1; @@ -2245,7 +2245,7 @@ int MySQL_Connection::async_select_db(short event) { PROXY_TRACE(); assert(mysql); assert(ret_mysql); - server_status=(MySerStatus)parent->status; // we copy it here to avoid race condition. The caller will see this + server_status=parent->get_status(); // we copy it here to avoid race condition. The caller will see this if (IsServerOffline()) return -1; @@ -2286,7 +2286,7 @@ int MySQL_Connection::async_set_autocommit(short event, bool ac) { PROXY_TRACE(); assert(mysql); assert(ret_mysql); - server_status=(MySerStatus)parent->status; // we copy it here to avoid race condition. The caller will see this + server_status=parent->get_status(); // we copy it here to avoid race condition. The caller will see this if (IsServerOffline()) return -1; @@ -2329,7 +2329,7 @@ int MySQL_Connection::async_set_names(short event, unsigned int c) { PROXY_TRACE(); assert(mysql); assert(ret_mysql); - server_status=(MySerStatus)parent->status; // we copy it here to avoid race condition. The caller will see this + server_status=parent->get_status(); // we copy it here to avoid race condition. The caller will see this if (IsServerOffline()) return -1; @@ -2372,7 +2372,7 @@ int MySQL_Connection::async_set_option(short event, bool mask) { PROXY_TRACE(); assert(mysql); assert(ret_mysql); - server_status=(MySerStatus)parent->status; // we copy it here to avoid race condition. The caller will see this + server_status=parent->get_status(); // we copy it here to avoid race condition. The caller will see this if (IsServerOffline()) return -1; @@ -2854,11 +2854,11 @@ int MySQL_Connection::async_send_simple_command(short event, char *stmt, unsigne PROXY_TRACE(); assert(mysql); assert(ret_mysql); - server_status=(MySerStatus)parent->status; // we copy it here to avoid race condition. The caller will see this + server_status=parent->get_status(); // we copy it here to avoid race condition. The caller will see this if ( - (parent->status==MYSQL_SERVER_STATUS_OFFLINE_HARD) // the server is OFFLINE as specific by the user + (parent->get_status()==MYSQL_SERVER_STATUS_OFFLINE_HARD) // the server is OFFLINE as specific by the user || - (parent->status==MYSQL_SERVER_STATUS_SHUNNED && parent->shunned_automatic==true && parent->shunned_and_kill_all_connections==true) // the server is SHUNNED due to a serious issue + (parent->get_status()==MYSQL_SERVER_STATUS_SHUNNED && parent->shunned_automatic==true && parent->shunned_and_kill_all_connections==true) // the server is SHUNNED due to a serious issue ) { return -1; } From 43b077efcad75613dd92ec61a30f0434e8378c4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Mon, 29 Apr 2024 12:28:21 +0000 Subject: [PATCH 38/79] set fileencoding on mysql_connection.cpp :set fileencoding=utf-8 on mysql_connection.cpp --- lib/mysql_connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index 3f0a8ef69f..58d8e5ddd3 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -1721,7 +1721,7 @@ MDB_ASYNC_ST MySQL_Connection::handler(short event) { // since 'add_eof' utilizes 'warning_count,' we are setting the 'warning_count' here update_warning_count_from_connection(); // we reach here if there was no error - // exclude warning_count from the OK/EOF packet for the ‘SHOW WARNINGS’ statement + // exclude warning_count from the OK/EOF packet for the ‘SHOW WARNINGSÂ’ statement MyRS->add_eof(query.length == 13 && strncasecmp(query.ptr, "SHOW WARNINGS", 13) == 0); NEXT_IMMEDIATE(ASYNC_QUERY_END); } From 4264e9349d5ba3f570a639264fa0a3157713183d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Tue, 30 Apr 2024 00:11:59 +0000 Subject: [PATCH 39/79] Refactoring MySQL_Connection::connect_start() --- include/mysql_connection.h | 5 ++ lib/mysql_connection.cpp | 120 ++++++++++++++++++++++--------------- 2 files changed, 78 insertions(+), 47 deletions(-) diff --git a/include/mysql_connection.h b/include/mysql_connection.h index 9fb33c86fc..7696866c71 100644 --- a/include/mysql_connection.h +++ b/include/mysql_connection.h @@ -60,6 +60,11 @@ class MySQL_Connection { void update_warning_count_from_statement(); bool is_expired(unsigned long long timeout); unsigned long long inserted_into_pool; + void connect_start_SetAttributes(); + void connect_start_SetCharset(); + void connect_start_SetClientFlag(unsigned long&); + char * connect_start_DNS_lookup(); + void connect_start_SetSslSettings(); public: struct { char *server_version; diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index 58d8e5ddd3..620524da3c 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -740,12 +740,7 @@ bool MySQL_Connection::match_tracked_options(const MySQL_Connection *c) { return false; } -// non blocking API -void MySQL_Connection::connect_start() { - PROXY_TRACE(); - mysql=mysql_init(NULL); - assert(mysql); - mysql_options(mysql, MYSQL_OPT_NONBLOCK, 0); +void MySQL_Connection::connect_start_SetAttributes() { mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "program_name", "proxysql"); mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_server_host", parent->address); { @@ -770,18 +765,10 @@ void MySQL_Connection::connect_start() { } mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "mysql_bug_102266", "Avoid MySQL bug https://bugs.mysql.com/bug.php?id=102266 , https://github.com/sysown/proxysql/issues/3276"); } - if (parent->use_ssl) { - if (ssl_params != NULL) { - delete ssl_params; - ssl_params = NULL; - } - ssl_params = MyHGM->get_Server_SSL_Params(parent->address, parent->port, userinfo->username); - MySQL_Connection::set_ssl_params(mysql, ssl_params); - mysql_options(mysql, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); - } - unsigned int timeout= 1; +} + +void MySQL_Connection::connect_start_SetCharset() { const char *csname = NULL; - mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&timeout); /* Take client character set and use it to connect to backend */ if (myds && myds->sess) { csname = mysql_variables.client_get_value(myds->sess, SQL_CHARACTER_SET); @@ -815,7 +802,10 @@ void MySQL_Connection::connect_start() { } //mysql_options(mysql, MYSQL_SET_CHARSET_NAME, c->csname); mysql->charset = c; - unsigned long client_flags = 0; +} + +void MySQL_Connection::connect_start_SetClientFlag(unsigned long& client_flags) { + client_flags = 0; if (parent->compression) client_flags |= CLIENT_COMPRESS; @@ -874,46 +864,82 @@ void MySQL_Connection::connect_start() { } } - char *auth_password=NULL; - if (userinfo->password) { - if (userinfo->password[0]=='*') { // we don't have the real password, let's pass sha1 - auth_password=userinfo->sha1_pass; - } else { - auth_password=userinfo->password; - } - } - if (parent->port) { +} - char* host_ip = NULL; - const std::string& res_ip = MySQL_Monitor::dns_lookup(parent->address, false); +char * MySQL_Connection::connect_start_DNS_lookup() { + char* host_ip = NULL; + const std::string& res_ip = MySQL_Monitor::dns_lookup(parent->address, false); - if (!res_ip.empty()) { - if (connected_host_details.hostname) { - if (strcmp(connected_host_details.hostname, parent->address) != 0) { - free(connected_host_details.hostname); - connected_host_details.hostname = strdup(parent->address); - } - } - else { + if (!res_ip.empty()) { + if (connected_host_details.hostname) { + if (strcmp(connected_host_details.hostname, parent->address) != 0) { + free(connected_host_details.hostname); connected_host_details.hostname = strdup(parent->address); } + } + else { + connected_host_details.hostname = strdup(parent->address); + } - if (connected_host_details.ip) { - if (strcmp(connected_host_details.ip, res_ip.c_str()) != 0) { - free(connected_host_details.ip); - connected_host_details.ip = strdup(res_ip.c_str()); - } - } - else { + if (connected_host_details.ip) { + if (strcmp(connected_host_details.ip, res_ip.c_str()) != 0) { + free(connected_host_details.ip); connected_host_details.ip = strdup(res_ip.c_str()); } - - host_ip = connected_host_details.ip; } else { - host_ip = parent->address; + connected_host_details.ip = strdup(res_ip.c_str()); } + host_ip = connected_host_details.ip; + } + else { + host_ip = parent->address; + } + return host_ip; +} + +void MySQL_Connection::connect_start_SetSslSettings() { + if (parent->use_ssl) { + if (ssl_params != NULL) { + delete ssl_params; + ssl_params = NULL; + } + ssl_params = MyHGM->get_Server_SSL_Params(parent->address, parent->port, userinfo->username); + MySQL_Connection::set_ssl_params(mysql, ssl_params); + mysql_options(mysql, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); + } +} + +// non blocking API +void MySQL_Connection::connect_start() { + PROXY_TRACE(); + mysql=mysql_init(NULL); + assert(mysql); + mysql_options(mysql, MYSQL_OPT_NONBLOCK, 0); + + connect_start_SetAttributes(); + + connect_start_SetSslSettings(); + + unsigned int timeout= 1; + mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&timeout); + + connect_start_SetCharset(); + + unsigned long client_flags = 0; + connect_start_SetClientFlag(client_flags); + + char *auth_password=NULL; + if (userinfo->password) { + if (userinfo->password[0]=='*') { // we don't have the real password, let's pass sha1 + auth_password=userinfo->sha1_pass; + } else { + auth_password=userinfo->password; + } + } + if (parent->port) { + char* host_ip = connect_start_DNS_lookup(); async_exit_status=mysql_real_connect_start(&ret_mysql, mysql, host_ip, userinfo->username, auth_password, userinfo->schemaname, parent->port, NULL, client_flags); } else { client_flags &= ~(CLIENT_COMPRESS); // disabling compression for connections made via Unix socket From 2d89f06a5afb5c3c2b53bd6acd4e1cd5729919e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Tue, 30 Apr 2024 00:28:08 +0000 Subject: [PATCH 40/79] Refactoring of ProcessQueryAndSetStatusFlags() --- include/mysql_connection.h | 4 ++ lib/mysql_connection.cpp | 130 ++++++++++++++++++++++--------------- 2 files changed, 80 insertions(+), 54 deletions(-) diff --git a/include/mysql_connection.h b/include/mysql_connection.h index 7696866c71..f88e41eb35 100644 --- a/include/mysql_connection.h +++ b/include/mysql_connection.h @@ -65,6 +65,10 @@ class MySQL_Connection { void connect_start_SetClientFlag(unsigned long&); char * connect_start_DNS_lookup(); void connect_start_SetSslSettings(); + void ProcessQueryAndSetStatusFlags_Warnings(char *); + void ProcessQueryAndSetStatusFlags_UserVariables(char *, int); + void ProcessQueryAndSetStatusFlags_Savepoint(char *); + void ProcessQueryAndSetStatusFlags_SetBackslashEscapes(); public: struct { char *server_version; diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index 620524da3c..252d0067d6 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -2669,24 +2669,7 @@ bool MySQL_Connection::IsKeepMultiplexEnabledVariables(char *query_digest_text) return true; } -void MySQL_Connection::ProcessQueryAndSetStatusFlags(char *query_digest_text) { - if (query_digest_text==NULL) return; - // unknown what to do with multiplex - int mul=-1; - if (myds) { - if (myds->sess) { - if (myds->sess->qpo) { - mul=myds->sess->qpo->multiplex; - if (mul==0) { - set_status(true, STATUS_MYSQL_CONNECTION_NO_MULTIPLEX); - } else { - if (mul==1) { - set_status(false, STATUS_MYSQL_CONNECTION_NO_MULTIPLEX); - } - } - } - } - } +void MySQL_Connection::ProcessQueryAndSetStatusFlags_Warnings(char *query_digest_text) { // checking warnings and disabling multiplexing will be effective only when the mysql-query_digests is enabled if (get_status(STATUS_MYSQL_CONNECTION_HAS_WARNINGS) == false) { if (warning_count > 0) { @@ -2712,7 +2695,10 @@ void MySQL_Connection::ProcessQueryAndSetStatusFlags(char *query_digest_text) { set_status(false, STATUS_MYSQL_CONNECTION_HAS_WARNINGS); } } - +} + + +void MySQL_Connection::ProcessQueryAndSetStatusFlags_UserVariables(char *query_digest_text, int mul) { if (get_status(STATUS_MYSQL_CONNECTION_USER_VARIABLE)==false) { // we search for variables only if not already set // if ( // strncasecmp(query_digest_text,"SELECT @@tx_isolation", strlen("SELECT @@tx_isolation")) @@ -2757,41 +2743,9 @@ void MySQL_Connection::ProcessQueryAndSetStatusFlags(char *query_digest_text) { } } } - if (get_status(STATUS_MYSQL_CONNECTION_PREPARED_STATEMENT)==false) { // we search if prepared was already executed - if (!strncasecmp(query_digest_text,"PREPARE ", strlen("PREPARE "))) { - set_status(true, STATUS_MYSQL_CONNECTION_PREPARED_STATEMENT); - } - } - if (get_status(STATUS_MYSQL_CONNECTION_TEMPORARY_TABLE)==false) { // we search for temporary if not already set - if (!strncasecmp(query_digest_text,"CREATE TEMPORARY TABLE ", strlen("CREATE TEMPORARY TABLE "))) { - set_status(true, STATUS_MYSQL_CONNECTION_TEMPORARY_TABLE); - } - } - if (get_status(STATUS_MYSQL_CONNECTION_LOCK_TABLES)==false) { // we search for lock tables only if not already set - if (!strncasecmp(query_digest_text,"LOCK TABLE", strlen("LOCK TABLE"))) { - set_status(true, STATUS_MYSQL_CONNECTION_LOCK_TABLES); - } - } - if (get_status(STATUS_MYSQL_CONNECTION_LOCK_TABLES)==false) { // we search for lock tables only if not already set - if (!strncasecmp(query_digest_text,"FLUSH TABLES WITH READ LOCK", strlen("FLUSH TABLES WITH READ LOCK"))) { // issue 613 - set_status(true, STATUS_MYSQL_CONNECTION_LOCK_TABLES); - } - } - if (get_status(STATUS_MYSQL_CONNECTION_LOCK_TABLES)==true) { - if (!strncasecmp(query_digest_text,"UNLOCK TABLES", strlen("UNLOCK TABLES"))) { - set_status(false, STATUS_MYSQL_CONNECTION_LOCK_TABLES); - } - } - if (get_status(STATUS_MYSQL_CONNECTION_GET_LOCK)==false) { // we search for get_lock if not already set - if (strcasestr(query_digest_text,"GET_LOCK(")) { - set_status(true, STATUS_MYSQL_CONNECTION_GET_LOCK); - } - } - if (get_status(STATUS_MYSQL_CONNECTION_FOUND_ROWS)==false) { // we search for SQL_CALC_FOUND_ROWS if not already set - if (strcasestr(query_digest_text,"SQL_CALC_FOUND_ROWS")) { - set_status(true, STATUS_MYSQL_CONNECTION_FOUND_ROWS); - } - } +} + +void MySQL_Connection::ProcessQueryAndSetStatusFlags_Savepoint(char *query_digest_text) { if (get_status(STATUS_MYSQL_CONNECTION_HAS_SAVEPOINT)==false) { if (mysql) { if ( @@ -2821,6 +2775,9 @@ void MySQL_Connection::ProcessQueryAndSetStatusFlags(char *query_digest_text) { set_status(false, STATUS_MYSQL_CONNECTION_HAS_SAVEPOINT); } } +} + +void MySQL_Connection::ProcessQueryAndSetStatusFlags_SetBackslashEscapes() { if (mysql) { if (myds && myds->sess) { if (myds->sess->client_myds && myds->sess->client_myds->myconn) { @@ -2834,6 +2791,71 @@ void MySQL_Connection::ProcessQueryAndSetStatusFlags(char *query_digest_text) { } } +void MySQL_Connection::ProcessQueryAndSetStatusFlags(char *query_digest_text) { + if (query_digest_text==NULL) return; + // unknown what to do with multiplex + int mul=-1; + if (myds) { + if (myds->sess) { + if (myds->sess->qpo) { + mul=myds->sess->qpo->multiplex; + if (mul==0) { + set_status(true, STATUS_MYSQL_CONNECTION_NO_MULTIPLEX); + } else { + if (mul==1) { + set_status(false, STATUS_MYSQL_CONNECTION_NO_MULTIPLEX); + } + } + } + } + } + + ProcessQueryAndSetStatusFlags_Warnings(query_digest_text); + + ProcessQueryAndSetStatusFlags_UserVariables(query_digest_text, mul); + + if (get_status(STATUS_MYSQL_CONNECTION_PREPARED_STATEMENT)==false) { // we search if prepared was already executed + if (!strncasecmp(query_digest_text,"PREPARE ", strlen("PREPARE "))) { + set_status(true, STATUS_MYSQL_CONNECTION_PREPARED_STATEMENT); + } + } + if (get_status(STATUS_MYSQL_CONNECTION_TEMPORARY_TABLE)==false) { // we search for temporary if not already set + if (!strncasecmp(query_digest_text,"CREATE TEMPORARY TABLE ", strlen("CREATE TEMPORARY TABLE "))) { + set_status(true, STATUS_MYSQL_CONNECTION_TEMPORARY_TABLE); + } + } + if (get_status(STATUS_MYSQL_CONNECTION_LOCK_TABLES)==false) { // we search for lock tables only if not already set + if (!strncasecmp(query_digest_text,"LOCK TABLE", strlen("LOCK TABLE"))) { + set_status(true, STATUS_MYSQL_CONNECTION_LOCK_TABLES); + } + } + if (get_status(STATUS_MYSQL_CONNECTION_LOCK_TABLES)==false) { // we search for lock tables only if not already set + if (!strncasecmp(query_digest_text,"FLUSH TABLES WITH READ LOCK", strlen("FLUSH TABLES WITH READ LOCK"))) { // issue 613 + set_status(true, STATUS_MYSQL_CONNECTION_LOCK_TABLES); + } + } + if (get_status(STATUS_MYSQL_CONNECTION_LOCK_TABLES)==true) { + if (!strncasecmp(query_digest_text,"UNLOCK TABLES", strlen("UNLOCK TABLES"))) { + set_status(false, STATUS_MYSQL_CONNECTION_LOCK_TABLES); + } + } + if (get_status(STATUS_MYSQL_CONNECTION_GET_LOCK)==false) { // we search for get_lock if not already set + if (strcasestr(query_digest_text,"GET_LOCK(")) { + set_status(true, STATUS_MYSQL_CONNECTION_GET_LOCK); + } + } + if (get_status(STATUS_MYSQL_CONNECTION_FOUND_ROWS)==false) { // we search for SQL_CALC_FOUND_ROWS if not already set + if (strcasestr(query_digest_text,"SQL_CALC_FOUND_ROWS")) { + set_status(true, STATUS_MYSQL_CONNECTION_FOUND_ROWS); + } + } + + ProcessQueryAndSetStatusFlags_Savepoint(query_digest_text); + + ProcessQueryAndSetStatusFlags_SetBackslashEscapes(); + +} + void MySQL_Connection::optimize() { if (mysql->net.max_packet > 65536) { // FIXME: temporary, maybe for very long time . This needs to become a global variable if ( ( mysql->net.buff == mysql->net.read_pos ) && ( mysql->net.read_pos == mysql->net.write_pos ) ) { From 6020216dd24f87167580363dc0810b4d90afeecd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Tue, 30 Apr 2024 03:29:09 +0000 Subject: [PATCH 41/79] Various refactoring in MySQL_Thread.cpp --- include/MySQL_Thread.h | 9 ++ lib/MySQL_Thread.cpp | 307 ++++++++++++++++++++++------------------- 2 files changed, 177 insertions(+), 139 deletions(-) diff --git a/include/MySQL_Thread.h b/include/MySQL_Thread.h index 906b4ab433..dc36a0f9f6 100644 --- a/include/MySQL_Thread.h +++ b/include/MySQL_Thread.h @@ -129,6 +129,7 @@ class __attribute__((aligned(64))) MySQL_Thread void idle_thread_prepares_session_to_send_to_worker_thread(int i); void idle_thread_to_kill_idle_sessions(); bool move_session_to_idle_mysql_sessions(MySQL_Data_Stream *myds, unsigned int n); + void run_Handle_epoll_wait(int); #endif // IDLE_THREADS unsigned int find_session_idx_in_mysql_sessions(MySQL_Session *sess); @@ -142,6 +143,13 @@ class __attribute__((aligned(64))) MySQL_Thread void tune_timeout_for_session_needs_pause(MySQL_Data_Stream *myds); void configure_pollout(MySQL_Data_Stream *myds, unsigned int n); + void run_MoveSessionsBetweenThreads(); + void run_BootstrapListener(); + int run_ComputePollTimeout(); + void run_StopListener(); + void run_SetAllSession_ToProcess0(); + + protected: int nfds; @@ -212,6 +220,7 @@ class __attribute__((aligned(64))) MySQL_Thread void ProcessAllSessions_SortingSessions(); void ProcessAllSessions_CompletedMirrorSession(unsigned int& n, MySQL_Session *sess); void ProcessAllSessions_MaintenanceLoop(MySQL_Session *sess, unsigned long long sess_time, unsigned int& total_active_transactions_); + void ProcessAllSessions_Healthy0(MySQL_Session *sess, unsigned int& n); void process_all_sessions(); void refresh_variables(); void register_session_connection_handler(MySQL_Session *_sess, bool _new=false); diff --git a/lib/MySQL_Thread.cpp b/lib/MySQL_Thread.cpp index 167b2aa733..72dedf1475 100644 --- a/lib/MySQL_Thread.cpp +++ b/lib/MySQL_Thread.cpp @@ -3142,6 +3142,134 @@ void MySQL_Thread::run___cleanup_mirror_queue() { } } + +#ifdef IDLE_THREADS +/** + * @brief Handles session assignment and retrieval between worker and idle threads. + * + * This block of code checks if the global configuration allows idle threads and if the current thread + * is not an idle maintenance thread. If both conditions are met, it randomly selects an idle worker thread + * and assigns sessions to it. Then, it retrieves sessions from the idle thread. + * + * @note This functionality is part of the management of worker and idle threads in the MySQL thread pool. + * It facilitates the distribution of sessions between active worker threads and idle threads to optimize resource utilization. + * + * @param idle_maintenance_thread Flag indicating whether the current thread is an idle maintenance thread. + * @param GloVars Global configuration variables for the MySQL thread. + * @param GloMTH Global MySQL thread handlers object. + */ +void MySQL_Thread::run_MoveSessionsBetweenThreads() { + if (GloVars.global.idle_threads) { + int r=rand()%(GloMTH->num_threads); + MySQL_Thread *thr=GloMTH->mysql_threads_idles[r].worker; + worker_thread_assigns_sessions_to_idle_thread(thr); + worker_thread_gets_sessions_from_idle_thread(); + } +} + +void MySQL_Thread::run_Handle_epoll_wait(int rc) { + if (rc) { + int i; + for (i=0; ilen && maintenance_loop) { + if (curtime == last_maintenance_time) { + idle_thread_to_kill_idle_sessions(); + } + } +} +#endif // IDLE_THREADS + + +void MySQL_Thread::run_BootstrapListener() { + unsigned int n; + while ( // spin here if ... + (n=__sync_add_and_fetch(&mypolls.pending_listener_add,0)) // there is a new listener to add + || + (GloMTH->bootstrapping_listeners == true) // MySQL_Thread_Handlers has more listeners to configure + ) { + if (n) { + poll_listener_add(n); + assert(__sync_bool_compare_and_swap(&mypolls.pending_listener_add,n,0)); + } else { + if (GloMTH->bootstrapping_listeners == false) { + // we stop looping + mypolls.bootstrapping_listeners = false; + } + } +#ifdef DEBUG + usleep(5+rand()%10); +#endif + } +} + +int MySQL_Thread::run_ComputePollTimeout() { + proxy_debug(PROXY_DEBUG_NET, 7, "poll_timeout=%u\n", mypolls.poll_timeout); + if (mysql_thread___wait_timeout==0) { + // we should be going into PAUSE mode + if (mypolls.poll_timeout==0 || mypolls.poll_timeout > 100000) { + mypolls.poll_timeout=100000; + } + } + proxy_debug(PROXY_DEBUG_NET, 7, "poll_timeout=%u\n", mypolls.poll_timeout); + + + pre_poll_time=curtime; + int ttw = ( mypolls.poll_timeout ? ( mypolls.poll_timeout/1000 < (unsigned int) mysql_thread___poll_timeout ? mypolls.poll_timeout/1000 : mysql_thread___poll_timeout ) : mysql_thread___poll_timeout ); + return ttw; +} + + +void MySQL_Thread::run_StopListener() { + unsigned int n; + while ((n=__sync_add_and_fetch(&mypolls.pending_listener_del,0))) { // spin here + if (static_cast(n) == -1) { + for (unsigned int i = 0; i < mypolls.len; i++) { + if (mypolls.myds[i] && mypolls.myds[i]->myds_type == MYDS_LISTENER) { + poll_listener_del(mypolls.myds[i]->fd); + } + } + } else { + poll_listener_del(n); + } + assert(__sync_bool_compare_and_swap(&mypolls.pending_listener_del,n,0)); + } +} + + +void MySQL_Thread::run_SetAllSession_ToProcess0() { + unsigned int n; +#ifdef IDLE_THREADS + // @note: in MySQL_Thread::run we have: bool idle_maintenance_thread=epoll_thread; + // Thus idle_maintenance_thread and epoll_thread are equivalent. + if (epoll_thread==false) { +#endif // IDLE_THREADS + for (n=0; nlen; n++) { + MySQL_Session *_sess=(MySQL_Session *)mysql_sessions->index(n); + _sess->to_process=0; + } +#ifdef IDLE_THREADS + } +#endif // IDLE_THREADS +} + + // main loop /** * @brief Main loop for the MySQL thread. @@ -3152,7 +3280,6 @@ void MySQL_Thread::run___cleanup_mirror_queue() { * @note This method assumes that relevant variables, mutexes, and objects have been properly initialized. */ void MySQL_Thread::run() { - unsigned int n; int rc; #ifdef IDLE_THREADS @@ -3196,79 +3323,28 @@ void MySQL_Thread::run() { if (idle_maintenance_thread) { idle_thread_gets_sessions_from_worker_thread(); - goto __run_skip_1a; - } + } else { #endif // IDLE_THREADS - handle_mirror_queue_mysql_sessions(); + handle_mirror_queue_mysql_sessions(); - ProcessAllMyDS_BeforePoll(); + ProcessAllMyDS_BeforePoll(); #ifdef IDLE_THREADS - /** - * @brief Handles session assignment and retrieval between worker and idle threads. - * - * This block of code checks if the global configuration allows idle threads and if the current thread - * is not an idle maintenance thread. If both conditions are met, it randomly selects an idle worker thread - * and assigns sessions to it. Then, it retrieves sessions from the idle thread. - * - * @note This functionality is part of the management of worker and idle threads in the MySQL thread pool. - * It facilitates the distribution of sessions between active worker threads and idle threads to optimize resource utilization. - * - * @param idle_maintenance_thread Flag indicating whether the current thread is an idle maintenance thread. - * @param GloVars Global configuration variables for the MySQL thread. - * @param GloMTH Global MySQL thread handlers object. - */ - if (GloVars.global.idle_threads) { - if (idle_maintenance_thread==false) { - int r=rand()%(GloMTH->num_threads); - MySQL_Thread *thr=GloMTH->mysql_threads_idles[r].worker; - worker_thread_assigns_sessions_to_idle_thread(thr); - worker_thread_gets_sessions_from_idle_thread(); - } + run_MoveSessionsBetweenThreads(); } - - -__run_skip_1a: #endif // IDLE_THREADS pthread_mutex_unlock(&thread_mutex); if (unlikely(mypolls.bootstrapping_listeners == true)) { - while ( // spin here if ... - (n=__sync_add_and_fetch(&mypolls.pending_listener_add,0)) // there is a new listener to add - || - (GloMTH->bootstrapping_listeners == true) // MySQL_Thread_Handlers has more listeners to configure - ) { - if (n) { - poll_listener_add(n); - assert(__sync_bool_compare_and_swap(&mypolls.pending_listener_add,n,0)); - } else { - if (GloMTH->bootstrapping_listeners == false) { - // we stop looping - mypolls.bootstrapping_listeners = false; - } - } -#ifdef DEBUG - usleep(5+rand()%10); -#endif - } - } - - proxy_debug(PROXY_DEBUG_NET, 7, "poll_timeout=%u\n", mypolls.poll_timeout); - if (mysql_thread___wait_timeout==0) { - // we should be going into PAUSE mode - if (mypolls.poll_timeout==0 || mypolls.poll_timeout > 100000) { - mypolls.poll_timeout=100000; - } + run_BootstrapListener(); } - proxy_debug(PROXY_DEBUG_NET, 7, "poll_timeout=%u\n", mypolls.poll_timeout); - // flush mysql log file GloMyLogger->flush(); - pre_poll_time=curtime; - int ttw = ( mypolls.poll_timeout ? ( mypolls.poll_timeout/1000 < (unsigned int) mysql_thread___poll_timeout ? mypolls.poll_timeout/1000 : mysql_thread___poll_timeout ) : mysql_thread___poll_timeout ); + int ttw = run_ComputePollTimeout(); + #ifdef IDLE_THREADS if (GloVars.global.idle_threads && idle_maintenance_thread) { memset(events,0,sizeof(struct epoll_event)*MY_EPOLL_THREAD_MAXEVENTS); // let's make valgrind happy. It also seems that needs to be zeroed anyway @@ -3286,17 +3362,8 @@ void MySQL_Thread::run() { #endif // IDLE_THREADS if (unlikely(maintenance_loop == true)) { - while ((n=__sync_add_and_fetch(&mypolls.pending_listener_del,0))) { // spin here - if (static_cast(n) == -1) { - for (unsigned int i = 0; i < mypolls.len; i++) { - if (mypolls.myds[i] && mypolls.myds[i]->myds_type == MYDS_LISTENER) { - poll_listener_del(mypolls.myds[i]->fd); - } - } - } else { - poll_listener_del(n); - } - assert(__sync_bool_compare_and_swap(&mypolls.pending_listener_del,n,0)); + if (unlikely(__sync_add_and_fetch(&mypolls.pending_listener_del,0))) { + run_StopListener(); } } @@ -3366,55 +3433,12 @@ void MySQL_Thread::run() { refresh_variables(); } -#ifdef IDLE_THREADS - if (idle_maintenance_thread==false) { -#endif // IDLE_THREADS - for (n=0; nlen; n++) { - MySQL_Session *_sess=(MySQL_Session *)mysql_sessions->index(n); - _sess->to_process=0; - } -#ifdef IDLE_THREADS - } -#endif // IDLE_THREADS + run_SetAllSession_ToProcess0(); #ifdef IDLE_THREADS // here we handle epoll_wait() if (GloVars.global.idle_threads && idle_maintenance_thread) { - if (rc) { - int i; - for (i=0; ilen && maintenance_loop) { - if (curtime == last_maintenance_time) { - idle_thread_to_kill_idle_sessions(); - } - } - goto __run_skip_2; - } -#endif // IDLE_THREADS - - ProcessAllMyDS_AfterPoll(); - -#ifdef IDLE_THREADS -__run_skip_2: - if (GloVars.global.idle_threads && idle_maintenance_thread) { - // this is an idle thread + run_Handle_epoll_wait(rc); unsigned int w=rand()%(GloMTH->num_threads); MySQL_Thread *thr=GloMTH->mysql_threads[w].worker; if (resume_mysql_sessions->len) { @@ -3424,13 +3448,14 @@ void MySQL_Thread::run() { } } else { #endif // IDLE_THREADS + ProcessAllMyDS_AfterPoll(); // iterate through all sessions and process the session logic process_all_sessions(); - return_local_connections(); #ifdef IDLE_THREADS } #endif // IDLE_THREADS + } } // end of ::run() @@ -3984,6 +4009,30 @@ void MySQL_Thread::ProcessAllSessions_MaintenanceLoop(MySQL_Session *sess, unsig } +void MySQL_Thread::ProcessAllSessions_Healthy0(MySQL_Session *sess, unsigned int& n) { + char _buf[1024]; + if (sess->client_myds) { + if (mysql_thread___log_unhealthy_connections) { + if (sess->session_fast_forward == false) { + proxy_warning( + "Closing unhealthy client connection %s:%d\n", sess->client_myds->addr.addr, + sess->client_myds->addr.port + ); + } else { + proxy_warning( + "Closing 'fast_forward' client connection %s:%d\n", sess->client_myds->addr.addr, + sess->client_myds->addr.port + ); + } + } + } + sprintf(_buf,"%s:%d:%s()", __FILE__, __LINE__, __func__); + GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_CLOSE, sess, NULL, _buf); + unregister_session(n); + n--; + delete sess; +} + /** * @brief Processes all active sessions within the MySQL thread. * @@ -4073,28 +4122,8 @@ void MySQL_Thread::process_all_sessions() { // removing this logic in 2.0.15 //sess->active_transactions = -1; } - if (sess->healthy==0) { - char _buf[1024]; - if (sess->client_myds) { - if (mysql_thread___log_unhealthy_connections) { - if (sess->session_fast_forward == false) { - proxy_warning( - "Closing unhealthy client connection %s:%d\n", sess->client_myds->addr.addr, - sess->client_myds->addr.port - ); - } else { - proxy_warning( - "Closing 'fast_forward' client connection %s:%d\n", sess->client_myds->addr.addr, - sess->client_myds->addr.port - ); - } - } - } - sprintf(_buf,"%s:%d:%s()", __FILE__, __LINE__, __func__); - GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_CLOSE, sess, NULL, _buf); - unregister_session(n); - n--; - delete sess; + if (unlikely(sess->healthy==0)) { + ProcessAllSessions_Healthy0(sess, n); } else { if (sess->to_process==1) { if (sess->pause_until <= curtime) { @@ -4112,7 +4141,7 @@ void MySQL_Thread::process_all_sessions() { } } } else { - if (sess->killed==true) { + if (unlikely(sess->killed==true)) { // this is a special cause, if killed the session needs to be executed no matter if paused sess->handler(); char _buf[1024]; From fcc800e39005b394656970665a7ce0990e403f89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Tue, 30 Apr 2024 11:41:14 +0000 Subject: [PATCH 42/79] Documentation on MySQL_Connection::async_query() --- lib/mysql_connection.cpp | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/lib/mysql_connection.cpp b/lib/mysql_connection.cpp index 252d0067d6..a963093727 100644 --- a/lib/mysql_connection.cpp +++ b/lib/mysql_connection.cpp @@ -2074,24 +2074,48 @@ bool MySQL_Connection::IsServerOffline() { return ret; } -// Returns: -// 0 when the query is completed -// 1 when the query is not completed -// the calling function should check mysql error in mysql struct +/** + * @brief Asynchronously execute a query on the MySQL connection. + * + * This function asynchronously executes a query on the MySQL connection. + * It handles various states of the asynchronous query execution process + * and returns appropriate status codes indicating the result of the execution. + * + * @param event The event associated with the query execution. + * @param stmt The query statement to be executed. + * @param length The length of the query statement. + * @param _stmt Pointer to the MySQL statement handle. + * @param stmt_meta Metadata associated with the statement execution. + * + * @return Returns an integer status code indicating the result of the query execution: + * - 0: Query execution completed successfully. + * - -1: Query execution failed. + * - 1: Query execution in progress. + * - 2: Processing a multi-statement query, control needs to be transferred to MySQL_Session. + * - 3: In the middle of processing a multi-statement query. + */ int MySQL_Connection::async_query(short event, char *stmt, unsigned long length, MYSQL_STMT **_stmt, stmt_execute_metadata_t *stmt_meta) { + // Trace the entry of the function PROXY_TRACE(); PROXY_TRACE2(); + + // Ensure MySQL connection is valid assert(mysql); assert(ret_mysql); server_status=parent->status; // we copy it here to avoid race condition. The caller will see this + + // Check if server is offline if (IsServerOffline()) return -1; + // Update DSS state if myds is available if (myds) { if (myds->DSS != STATE_MARIADB_QUERY) { myds->DSS = STATE_MARIADB_QUERY; } } + + // Handle different states of async query execution switch (async_state_machine) { case ASYNC_QUERY_END: processing_multi_statement=false; // no matter if we are processing a multi statement or not, we reached the end @@ -2125,6 +2149,8 @@ int MySQL_Connection::async_query(short event, char *stmt, unsigned long length, break; } + // Handle different states after async query execution. + // That means after hander() was executed. if (async_state_machine==ASYNC_QUERY_END) { PROXY_TRACE2(); compute_unknown_transaction_status(); From df0e36e854b5b6996be4c406f3a570409e3306d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Tue, 30 Apr 2024 21:03:51 +0700 Subject: [PATCH 43/79] Create MySQL_Connection.md --- doc/internal/MySQL_Connection.md | 70 ++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 doc/internal/MySQL_Connection.md diff --git a/doc/internal/MySQL_Connection.md b/doc/internal/MySQL_Connection.md new file mode 100644 index 0000000000..50377d3c23 --- /dev/null +++ b/doc/internal/MySQL_Connection.md @@ -0,0 +1,70 @@ +### Flowchart of `MySQL_Connection::async_query()` + +This function asynchronously executes a query on the MySQL connection. +It handles various states of the asynchronous query execution process and returns appropriate status codes indicating the result of the execution. + +Returns an integer status code indicating the result of the query execution: +- 0: Query execution completed successfully. +- -1: Query execution failed. +- 1: Query execution in progress. +- 2: Processing a multi-statement query, control needs to be transferred to MySQL_Session. +- 3: In the middle of processing a multi-statement query. + + +```mermaid +--- +title: MySQL_Connection::async_query() +--- +flowchart TD +Assert["assert()"] +ValidConnection{Valid Connection} +ValidConnection -- no --> Assert +IsServerOffline{"IsServerOffline()"} +ValidConnection -- yes --> IsServerOffline +IsServerOffline -- yes --> ReturnMinus1 +asyncStateMachine1{async_state_machine} +asyncStateMachine2{async_state_machine} +IsServerOffline -- no --> asyncStateMachine1 +asyncStateMachine1 -- ASYNC_QUERY_END --> Return0 +handler["handler()"] +asyncStateMachine1 --> handler +handler --> asyncStateMachine2 +asyncStateMachine2 -- ASYNC_QUERY_END --> mysql_error{"mysql_error"} +asyncStateMachine2 -- ASYNC_STMT_EXECUTE_END --> mysql_error +asyncStateMachine2 -- ASYNC_STMT_PREPARE_FAILED --> ReturnMinus1 +asyncStateMachine2 -- ASYNC_STMT_PREPARE_SUCCESSFUL --> Return0 +mysql_error -- yes --> ReturnMinus1 +mysql_error -- no --> Return0 +asyncStateMachine2 -- ASYNC_NEXT_RESULT_START --> Return2 +processing_multi_statement{"processing_multi_statement"} +asyncStateMachine2 --> processing_multi_statement +processing_multi_statement -- yes --> Return3 +processing_multi_statement -- no --> Return1 +ReturnMinus1["return -1"] +Return0["return 0"] +Return1["return 1"] +Return2["return 2"] +Return3["return 3"] +``` + +### Flowchart of `MySQL_Connection::IsServerOffline()` + +```mermaid +--- +title: MySQL_Connection::IsServerOffline() +--- +flowchart TD +True[true] +False[false] +SS1{"server_status"} +SA{"shunned_automatic"} +SB{"shunned_and_kill_all_connections"} +SS1 -- OFFLINE_HARD --> True +SS1 -- REPLICATION_LAG --> True +SS1 -- SHUNNED --> SA +SA -- yes --> SB +SB -- yes --> True +SA -- no --> False +SB -- no --> False +SS1 --> False +``` From 6c7c0cc2b583ccfff64968d281a2e6fa5b7f5b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Tue, 30 Apr 2024 21:48:48 +0700 Subject: [PATCH 44/79] Create MySQL_Session.md --- doc/internal/MySQL_Session.md | 68 +++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 doc/internal/MySQL_Session.md diff --git a/doc/internal/MySQL_Session.md b/doc/internal/MySQL_Session.md new file mode 100644 index 0000000000..e56fc6c293 --- /dev/null +++ b/doc/internal/MySQL_Session.md @@ -0,0 +1,68 @@ +### Flowchart of `MySQL_Session::RunQuery()` + +This function mostly calls `MySQL_Connection::async_query()` with the right arguments. +Returns an integer status code indicating the result of the query execution: +- 0: Query execution completed successfully. +- -1: Query execution failed. +- 1: Query execution in progress. +- 2: Processing a multi-statement query, control needs to be transferred to MySQL_Session. +- 3: In the middle of processing a multi-statement query. + +```mermaid +--- +title: MySQL_Session::RunQuery() +--- +flowchart TD +RQ["MySQL_Connection::async_query()"] +BEGIN --> RQ +RQ --> END +``` + +### Flowchart of `MySQL_Session::handler()` + +WORK IN PROGRESS + +```mermaid +--- +title: MySQL_Session::handler() +--- +flowchart TD +RQ["rc = RunQuery()"] +RC{rc} +CBCS["rc1 = handler_ProcessingQueryError_CheckBackendConnectionStatus()"] +RC1{rc1} +RQ --> RC +RC -- 0 --> OK +RC -- -1 --> CBCS +CBCS --> RC1 +CS["CONNECTING_SERVER"] +ReturnMinus1["return -1"] +RC1 -- -1 --> ReturnMinus1 +RC1 -- 1 --> CS +HM1CLE1["handler_minus1_ClientLibraryError()"] +HM1CLE2["handler_minus1_ClientLibraryError()"] +myerr1{"myerr >= 2000 +&& +myerr < 3000"} +RC1 --> myerr1 +myerr1 -- yes --> HM1CLE1 +HM1CLE1 -- true --> CS +HM1CLE1 -- false --> ReturnMinus1 +HM1LEDQ1["handler_minus1_LogErrorDuringQuery()"] +myerr1 -- no --> HM1LEDQ1 +HM1HEC1["handler_minus1_HandleErrorCodes()"] +HM1LEDQ1 --> HM1HEC1 +HM1HEC1 -- true --> HR1{"handler_ret"} +HR1 -- 0 --> CS +HR1 --> RHR1["return handler_ret"] +HM1GEM1["handler_minus1_GenerateErrorMessage()"] +HM1HEC1 -- false --> HM1GEM1 +RE["RequestEnd()"] +HM1HBC1["handler_minus1_HandleBackendConnection()"] +HM1GEM1 --> RE +RE --> HM1HBC1 +``` + + +### Flowchart of `MySQL_Session::handler_ProcessingQueryError_CheckBackendConnectionStatus()` +TODO From 3cd29019825b05dd823ed21740dfbaf06ec96538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Tue, 30 Apr 2024 14:52:51 +0000 Subject: [PATCH 45/79] Few inline documentation --- lib/MySQL_Session.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 7463536988..e47fd386e4 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -4992,6 +4992,24 @@ void MySQL_Session::handler_minus1_HandleBackendConnection(MySQL_Data_Stream *my } // this function was inline +/** + * @brief Run a MySQL query on the current session. + * + * This function executes a MySQL query on the current session based on its status. + * The query execution is asynchronous and handled by the specified MySQL connection. + * It returns the result code of the asynchronous query execution. + * + * @param myds Pointer to the MySQL data stream associated with the session. + * @param myconn Pointer to the MySQL connection used to execute the query. + * + * @return Returns an integer status code indicating the result of the query execution, + * as returned by async_query(): + * - 0: Query execution completed successfully. + * - -1: Query execution failed. + * - 1: Query execution in progress. + * - 2: Processing a multi-statement query, control needs to be transferred to MySQL_Session. + * - 3: In the middle of processing a multi-statement query. + */ int MySQL_Session::RunQuery(MySQL_Data_Stream *myds, MySQL_Connection *myconn) { PROXY_TRACE2(); int rc = 0; From 321774fa8338abf0d9ca87be075c603b7935d3cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Tue, 30 Apr 2024 17:05:45 +0000 Subject: [PATCH 46/79] Comments and refactoring of handler_ProcessingQueryError_CheckBackendConnectionStatus() --- lib/MySQL_Session.cpp | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index e47fd386e4..648f31a03a 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -4609,20 +4609,32 @@ int MySQL_Session::get_pkts_from_client(bool& wrong_pass, PtrSize_t& pkt) { // 0 : no action // -1 : the calling function will return // 1 : call to NEXT_IMMEDIATE + + +/** + * @brief Handler for processing query errors and checking backend connection status. + * + * This function handles query errors and checks the status of the backend connection. + * It evaluates the server status and takes appropriate actions based on specific conditions. + * + * @param myds Pointer to the MySQL data stream associated with the session. + * + * @return Returns an integer status code indicating the result of the error handling: + * - 0: No action required, query processing can continue. + * - 1: Retry required due to backend connection status, query processing needs to be retried. + * - -1: Error encountered, query processing cannot continue. + */ + int MySQL_Session::handler_ProcessingQueryError_CheckBackendConnectionStatus(MySQL_Data_Stream *myds) { MySQL_Connection *myconn = myds->myconn; // the query failed - if ( - // due to #774 , we now read myconn->server_status instead of myconn->parent->status - (myconn->server_status==MYSQL_SERVER_STATUS_OFFLINE_HARD) // the query failed because the server is offline hard - || - (myconn->server_status==MYSQL_SERVER_STATUS_SHUNNED && myconn->parent->shunned_automatic==true && myconn->parent->shunned_and_kill_all_connections==true) // the query failed because the server is shunned due to a serious failure - || - (myconn->server_status==MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG) // slave is lagging! see #774 - ) { + if (myconn->IsServerOffline()) { + // Set maximum connect time if connect timeout is configured if (mysql_thread___connect_timeout_server_max) { myds->max_connect_time=thread->curtime+mysql_thread___connect_timeout_server_max*1000; } + + // Variables to track retry and error conditions bool retry_conn=false; if (myconn->server_status==MYSQL_SERVER_STATUS_SHUNNED_REPLICATION_LAG) { thread->status_variables.stvar[st_var_backend_lagging_during_query]++; @@ -4633,6 +4645,8 @@ int MySQL_Session::handler_ProcessingQueryError_CheckBackendConnectionStatus(MyS proxy_error("Detected an offline server during query: %s, %d, session_id:%u\n", myconn->parent->address, myconn->parent->port, this->thread_session_id); MyHGM->p_update_mysql_error_counter(p_mysql_error_type::proxysql, myconn->parent->myhgc->hid, myconn->parent->address, myconn->parent->port, ER_PROXYSQL_OFFLINE_SRV); } + + // Retry the query if retries are allowed and conditions permit if (myds->query_retries_on_failure > 0) { myds->query_retries_on_failure--; if ((myds->myconn->reusable==true) && myds->myconn->IsActiveTransaction()==false && myds->myconn->MultiplexDisabled()==false) { @@ -4644,6 +4658,8 @@ int MySQL_Session::handler_ProcessingQueryError_CheckBackendConnectionStatus(MyS } } } + + // Destroy MySQL connection from pool and reset file descriptor myds->destroy_MySQL_Connection_From_Pool(false); myds->fd=0; if (retry_conn) { From 1e0f821a4eb65c5c11f1f00e2e94478252153f81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Fri, 3 May 2024 08:52:07 +0200 Subject: [PATCH 47/79] Fix clients reconnect with SSL by disabling SSL tickets SSL sessions resume/tickets aren't supported right now, disabling them shouldn't have negative effects. On the other hand, enabling them can lead to invalid SSL handshakes when the client tries to reuse a previously issued session ticket. Some clients, e.g: MySQL > 8.0.29, attempt session reuses during reconnect operations. --- src/proxy_tls.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/proxy_tls.cpp b/src/proxy_tls.cpp index d43f25f8c1..433bb173fc 100644 --- a/src/proxy_tls.cpp +++ b/src/proxy_tls.cpp @@ -417,8 +417,15 @@ int ProxySQL_create_or_load_TLS(bool bootstrap, std::string& msg) { // verifications purposes. if (!SSL_CTX_load_verify_locations(GloVars.global.ssl_ctx, ssl_ca_fp, ssl_ca_fp)) { proxy_error("Unable to load CA certificates location for verification. Shutting down\n"); - exit(EXIT_SUCCESS); // we exit gracefully to not be restarted } + + // Completely disable session tickets and session-cache. SSL sessions resume/tickets aren't supported + // right now, so disabling them shouldn't have negative effects. On the other hand, enabling them can + // lead to invalid SSL handshakes when the client tries to reuse a previously issued session ticket. + // In this scenario an invalid handshake will take place, and the client will be disconnected. Some + // clients (MySQL > 8.0.29) attempt session reuses during reconnect operations. + SSL_CTX_set_options(GloVars.global.ssl_ctx, SSL_OP_NO_TICKET); + SSL_CTX_set_session_cache_mode(GloVars.global.ssl_ctx, SSL_SESS_CACHE_OFF); } else { // here we use global.tmp_ssl_ctx instead of global.ssl_ctx // because we will try to swap at the end @@ -478,6 +485,9 @@ int ProxySQL_create_or_load_TLS(bool bootstrap, std::string& msg) { } if (ret == 0) { SSL_CTX_set_verify(GloVars.global.ssl_ctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE, callback_ssl_verify_peer); + // Completely disable session tickets and session-cache. See comment above. + SSL_CTX_set_options(GloVars.global.ssl_ctx, SSL_OP_NO_TICKET); + SSL_CTX_set_session_cache_mode(GloVars.global.ssl_ctx, SSL_SESS_CACHE_OFF); } X509_free(x509); EVP_PKEY_free(pkey); From 2b0ca2b24dfb5bf3a2a93dea57416024566e5dc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Fri, 3 May 2024 09:02:35 +0200 Subject: [PATCH 48/79] Add new MySQL8 client dependency for TAP tests --- test/deps/Makefile | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/test/deps/Makefile b/test/deps/Makefile index 0570da1f43..90c2bcb348 100644 --- a/test/deps/Makefile +++ b/test/deps/Makefile @@ -10,7 +10,7 @@ DEPS_PATH := $(PROXYSQL_PATH)/deps default: all .PHONY: all -all: mariadb_client mysql_client +all: mariadb_client mysql_client mysql8_client ### test deps targets @@ -25,15 +25,6 @@ mariadb-connector-c/mariadb-connector-c/libmariadb/libmariadbclient.a: mariadb_client: mariadb-connector-c/mariadb-connector-c/libmariadb/libmariadbclient.a - -#mysql-connector-c/mysql-connector-c/libmysql/libmysqlclient.a: -# cd mysql-connector-c && rm -rf mysql-connector-c-*-src/ || true -# cd mysql-connector-c && tar -zxf mysql-connector-c-*-src.tar.gz -# cd mysql-connector-c/mysql-connector-c && patch -p0 < ../CMakeLists.txt.patch -# cd mysql-connector-c/mysql-connector-c && patch -p0 < ../install_macros.cmake.patch -# cd mysql-connector-c/mysql-connector-c && cmake . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DOPENSSL_ROOT_DIR=$(DEPS_PATH)/libssl/openssl -# cd mysql-connector-c/mysql-connector-c && CC=${CC} CXX=${CXX} ${MAKE} mysqlclient mysql - mysql-connector-c/mysql-boost-5.7.44.tar.gz: cd mysql-connector-c && curl -C - -O -s https://cdn.mysql.com//Downloads/MySQL-5.7/mysql-boost-5.7.44.tar.gz || wget -nc -q https://cdn.mysql.com//Downloads/MySQL-5.7/mysql-boost-5.7.44.tar.gz @@ -42,12 +33,25 @@ mysql-connector-c/mysql-connector-c/libmysql/libmysqlclient.a: mysql-connector-c cd mysql-connector-c && tar -zxf mysql-boost-5.7.*.tar.gz cd mysql-connector-c && ln -fsT $$(ls -1d mysql-5.7.*/) mysql-connector-c cd mysql-connector-c/mysql-connector-c && cmake . -DWITH_BOOST=./boost -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_CXX_FLAGS_RELWITHDEBINFO="-O0 -ggdb -DNDEBUG -fPIC" -DOPENSSL_ROOT_DIR=$(DEPS_PATH)/libssl/openssl -# cd mysql-connector-c/mysql-connector-c && cmake . -DWITH_BOOST=./boost -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS_DEBUG="-O0 -ggdb -fPIC" -DOPENSSL_ROOT_DIR=$(DEPS_PATH)/libssl/openssl cd mysql-connector-c/mysql-connector-c && CC=${CC} CXX=${CXX} ${MAKE} mysqlclient mysql cd mysql-connector-c/mysql-connector-c && cp archive_output_directory/libmysqlclient.a libmysql/ mysql_client: mysql-connector-c/mysql-connector-c/libmysql/libmysqlclient.a +mysql-connector-c-8.4.0/mysql-8.4.0.tar.gz: + cd mysql-connector-c-8.4.0 && curl -C - -O -s https://cdn.mysql.com//Downloads/MySQL-8.4/mysql-8.4.0.tar.gz || wget -nc -q https://cdn.mysql.com//Downloads/MySQL-8.4/mysql-8.4.0.tar.gz + +mysql-connector-c-8.4.0/mysql-connector-c/libmysql/libmysqlclient.a: mysql-connector-c-8.4.0/mysql-8.4.0.tar.gz + cd mysql-connector-c-8.4.0 && rm -rf mysql-*/ || true + cd mysql-connector-c-8.4.0 && tar -zxf mysql-*.tar.gz + cd mysql-connector-c-8.4.0 && ln -fsT $$(ls -1d mysql-8.4.*/) mysql-connector-c + cd mysql-connector-c-8.4.0/mysql-connector-c && cmake . -DFORCE_INSOURCE_BUILD=1 -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DWITHOUT_SERVER=ON -DDOWNLOAD_BOOST=1 -DWITH_BOOST=./mysql-server/downloads/ -DWITH_UNIT_TESTS=OFF \ + -DCMAKE_CXX_FLAGS_RELWITHDEBINFO="-O0 -ggdb -DNDEBUG -fPIC" -DOPENSSL_ROOT_DIR=$(DEPS_PATH)/libssl/openssl + cd mysql-connector-c-8.4.0/mysql-connector-c && CC=${CC} CXX=${CXX} ${MAKE} + cd mysql-connector-c-8.4.0/mysql-connector-c && cp archive_output_directory/libmysqlclient.a libmysql/ + +mysql8_client: mysql-connector-c-8.4.0/mysql-connector-c/libmysql/libmysqlclient.a ### clean targets @@ -56,6 +60,7 @@ mysql_client: mysql-connector-c/mysql-connector-c/libmysql/libmysqlclient.a cleanall: cd mariadb-connector-c && rm -rf mariadb-connector-c-*/ || true cd mysql-connector-c && rm -rf mysql-5.7.*/ || true + cd mysql-connector-c-8.4.0 && rm -rf mysql-8.4.*/ || true .PHONY: clean .SILENT: clean @@ -65,3 +70,6 @@ clean: cd mysql-connector-c/mysql-connector-c && $(MAKE) --no-print-directory clean || true cd mysql-connector-c/mysql-connector-c && rm -f CMakeCache.txt || true cd mysql-connector-c/mysql-connector-c && rm -f libmysql/libmysqlclient.a || true + cd mysql-connector-c-8.4.0/mysql-connector-c && $(MAKE) --no-print-directory clean || true + cd mysql-connector-c-8.4.0/mysql-connector-c && rm -f CMakeCache.txt || true + cd mysql-connector-c-8.4.0/mysql-connector-c && rm -f libmysql/libmysqlclient.a || true From 5215172da1dd3a67d1f21f937a2527a2a169e7c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Fri, 3 May 2024 09:09:00 +0200 Subject: [PATCH 49/79] Allow uninitialized members in 'mysql_*' warnings overrides --- test/tap/tap/utils.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/test/tap/tap/utils.cpp b/test/tap/tap/utils.cpp index 60fda98e4f..54b822a16b 100644 --- a/test/tap/tap/utils.cpp +++ b/test/tap/tap/utils.cpp @@ -38,9 +38,10 @@ using nlohmann::json; #define STMT_VECTOR_PTR(mysql) (static_cast*>(mysql->unused_3)) #define STMT_EXECUTED_VECTOR_PTR(mysql) (static_cast>*>(mysql->unused_4)) -#define STMT_FIND_INDEX(stmt,idx) const std::vector& vec_stmt = STMT_VECTOR(stmt); \ - for (size_t i = 0; i < vec_stmt.size(); i++) {\ - if (vec_stmt[i] == stmt) {\ +#define STMT_FIND_INDEX(stmt,idx) const std::vector* vec_stmt = STMT_VECTOR_PTR(stmt->mysql);\ + size_t vec_size = vec_stmt ? vec_stmt->size() : 0;\ + for (size_t i = 0; i < vec_size; i++) {\ + if ((*vec_stmt)[i] == stmt) {\ idx = i; \ break; \ }\ @@ -88,7 +89,9 @@ MYSQL* mysql_init_override(MYSQL* mysql, const char* file, int line) { int mysql_query_override(MYSQL* mysql, const char* query, const char* file, int line) { const int result = (*real_mysql_query)(mysql, query); if (result == 0) { - LAST_QUERY_EXECUTED_STR(mysql) = query; + if (LAST_QUERY_EXECUTED_PTR(mysql)) { + LAST_QUERY_EXECUTED_STR(mysql) = query; + } if (mysql_errno(mysql) == 0 && mysql_field_count(mysql) == 0 && mysql_warning_count(mysql) > 0) { fprintf(stdout, "File %s, Line %d, [mysql_query] A warning was generated during the execution of the query:'%s', warning count:%d\n", file, line, query, mysql_warning_count(mysql)); @@ -99,7 +102,7 @@ int mysql_query_override(MYSQL* mysql, const char* query, const char* file, int MYSQL_RES* mysql_store_result_override(MYSQL* mysql, const char* file, int line) { MYSQL_RES* result = (*real_mysql_store_result)(mysql); - if (mysql_errno(mysql) == 0 && mysql_warning_count(mysql) > 0) { + if (mysql_errno(mysql) == 0 && mysql_warning_count(mysql) > 0 && LAST_QUERY_EXECUTED_PTR(mysql)) { fprintf(stdout, "File %s, Line %d, [mysql_store_result] A warning was generated during the execution of the query:'%s', warning count:%d\n", file, line, LAST_QUERY_EXECUTED_STR(mysql).c_str(), mysql_warning_count(mysql)); } @@ -107,7 +110,9 @@ MYSQL_RES* mysql_store_result_override(MYSQL* mysql, const char* file, int line) } void mysql_close_override(MYSQL* mysql, const char* file, int line) { - delete LAST_QUERY_EXECUTED_PTR(mysql); + if (LAST_QUERY_EXECUTED_PTR(mysql)) { + delete LAST_QUERY_EXECUTED_PTR(mysql); + } if (STMT_VECTOR_PTR(mysql)) { delete STMT_VECTOR_PTR(mysql); delete STMT_EXECUTED_VECTOR_PTR(mysql); From 8865f5e6639e8fdc895f98338684af910bebd6b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Fri, 3 May 2024 09:11:02 +0200 Subject: [PATCH 50/79] Add new 'cleanall' target to TAP makefile --- test/tap/Makefile | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/tap/Makefile b/test/tap/Makefile index ee93f10b31..52f6693fca 100644 --- a/test/tap/Makefile +++ b/test/tap/Makefile @@ -28,6 +28,13 @@ tests_with_deps: tap test_deps .PHONY: clean .SILENT: clean clean: + cd tap && ${MAKE} -s clean + cd tests && ${MAKE} -s clean + cd tests_with_deps && ${MAKE} -s clean + +.PHONY: cleanall +.SILENT: cleanall +cleanall: cd ../deps && ${MAKE} -s clean cd tap && ${MAKE} -s clean cd tests && ${MAKE} -s clean From af1ba7d3ffc0f5f43c498abfe4061903fe82ebb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Fri, 3 May 2024 09:11:59 +0200 Subject: [PATCH 51/79] Fix 'tap' target with 'test_deps' for TAP makefile --- test/tap/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tap/Makefile b/test/tap/Makefile index 52f6693fca..6f8c64cb42 100644 --- a/test/tap/Makefile +++ b/test/tap/Makefile @@ -13,7 +13,7 @@ test_deps: cd ../deps && CC=${CC} CXX=${CXX} ${MAKE} .PHONY: tap -tap: +tap: test_deps cd tap && CC=${CC} CXX=${CXX} ${MAKE} .PHONY: tests From 9283cd2e9bbc66d2871eac9d34646a6a0499c89d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Mon, 6 May 2024 06:39:08 +0000 Subject: [PATCH 52/79] Code cleanup in MySQL_Thread::refresh_variables() Use macros to avoid repeating variable name and strings with the variable name. Because this commit avoids duplications of name and literals, this commit also renames: mysql_thread___monitor_groupreplication_max_transaction_behind_for_read_only to: mysql_thread___monitor_groupreplication_max_transactions_behind_for_read_only --- include/proxysql_structs.h | 4 +- lib/MySQL_HostGroups_Manager.cpp | 8 +- lib/MySQL_Thread.cpp | 385 +++++++++++++++++-------------- 3 files changed, 220 insertions(+), 177 deletions(-) diff --git a/include/proxysql_structs.h b/include/proxysql_structs.h index 28f98ac8c5..f3d322305c 100644 --- a/include/proxysql_structs.h +++ b/include/proxysql_structs.h @@ -916,7 +916,7 @@ __thread int mysql_thread___monitor_groupreplication_healthcheck_interval; __thread int mysql_thread___monitor_groupreplication_healthcheck_timeout; __thread int mysql_thread___monitor_groupreplication_healthcheck_max_timeout_count; __thread int mysql_thread___monitor_groupreplication_max_transactions_behind_count; -__thread int mysql_thread___monitor_groupreplication_max_transaction_behind_for_read_only; +__thread int mysql_thread___monitor_groupreplication_max_transactions_behind_for_read_only; __thread int mysql_thread___monitor_galera_healthcheck_interval; __thread int mysql_thread___monitor_galera_healthcheck_timeout; __thread int mysql_thread___monitor_galera_healthcheck_max_timeout_count; @@ -1087,7 +1087,7 @@ extern __thread int mysql_thread___monitor_replication_lag_count; extern __thread int mysql_thread___monitor_groupreplication_healthcheck_interval; extern __thread int mysql_thread___monitor_groupreplication_healthcheck_timeout; extern __thread int mysql_thread___monitor_groupreplication_healthcheck_max_timeout_count; -extern __thread int mysql_thread___monitor_groupreplication_max_transaction_behind_for_read_only; +extern __thread int mysql_thread___monitor_groupreplication_max_transactions_behind_for_read_only; extern __thread int mysql_thread___monitor_groupreplication_max_transactions_behind_count; extern __thread int mysql_thread___monitor_galera_healthcheck_interval; extern __thread int mysql_thread___monitor_galera_healthcheck_timeout; diff --git a/lib/MySQL_HostGroups_Manager.cpp b/lib/MySQL_HostGroups_Manager.cpp index 705a06515a..1483b37c12 100644 --- a/lib/MySQL_HostGroups_Manager.cpp +++ b/lib/MySQL_HostGroups_Manager.cpp @@ -2866,8 +2866,8 @@ void MySQL_HostGroups_Manager::group_replication_lag_action( MyHGC* myhgc = nullptr; if ( - mysql_thread___monitor_groupreplication_max_transaction_behind_for_read_only == 0 || - mysql_thread___monitor_groupreplication_max_transaction_behind_for_read_only == 2 || + mysql_thread___monitor_groupreplication_max_transactions_behind_for_read_only == 0 || + mysql_thread___monitor_groupreplication_max_transactions_behind_for_read_only == 2 || enable ) { if (read_only == false) { @@ -2877,8 +2877,8 @@ void MySQL_HostGroups_Manager::group_replication_lag_action( } if ( - mysql_thread___monitor_groupreplication_max_transaction_behind_for_read_only == 1 || - mysql_thread___monitor_groupreplication_max_transaction_behind_for_read_only == 2 || + mysql_thread___monitor_groupreplication_max_transactions_behind_for_read_only == 1 || + mysql_thread___monitor_groupreplication_max_transactions_behind_for_read_only == 2 || enable ) { myhgc = MyHGM->MyHGC_find(reader_hostgroup); diff --git a/lib/MySQL_Thread.cpp b/lib/MySQL_Thread.cpp index 72dedf1475..5f33f0c729 100644 --- a/lib/MySQL_Thread.cpp +++ b/lib/MySQL_Thread.cpp @@ -38,6 +38,70 @@ MySQL_Session *sess_stopat; #define PROXYSQL_LISTEN_LEN 1024 #define MIN_THREADS_FOR_MAINTENANCE 8 +/** + * @brief Helper macro to stringify a macro argument. + * + * This macro takes a single argument 'x' and converts it into a string literal. + * It is a helper macro used by the STRINGIFY macro. + * + * @param x The macro argument to be converted into a string. + * @return The string representation of the macro argument. + */ +#define STRINGIFY_HELPER(x) #x + +/** + * @brief Macro to stringify a macro argument. + * + * This macro takes a single argument 'x' and converts it into a string literal + * using the STRINGIFY_HELPER macro. + * + * @param x The macro argument to be converted into a string. + * @return The string representation of the macro argument. + */ +#define STRINGIFY(x) STRINGIFY_HELPER(x) + +/** + * @brief Refreshes a boolean variable from the MySQL thread. + * + * This macro updates the value of a boolean variable named 'name' from + * the MySQL thread. It retrieves the value using the 'get_variable_int' function + * from the 'GloMTH' object and assigns it to the variable 'mysql_thread___name'. + * The retrieved integer value is cast to a boolean before assignment. + * + * @param name The name of the boolean variable to be refreshed. + */ +#define REFRESH_VARIABLE_BOOL(name) \ + mysql_thread___ ## name = (bool)GloMTH->get_variable_int((char *)STRINGIFY(name)) + +/** + * @brief Refreshes an integer variable from the MySQL thread. + * + * This macro updates the value of an integer variable named 'name' from + * the MySQL thread. It retrieves the value using the 'get_variable_int' function + * from the 'GloMTH' object and assigns it to the variable 'mysql_thread___name'. + * + * @param name The name of the integer variable to be refreshed. + */ +#define REFRESH_VARIABLE_INT(name) \ + mysql_thread___ ## name = GloMTH->get_variable_int((char *)STRINGIFY(name)) + +/** + * @brief Refreshes a character variable from the MySQL thread. + * + * This macro updates the value of a character variable named 'name' from + * the MySQL thread. It retrieves the value using the 'get_variable_string' function + * from the 'GloMTH' object and assigns it to the variable 'mysql_thread___name'. + * If the variable 'mysql_thread___name' was previously allocated memory, + * it frees that memory before assigning the new value. + * + * @param name The name of the character variable to be refreshed. + */ +#define REFRESH_VARIABLE_CHAR(name) \ + do { \ + if (mysql_thread___ ## name) free(mysql_thread___ ## name); \ + mysql_thread___ ## name = GloMTH->get_variable_string((char *)STRINGIFY(name)); \ + } while (0) + extern Query_Processor *GloQPro; extern MySQL_Authentication *GloMyAuth; extern MySQL_Threads_Handler *GloMTH; @@ -4184,126 +4248,111 @@ void MySQL_Thread::refresh_variables() { } GloMTH->wrlock(); __thread_MySQL_Thread_Variables_version=__global_MySQL_Thread_Variables_version; - mysql_thread___max_allowed_packet=GloMTH->get_variable_int((char *)"max_allowed_packet"); - mysql_thread___automatic_detect_sqli=(bool)GloMTH->get_variable_int((char *)"automatic_detect_sqli"); - mysql_thread___firewall_whitelist_enabled=(bool)GloMTH->get_variable_int((char *)"firewall_whitelist_enabled"); - mysql_thread___use_tcp_keepalive=(bool)GloMTH->get_variable_int((char *)"use_tcp_keepalive"); - mysql_thread___tcp_keepalive_time=GloMTH->get_variable_int((char *)"tcp_keepalive_time"); - mysql_thread___throttle_connections_per_sec_to_hostgroup=GloMTH->get_variable_int((char *)"throttle_connections_per_sec_to_hostgroup"); - mysql_thread___max_transaction_idle_time=GloMTH->get_variable_int((char *)"max_transaction_idle_time"); - mysql_thread___max_transaction_time=GloMTH->get_variable_int((char *)"max_transaction_time"); - mysql_thread___threshold_query_length=GloMTH->get_variable_int((char *)"threshold_query_length"); - mysql_thread___threshold_resultset_size=GloMTH->get_variable_int((char *)"threshold_resultset_size"); - mysql_thread___query_digests_max_digest_length=GloMTH->get_variable_int((char *)"query_digests_max_digest_length"); - mysql_thread___query_digests_max_query_length=GloMTH->get_variable_int((char *)"query_digests_max_query_length"); - mysql_thread___wait_timeout=GloMTH->get_variable_int((char *)"wait_timeout"); - mysql_thread___throttle_max_bytes_per_second_to_client=GloMTH->get_variable_int((char *)"throttle_max_bytes_per_second_to_client"); - mysql_thread___throttle_ratio_server_to_client=GloMTH->get_variable_int((char *)"throttle_ratio_server_to_client"); - mysql_thread___max_connections=GloMTH->get_variable_int((char *)"max_connections"); - mysql_thread___max_stmts_per_connection=GloMTH->get_variable_int((char *)"max_stmts_per_connection"); - mysql_thread___max_stmts_cache=GloMTH->get_variable_int((char *)"max_stmts_cache"); - mysql_thread___mirror_max_concurrency=GloMTH->get_variable_int((char *)"mirror_max_concurrency"); - mysql_thread___mirror_max_queue_length=GloMTH->get_variable_int((char *)"mirror_max_queue_length"); - mysql_thread___default_query_delay=GloMTH->get_variable_int((char *)"default_query_delay"); - mysql_thread___default_query_timeout=GloMTH->get_variable_int((char *)"default_query_timeout"); - mysql_thread___query_processor_iterations=GloMTH->get_variable_int((char *)"query_processor_iterations"); - mysql_thread___query_processor_regex=GloMTH->get_variable_int((char *)"query_processor_regex"); - mysql_thread___set_query_lock_on_hostgroup=GloMTH->get_variable_int((char *)"set_query_lock_on_hostgroup"); - mysql_thread___set_parser_algorithm=GloMTH->get_variable_int((char *)"set_parser_algorithm"); - mysql_thread___reset_connection_algorithm=GloMTH->get_variable_int((char *)"reset_connection_algorithm"); - mysql_thread___auto_increment_delay_multiplex=GloMTH->get_variable_int((char *)"auto_increment_delay_multiplex"); - mysql_thread___auto_increment_delay_multiplex_timeout_ms=GloMTH->get_variable_int((char *)"auto_increment_delay_multiplex_timeout_ms"); - mysql_thread___default_max_latency_ms=GloMTH->get_variable_int((char *)"default_max_latency_ms"); - mysql_thread___long_query_time=GloMTH->get_variable_int((char *)"long_query_time"); - mysql_thread___query_cache_size_MB=GloMTH->get_variable_int((char *)"query_cache_size_MB"); - mysql_thread___query_cache_soft_ttl_pct=GloMTH->get_variable_int((char *)"query_cache_soft_ttl_pct"); - mysql_thread___query_cache_handle_warnings =GloMTH->get_variable_int((char*)"query_cache_handle_warnings"); - mysql_thread___ping_interval_server_msec=GloMTH->get_variable_int((char *)"ping_interval_server_msec"); - mysql_thread___ping_timeout_server=GloMTH->get_variable_int((char *)"ping_timeout_server"); - mysql_thread___shun_on_failures=GloMTH->get_variable_int((char *)"shun_on_failures"); - mysql_thread___shun_recovery_time_sec=GloMTH->get_variable_int((char *)"shun_recovery_time_sec"); - mysql_thread___unshun_algorithm=GloMTH->get_variable_int((char *)"unshun_algorithm"); - mysql_thread___query_retries_on_failure=GloMTH->get_variable_int((char *)"query_retries_on_failure"); - mysql_thread___connect_retries_on_failure=GloMTH->get_variable_int((char *)"connect_retries_on_failure"); - mysql_thread___connection_delay_multiplex_ms=GloMTH->get_variable_int((char *)"connection_delay_multiplex_ms"); - mysql_thread___connection_max_age_ms=GloMTH->get_variable_int((char *)"connection_max_age_ms"); - mysql_thread___connect_timeout_client=GloMTH->get_variable_int((char *)"connect_timeout_client"); - mysql_thread___connect_timeout_server=GloMTH->get_variable_int((char *)"connect_timeout_server"); - mysql_thread___connect_timeout_server_max=GloMTH->get_variable_int((char *)"connect_timeout_server_max"); - mysql_thread___free_connections_pct=GloMTH->get_variable_int((char *)"free_connections_pct"); + REFRESH_VARIABLE_INT (max_allowed_packet); + REFRESH_VARIABLE_BOOL(automatic_detect_sqli); + REFRESH_VARIABLE_BOOL(firewall_whitelist_enabled); + REFRESH_VARIABLE_BOOL(use_tcp_keepalive); + REFRESH_VARIABLE_INT(tcp_keepalive_time); + REFRESH_VARIABLE_INT(throttle_connections_per_sec_to_hostgroup); + REFRESH_VARIABLE_INT(max_transaction_idle_time); + REFRESH_VARIABLE_INT(max_transaction_time); + REFRESH_VARIABLE_INT(threshold_query_length); + REFRESH_VARIABLE_INT(threshold_resultset_size); + REFRESH_VARIABLE_INT(query_digests_max_digest_length); + REFRESH_VARIABLE_INT(query_digests_max_query_length); + REFRESH_VARIABLE_INT(wait_timeout); + REFRESH_VARIABLE_INT(throttle_max_bytes_per_second_to_client); + REFRESH_VARIABLE_INT(throttle_ratio_server_to_client); + REFRESH_VARIABLE_INT(max_connections); + REFRESH_VARIABLE_INT(max_stmts_per_connection); + REFRESH_VARIABLE_INT(max_stmts_cache); + REFRESH_VARIABLE_INT(mirror_max_concurrency); + REFRESH_VARIABLE_INT(mirror_max_queue_length); + REFRESH_VARIABLE_INT(default_query_delay); + REFRESH_VARIABLE_INT(default_query_timeout); + REFRESH_VARIABLE_INT(query_processor_iterations); + REFRESH_VARIABLE_INT(query_processor_regex); + REFRESH_VARIABLE_INT(set_query_lock_on_hostgroup); + REFRESH_VARIABLE_INT(set_parser_algorithm); + REFRESH_VARIABLE_INT(reset_connection_algorithm); + REFRESH_VARIABLE_INT(auto_increment_delay_multiplex); + REFRESH_VARIABLE_INT(auto_increment_delay_multiplex_timeout_ms); + REFRESH_VARIABLE_INT(default_max_latency_ms); + REFRESH_VARIABLE_INT(long_query_time); + REFRESH_VARIABLE_INT(query_cache_size_MB); + REFRESH_VARIABLE_INT(query_cache_soft_ttl_pct); + REFRESH_VARIABLE_INT(query_cache_handle_warnings); + REFRESH_VARIABLE_INT(ping_interval_server_msec); + REFRESH_VARIABLE_INT(ping_timeout_server); + REFRESH_VARIABLE_INT(shun_on_failures); + REFRESH_VARIABLE_INT(shun_recovery_time_sec); + REFRESH_VARIABLE_INT(unshun_algorithm); + REFRESH_VARIABLE_INT(query_retries_on_failure); + REFRESH_VARIABLE_INT(connect_retries_on_failure); + REFRESH_VARIABLE_INT(connection_delay_multiplex_ms); + REFRESH_VARIABLE_INT(connection_max_age_ms); + REFRESH_VARIABLE_INT(connect_timeout_client); + REFRESH_VARIABLE_INT(connect_timeout_server); + REFRESH_VARIABLE_INT(connect_timeout_server_max); + REFRESH_VARIABLE_INT(free_connections_pct); #ifdef IDLE_THREADS - mysql_thread___session_idle_ms=GloMTH->get_variable_int((char *)"session_idle_ms"); + REFRESH_VARIABLE_INT(session_idle_ms); #endif // IDLE_THREADS - mysql_thread___connect_retries_delay=GloMTH->get_variable_int((char *)"connect_retries_delay"); + REFRESH_VARIABLE_INT(connect_retries_delay); - if (mysql_thread___monitor_username) free(mysql_thread___monitor_username); - mysql_thread___monitor_username=GloMTH->get_variable_string((char *)"monitor_username"); - if (mysql_thread___monitor_password) free(mysql_thread___monitor_password); - mysql_thread___monitor_password=GloMTH->get_variable_string((char *)"monitor_password"); - if (mysql_thread___monitor_replication_lag_use_percona_heartbeat) free(mysql_thread___monitor_replication_lag_use_percona_heartbeat); - mysql_thread___monitor_replication_lag_use_percona_heartbeat=GloMTH->get_variable_string((char *)"monitor_replication_lag_use_percona_heartbeat"); + REFRESH_VARIABLE_CHAR(monitor_username); + REFRESH_VARIABLE_CHAR(monitor_password); + REFRESH_VARIABLE_CHAR(monitor_replication_lag_use_percona_heartbeat); // SSL proxy to server - if (mysql_thread___ssl_p2s_ca) free(mysql_thread___ssl_p2s_ca); - mysql_thread___ssl_p2s_ca=GloMTH->get_variable_string((char *)"ssl_p2s_ca"); - if (mysql_thread___ssl_p2s_capath) free(mysql_thread___ssl_p2s_capath); - mysql_thread___ssl_p2s_capath=GloMTH->get_variable_string((char *)"ssl_p2s_capath"); - if (mysql_thread___ssl_p2s_cert) free(mysql_thread___ssl_p2s_cert); - mysql_thread___ssl_p2s_cert=GloMTH->get_variable_string((char *)"ssl_p2s_cert"); - if (mysql_thread___ssl_p2s_key) free(mysql_thread___ssl_p2s_key); - mysql_thread___ssl_p2s_key=GloMTH->get_variable_string((char *)"ssl_p2s_key"); - if (mysql_thread___ssl_p2s_cipher) free(mysql_thread___ssl_p2s_cipher); - mysql_thread___ssl_p2s_cipher=GloMTH->get_variable_string((char *)"ssl_p2s_cipher"); - if (mysql_thread___ssl_p2s_crl) free(mysql_thread___ssl_p2s_crl); - mysql_thread___ssl_p2s_crl=GloMTH->get_variable_string((char *)"ssl_p2s_crl"); - if (mysql_thread___ssl_p2s_crlpath) free(mysql_thread___ssl_p2s_crlpath); - mysql_thread___ssl_p2s_crlpath=GloMTH->get_variable_string((char *)"ssl_p2s_crlpath"); - - mysql_thread___monitor_wait_timeout=(bool)GloMTH->get_variable_int((char *)"monitor_wait_timeout"); - mysql_thread___monitor_writer_is_also_reader=(bool)GloMTH->get_variable_int((char *)"monitor_writer_is_also_reader"); - mysql_thread___monitor_enabled=(bool)GloMTH->get_variable_int((char *)"monitor_enabled"); - mysql_thread___monitor_history=GloMTH->get_variable_int((char *)"monitor_history"); - mysql_thread___monitor_connect_interval=GloMTH->get_variable_int((char *)"monitor_connect_interval"); - mysql_thread___monitor_connect_timeout=GloMTH->get_variable_int((char *)"monitor_connect_timeout"); - mysql_thread___monitor_ping_interval=GloMTH->get_variable_int((char *)"monitor_ping_interval"); - mysql_thread___monitor_ping_max_failures=GloMTH->get_variable_int((char *)"monitor_ping_max_failures"); - mysql_thread___monitor_ping_timeout=GloMTH->get_variable_int((char *)"monitor_ping_timeout"); - mysql_thread___monitor_aws_rds_topology_discovery_interval=GloMTH->get_variable_int((char *)"monitor_aws_rds_topology_discovery_interval"); - mysql_thread___monitor_read_only_interval=GloMTH->get_variable_int((char *)"monitor_read_only_interval"); - mysql_thread___monitor_read_only_timeout=GloMTH->get_variable_int((char *)"monitor_read_only_timeout"); - mysql_thread___monitor_read_only_max_timeout_count=GloMTH->get_variable_int((char *)"monitor_read_only_max_timeout_count"); - mysql_thread___monitor_replication_lag_group_by_host=(bool)GloMTH->get_variable_int((char *)"monitor_replication_lag_group_by_host"); - mysql_thread___monitor_replication_lag_interval=GloMTH->get_variable_int((char *)"monitor_replication_lag_interval"); - mysql_thread___monitor_replication_lag_timeout=GloMTH->get_variable_int((char *)"monitor_replication_lag_timeout"); - mysql_thread___monitor_replication_lag_count=GloMTH->get_variable_int((char *)"monitor_replication_lag_count"); - mysql_thread___monitor_groupreplication_healthcheck_interval=GloMTH->get_variable_int((char *)"monitor_groupreplication_healthcheck_interval"); - mysql_thread___monitor_groupreplication_healthcheck_timeout=GloMTH->get_variable_int((char *)"monitor_groupreplication_healthcheck_timeout"); - mysql_thread___monitor_groupreplication_healthcheck_max_timeout_count=GloMTH->get_variable_int((char *)"monitor_groupreplication_healthcheck_max_timeout_count"); - mysql_thread___monitor_groupreplication_max_transactions_behind_count=GloMTH->get_variable_int((char *)"monitor_groupreplication_max_transactions_behind_count"); - mysql_thread___monitor_groupreplication_max_transaction_behind_for_read_only=GloMTH->get_variable_int((char *)"monitor_groupreplication_max_transactions_behind_for_read_only"); - mysql_thread___monitor_galera_healthcheck_interval=GloMTH->get_variable_int((char *)"monitor_galera_healthcheck_interval"); - mysql_thread___monitor_galera_healthcheck_timeout=GloMTH->get_variable_int((char *)"monitor_galera_healthcheck_timeout"); - mysql_thread___monitor_galera_healthcheck_max_timeout_count=GloMTH->get_variable_int((char *)"monitor_galera_healthcheck_max_timeout_count"); - mysql_thread___monitor_query_interval=GloMTH->get_variable_int((char *)"monitor_query_interval"); - mysql_thread___monitor_query_timeout=GloMTH->get_variable_int((char *)"monitor_query_timeout"); - mysql_thread___monitor_slave_lag_when_null=GloMTH->get_variable_int((char *)"monitor_slave_lag_when_null"); - mysql_thread___monitor_threads_min = GloMTH->get_variable_int((char *)"monitor_threads_min"); - mysql_thread___monitor_threads_max = GloMTH->get_variable_int((char *)"monitor_threads_max"); - mysql_thread___monitor_threads_queue_maxsize = GloMTH->get_variable_int((char *)"monitor_threads_queue_maxsize"); - mysql_thread___monitor_local_dns_cache_ttl = GloMTH->get_variable_int((char*)"monitor_local_dns_cache_ttl"); - mysql_thread___monitor_local_dns_cache_refresh_interval = GloMTH->get_variable_int((char*)"monitor_local_dns_cache_refresh_interval"); - mysql_thread___monitor_local_dns_resolver_queue_maxsize = GloMTH->get_variable_int((char*)"monitor_local_dns_resolver_queue_maxsize"); - - if (mysql_thread___firewall_whitelist_errormsg) free(mysql_thread___firewall_whitelist_errormsg); - mysql_thread___firewall_whitelist_errormsg=GloMTH->get_variable_string((char *)"firewall_whitelist_errormsg"); - if (mysql_thread___init_connect) free(mysql_thread___init_connect); - mysql_thread___init_connect=GloMTH->get_variable_string((char *)"init_connect"); - if (mysql_thread___ldap_user_variable) free(mysql_thread___ldap_user_variable); - mysql_thread___ldap_user_variable=GloMTH->get_variable_string((char *)"ldap_user_variable"); - if (mysql_thread___add_ldap_user_comment) free(mysql_thread___add_ldap_user_comment); - mysql_thread___add_ldap_user_comment=GloMTH->get_variable_string((char *)"add_ldap_user_comment"); - if (mysql_thread___default_session_track_gtids) free(mysql_thread___default_session_track_gtids); - mysql_thread___default_session_track_gtids=GloMTH->get_variable_string((char *)"default_session_track_gtids"); + REFRESH_VARIABLE_CHAR(ssl_p2s_ca); + REFRESH_VARIABLE_CHAR(ssl_p2s_capath); + REFRESH_VARIABLE_CHAR(ssl_p2s_cert); + REFRESH_VARIABLE_CHAR(ssl_p2s_key); + REFRESH_VARIABLE_CHAR(ssl_p2s_cipher); + REFRESH_VARIABLE_CHAR(ssl_p2s_crl); + REFRESH_VARIABLE_CHAR(ssl_p2s_crlpath); + + REFRESH_VARIABLE_BOOL(monitor_wait_timeout); + REFRESH_VARIABLE_BOOL(monitor_writer_is_also_reader); + REFRESH_VARIABLE_BOOL(monitor_enabled); + REFRESH_VARIABLE_INT(monitor_history); + REFRESH_VARIABLE_INT(monitor_connect_interval); + REFRESH_VARIABLE_INT(monitor_connect_timeout); + REFRESH_VARIABLE_INT(monitor_ping_interval); + REFRESH_VARIABLE_INT(monitor_ping_max_failures); + REFRESH_VARIABLE_INT(monitor_ping_timeout); + REFRESH_VARIABLE_INT(monitor_aws_rds_topology_discovery_interval); + REFRESH_VARIABLE_INT(monitor_read_only_interval); + REFRESH_VARIABLE_INT(monitor_read_only_timeout); + REFRESH_VARIABLE_INT(monitor_read_only_max_timeout_count); + REFRESH_VARIABLE_BOOL(monitor_replication_lag_group_by_host); + REFRESH_VARIABLE_INT(monitor_replication_lag_interval); + REFRESH_VARIABLE_INT(monitor_replication_lag_timeout); + REFRESH_VARIABLE_INT(monitor_replication_lag_count); + REFRESH_VARIABLE_INT(monitor_groupreplication_healthcheck_interval); + REFRESH_VARIABLE_INT(monitor_groupreplication_healthcheck_timeout); + REFRESH_VARIABLE_INT(monitor_groupreplication_healthcheck_max_timeout_count); + REFRESH_VARIABLE_INT(monitor_groupreplication_max_transactions_behind_count); + REFRESH_VARIABLE_INT(monitor_groupreplication_max_transactions_behind_for_read_only); + REFRESH_VARIABLE_INT(monitor_galera_healthcheck_interval); + REFRESH_VARIABLE_INT(monitor_galera_healthcheck_timeout); + REFRESH_VARIABLE_INT(monitor_galera_healthcheck_max_timeout_count); + REFRESH_VARIABLE_INT(monitor_query_interval); + REFRESH_VARIABLE_INT(monitor_query_timeout); + REFRESH_VARIABLE_INT(monitor_slave_lag_when_null); + REFRESH_VARIABLE_INT(monitor_threads_min); + REFRESH_VARIABLE_INT(monitor_threads_max); + REFRESH_VARIABLE_INT(monitor_threads_queue_maxsize); + REFRESH_VARIABLE_INT(monitor_local_dns_cache_ttl); + REFRESH_VARIABLE_INT(monitor_local_dns_cache_refresh_interval); + REFRESH_VARIABLE_INT(monitor_local_dns_resolver_queue_maxsize); + + REFRESH_VARIABLE_CHAR(firewall_whitelist_errormsg); + REFRESH_VARIABLE_CHAR(init_connect); + REFRESH_VARIABLE_CHAR(ldap_user_variable); + REFRESH_VARIABLE_CHAR(add_ldap_user_comment); + REFRESH_VARIABLE_CHAR(default_session_track_gtids); for (int i=0; iget_variable_string((char *)"server_version"); - if (mysql_thread___eventslog_filename) free(mysql_thread___eventslog_filename); - mysql_thread___eventslog_filesize=GloMTH->get_variable_int((char *)"eventslog_filesize"); - mysql_thread___eventslog_default_log=GloMTH->get_variable_int((char *)"eventslog_default_log"); - mysql_thread___eventslog_format=GloMTH->get_variable_int((char *)"eventslog_format"); - mysql_thread___eventslog_filename=GloMTH->get_variable_string((char *)"eventslog_filename"); - if (mysql_thread___auditlog_filename) free(mysql_thread___auditlog_filename); - mysql_thread___auditlog_filesize=GloMTH->get_variable_int((char *)"auditlog_filesize"); - mysql_thread___auditlog_filename=GloMTH->get_variable_string((char *)"auditlog_filename"); + REFRESH_VARIABLE_CHAR(server_version); + REFRESH_VARIABLE_INT(eventslog_filesize); + REFRESH_VARIABLE_INT(eventslog_default_log); + REFRESH_VARIABLE_INT(eventslog_format); + REFRESH_VARIABLE_CHAR(eventslog_filename); + REFRESH_VARIABLE_INT(auditlog_filesize); + REFRESH_VARIABLE_CHAR(auditlog_filename); GloMyLogger->events_set_base_filename(); // both filename and filesize are set here GloMyLogger->audit_set_base_filename(); // both filename and filesize are set here - if (mysql_thread___default_schema) free(mysql_thread___default_schema); - mysql_thread___default_schema=GloMTH->get_variable_string((char *)"default_schema"); - if (mysql_thread___keep_multiplexing_variables) free(mysql_thread___keep_multiplexing_variables); - mysql_thread___keep_multiplexing_variables=GloMTH->get_variable_string((char *)"keep_multiplexing_variables"); - if (mysql_thread___default_authentication_plugin) free(mysql_thread___default_authentication_plugin); - mysql_thread___default_authentication_plugin=GloMTH->get_variable_string((char *)"default_authentication_plugin"); + REFRESH_VARIABLE_CHAR(default_schema); + REFRESH_VARIABLE_CHAR(keep_multiplexing_variables); + REFRESH_VARIABLE_CHAR(default_authentication_plugin); mysql_thread___default_authentication_plugin_int = GloMTH->variables.default_authentication_plugin_int; mysql_thread___server_capabilities=GloMTH->get_variable_uint16((char *)"server_capabilities"); - mysql_thread___handle_unknown_charset=GloMTH->get_variable_int((char *)"handle_unknown_charset"); - mysql_thread___poll_timeout=GloMTH->get_variable_int((char *)"poll_timeout"); - mysql_thread___poll_timeout_on_failure=GloMTH->get_variable_int((char *)"poll_timeout_on_failure"); - mysql_thread___have_compress=(bool)GloMTH->get_variable_int((char *)"have_compress"); - mysql_thread___have_ssl=(bool)GloMTH->get_variable_int((char *)"have_ssl"); - mysql_thread___multiplexing=(bool)GloMTH->get_variable_int((char *)"multiplexing"); - mysql_thread___log_unhealthy_connections=(bool)GloMTH->get_variable_int((char *)"log_unhealthy_connections"); - mysql_thread___connection_warming=(bool)GloMTH->get_variable_int((char*)"connection_warming"); - mysql_thread___enforce_autocommit_on_reads=(bool)GloMTH->get_variable_int((char *)"enforce_autocommit_on_reads"); - mysql_thread___autocommit_false_not_reusable=(bool)GloMTH->get_variable_int((char *)"autocommit_false_not_reusable"); - mysql_thread___autocommit_false_is_transaction=(bool)GloMTH->get_variable_int((char *)"autocommit_false_is_transaction"); - mysql_thread___verbose_query_error=(bool)GloMTH->get_variable_int((char *)"verbose_query_error"); - mysql_thread___commands_stats=(bool)GloMTH->get_variable_int((char *)"commands_stats"); - mysql_thread___query_digests=(bool)GloMTH->get_variable_int((char *)"query_digests"); - mysql_thread___query_digests_lowercase=(bool)GloMTH->get_variable_int((char *)"query_digests_lowercase"); - mysql_thread___query_digests_replace_null=(bool)GloMTH->get_variable_int((char *)"query_digests_replace_null"); - mysql_thread___query_digests_no_digits=(bool)GloMTH->get_variable_int((char *)"query_digests_no_digits"); - mysql_thread___query_digests_normalize_digest_text=(bool)GloMTH->get_variable_int((char *)"query_digests_normalize_digest_text"); - mysql_thread___query_digests_track_hostname=(bool)GloMTH->get_variable_int((char *)"query_digests_track_hostname"); - mysql_thread___query_digests_grouping_limit=(int)GloMTH->get_variable_int((char *)"query_digests_grouping_limit"); - mysql_thread___query_digests_groups_grouping_limit=(int)GloMTH->get_variable_int((char *)"query_digests_groups_grouping_limit"); - mysql_thread___query_digests_keep_comment=(bool)GloMTH->get_variable_int((char *)"query_digests_keep_comment"); - mysql_thread___parse_failure_logs_digest=(bool)GloMTH->get_variable_int((char *)"parse_failure_logs_digest"); + REFRESH_VARIABLE_INT(handle_unknown_charset); + REFRESH_VARIABLE_INT(poll_timeout); + REFRESH_VARIABLE_INT(poll_timeout_on_failure); + REFRESH_VARIABLE_BOOL(have_compress); + REFRESH_VARIABLE_BOOL(have_ssl); + REFRESH_VARIABLE_BOOL(multiplexing); + REFRESH_VARIABLE_BOOL(log_unhealthy_connections); + REFRESH_VARIABLE_BOOL(connection_warming); + REFRESH_VARIABLE_BOOL(enforce_autocommit_on_reads); + REFRESH_VARIABLE_BOOL(autocommit_false_not_reusable); + REFRESH_VARIABLE_BOOL(autocommit_false_is_transaction); + REFRESH_VARIABLE_BOOL(verbose_query_error); + REFRESH_VARIABLE_BOOL(commands_stats); + REFRESH_VARIABLE_BOOL(query_digests); + REFRESH_VARIABLE_BOOL(query_digests_lowercase); + REFRESH_VARIABLE_BOOL(query_digests_replace_null); + REFRESH_VARIABLE_BOOL(query_digests_no_digits); + REFRESH_VARIABLE_BOOL(query_digests_normalize_digest_text); + REFRESH_VARIABLE_BOOL(query_digests_track_hostname); + REFRESH_VARIABLE_INT(query_digests_grouping_limit); + REFRESH_VARIABLE_INT(query_digests_groups_grouping_limit); + REFRESH_VARIABLE_BOOL(query_digests_keep_comment); + REFRESH_VARIABLE_BOOL(parse_failure_logs_digest); variables.min_num_servers_lantency_awareness=GloMTH->get_variable_int((char *)"min_num_servers_lantency_awareness"); variables.aurora_max_lag_ms_only_read_from_replicas=GloMTH->get_variable_int((char *)"aurora_max_lag_ms_only_read_from_replicas"); variables.stats_time_backend_query=(bool)GloMTH->get_variable_int((char *)"stats_time_backend_query"); variables.stats_time_query_processor=(bool)GloMTH->get_variable_int((char *)"stats_time_query_processor"); variables.query_cache_stores_empty_result=(bool)GloMTH->get_variable_int((char *)"query_cache_stores_empty_result"); - mysql_thread___hostgroup_manager_verbose = GloMTH->get_variable_int((char *)"hostgroup_manager_verbose"); - mysql_thread___kill_backend_connection_when_disconnect=(bool)GloMTH->get_variable_int((char *)"kill_backend_connection_when_disconnect"); - mysql_thread___client_session_track_gtid=(bool)GloMTH->get_variable_int((char *)"client_session_track_gtid"); - mysql_thread___sessions_sort=(bool)GloMTH->get_variable_int((char *)"sessions_sort"); + REFRESH_VARIABLE_INT(hostgroup_manager_verbose); + REFRESH_VARIABLE_BOOL(kill_backend_connection_when_disconnect); + REFRESH_VARIABLE_BOOL(client_session_track_gtid); + REFRESH_VARIABLE_BOOL(sessions_sort); #ifdef IDLE_THREADS - mysql_thread___session_idle_show_processlist=(bool)GloMTH->get_variable_int((char *)"session_idle_show_processlist"); + REFRESH_VARIABLE_BOOL(session_idle_show_processlist); #endif // IDLE_THREADS - mysql_thread___show_processlist_extended=GloMTH->get_variable_int((char *)"show_processlist_extended"); - mysql_thread___servers_stats=(bool)GloMTH->get_variable_int((char *)"servers_stats"); - mysql_thread___default_reconnect=(bool)GloMTH->get_variable_int((char *)"default_reconnect"); - mysql_thread___enable_client_deprecate_eof=(bool)GloMTH->get_variable_int((char *)"enable_client_deprecate_eof"); - mysql_thread___enable_server_deprecate_eof=(bool)GloMTH->get_variable_int((char *)"enable_server_deprecate_eof"); - mysql_thread___enable_load_data_local_infile=(bool)GloMTH->get_variable_int((char *)"enable_load_data_local_infile"); - mysql_thread___log_mysql_warnings_enabled=(bool)GloMTH->get_variable_int((char *)"log_mysql_warnings_enabled"); - mysql_thread___client_host_cache_size=GloMTH->get_variable_int((char *)"client_host_cache_size"); - mysql_thread___client_host_error_counts=GloMTH->get_variable_int((char *)"client_host_error_counts"); - mysql_thread___handle_warnings=GloMTH->get_variable_int((char*)"handle_warnings"); - mysql_thread___evaluate_replication_lag_on_servers_load=GloMTH->get_variable_int((char*)"evaluate_replication_lag_on_servers_load"); + REFRESH_VARIABLE_INT(show_processlist_extended); + REFRESH_VARIABLE_BOOL(servers_stats); + REFRESH_VARIABLE_BOOL(default_reconnect); + REFRESH_VARIABLE_BOOL(enable_client_deprecate_eof); + REFRESH_VARIABLE_BOOL(enable_server_deprecate_eof); + REFRESH_VARIABLE_BOOL(enable_load_data_local_infile); + REFRESH_VARIABLE_BOOL(log_mysql_warnings_enabled); + REFRESH_VARIABLE_INT(client_host_cache_size); + REFRESH_VARIABLE_INT(client_host_error_counts); + REFRESH_VARIABLE_INT(handle_warnings); + REFRESH_VARIABLE_INT(evaluate_replication_lag_on_servers_load); #ifdef DEBUG - mysql_thread___session_debug=(bool)GloMTH->get_variable_int((char *)"session_debug"); + REFRESH_VARIABLE_BOOL(session_debug); #endif /* DEBUG */ GloMTH->wrunlock(); pthread_mutex_unlock(&GloVars.global.ext_glomth_mutex); From 7afaa649ae7b5d56060bfbdc55559f6f6fe1d690 Mon Sep 17 00:00:00 2001 From: Andriy Utkin Date: Tue, 7 May 2024 15:50:13 +0100 Subject: [PATCH 53/79] test/tap/tests: link dynamic libuuid instead of static Ubuntu ships libuuid.a in uuid-dev package which is why it could work. Fedora doesn't ship libuuid.a in any package. But it isn't picked up because `-luuid` flag is preceded by `-Wl,-Bstatic`: $ make build_tap_test_debug cd deps && OPTZ="-O0 -ggdb -DDEBUG" PROXYSQLCLICKHOUSE=1 PROXYDEBUG=1 CC=cc CXX=g++ make make[1]: Entering directory '/proxysql/deps' make[1]: Nothing to be done for 'default'. make[1]: Leaving directory '/proxysql/deps' cd lib && OPTZ="-O0 -ggdb -DDEBUG" PROXYSQLCLICKHOUSE=1 CC=cc CXX=g++ make make[1]: Entering directory '/proxysql/lib' make[1]: Nothing to be done for 'default'. make[1]: Leaving directory '/proxysql/lib' cd src && OPTZ="-O0 -ggdb -DDEBUG" PROXYSQLCLICKHOUSE=1 CC=cc CXX=g++ make make[1]: Entering directory '/proxysql/src' make[1]: 'proxysql' is up to date. make[1]: Leaving directory '/proxysql/src' cd test/tap && OPTZ="-O0 -ggdb -DDEBUG" CC=cc CXX=g++ make debug make[1]: Entering directory '/proxysql/test/tap' cd tap && CC=cc CXX=g++ make make[2]: Entering directory '/proxysql/test/tap/tap' make[2]: Nothing to be done for 'default'. make[2]: Leaving directory '/proxysql/test/tap/tap' cd ../deps && CC=cc CXX=g++ make make[2]: Entering directory '/proxysql/test/deps' make[2]: Nothing to be done for 'default'. make[2]: Leaving directory '/proxysql/test/deps' cd tests && CC=cc CXX=g++ make debug make[2]: Entering directory '/proxysql/test/tap/tests' g++ admin-listen_on_unix-t.cpp -I../tap -I/proxysql/deps/re2/re2 -I/proxysql/include -I/proxysql/deps/jemalloc/jemalloc/include/jemalloc -I/proxysql/deps/libconfig/libconfig/lib -I/proxysql/deps/mariadb-client-library/mariadb_client/include -I/proxysql/deps/libdaemon/libdaemon -I/proxysql/deps/libmicrohttpd/libmicrohttpd/src/include -I/proxysql/deps/libhttpserver/libhttpserver/src -I/proxysql/deps/curl/curl/include -I/proxysql/deps/libev/libev/ -I/proxysql/deps/prometheus-cpp/prometheus-cpp/pull/include -I/proxysql/deps/prometheus-cpp/prometheus-cpp/core/include -I../tap/cpp-dotenv/dynamic/cpp-dotenv/include -I/proxysql/deps/libssl/openssl//include -I/proxysql/deps/sqlite3/sqlite3 -I/proxysql/deps/json -L../tap -L/proxysql/deps/re2/re2/obj -L/proxysql/lib -L/proxysql/deps/jemalloc/jemalloc/lib -L/proxysql/deps/libconfig/libconfig/lib/.libs -L/proxysql/deps/mariadb-client-library/mariadb_client/libmariadb -L/proxysql/deps/libdaemon/libdaemon/libdaemon/.libs -L/proxysql/deps/libmicrohttpd/libmicrohttpd/src/microhttpd/.libs -L/proxysql/deps/libhttpserver/libhttpserver/build/src/.libs/ -L/proxysql/deps/curl/curl/lib/.libs -L/proxysql/deps/libev/libev//.libs -L/proxysql/deps/prometheus-cpp/prometheus-cpp/lib -L../tap -L/proxysql/deps/libssl/openssl/ -L/proxysql/deps/pcre/pcre/.libs -L/proxysql/deps/libinjection/libinjection/src -L/proxysql/deps/coredumper/coredumper/src -std=c++17 -DCXX17 -O0 -DDEBUG -ggdb -Wl,--no-as-needed -Wl,-rpath,../tap -DGITVERSION=\"2.6.3-2-g1d5ed29\" -Wl,--export-dynamic -Wl,-Bdynamic -lgnutls -ltap -lcpp_dotenv -lcurl -lssl -lcrypto -Wl,-Bstatic -lconfig -lproxysql -ldaemon -lconfig++ -lre2 -lpcrecpp -lpcre -lmariadbclient -lhttpserver -lmicrohttpd -linjection -lev -lprometheus-cpp-pull -lprometheus-cpp-core -luuid -Wl,-Bdynamic -lpthread -lm -lz -lrt -ldl /proxysql/deps/cityhash/cityhash//src/.libs/libcityhash.a /proxysql/deps/coredumper/coredumper/src/libcoredumper.a -o admin-listen_on_unix-t /usr/bin/ld: cannot find -luuid: No such file or directory collect2: error: ld returned 1 exit status make[2]: *** [Makefile:226: admin-listen_on_unix-t] Error 1 make[2]: Leaving directory '/proxysql/test/tap/tests' make[1]: *** [Makefile:21: tests] Error 2 make[1]: Leaving directory '/proxysql/test/tap' make: *** [Makefile:236: build_tap_tests_debug] Error 2 --- test/tap/tests/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/tap/tests/Makefile b/test/tap/tests/Makefile index b1f73e5c0b..4e1024c9de 100644 --- a/test/tap/tests/Makefile +++ b/test/tap/tests/Makefile @@ -116,8 +116,8 @@ ifeq ($(UNAME_S),Linux) endif MYLIBS := -Wl,--export-dynamic -MYLIBS += -Wl,-Bdynamic -lgnutls -ltap -lcpp_dotenv -lcurl -lssl -lcrypto -MYLIBS += -Wl,-Bstatic -lconfig -lproxysql -ldaemon -lconfig++ -lre2 -lpcrecpp -lpcre -lmariadbclient -lhttpserver -lmicrohttpd -linjection -lev -lprometheus-cpp-pull -lprometheus-cpp-core -luuid +MYLIBS += -Wl,-Bdynamic -lgnutls -ltap -lcpp_dotenv -lcurl -lssl -lcrypto -luuid +MYLIBS += -Wl,-Bstatic -lconfig -lproxysql -ldaemon -lconfig++ -lre2 -lpcrecpp -lpcre -lmariadbclient -lhttpserver -lmicrohttpd -linjection -lev -lprometheus-cpp-pull -lprometheus-cpp-core MYLIBS += -Wl,-Bdynamic -lpthread -lm -lz -lrt -ldl $(EXTRALINK) #MYLIBS := -Wl,--export-dynamic -Wl,-Bdynamic -lssl -lcrypto -lgnutls -ltap -lcpp_dotenv -Wl,-Bstatic -lconfig -lproxysql -ldaemon -lconfig++ -lre2 -lpcrecpp -lpcre -lmariadbclient -lhttpserver -lmicrohttpd -linjection -lev -lprometheus-cpp-pull -lprometheus-cpp-core -luuid -Wl,-Bdynamic -lpthread -lm -lz -lrt -ldl $(EXTRALINK) MYLIBSJEMALLOC := -Wl,-Bstatic -ljemalloc From c18754b5958d44618dfd03a8916d35729c181b5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Fri, 10 May 2024 01:39:57 +0000 Subject: [PATCH 54/79] Disable debug filter in test_query_rules_fast_routing_algorithm-t --- test/tap/tests/test_query_rules_fast_routing_algorithm-t.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/tap/tests/test_query_rules_fast_routing_algorithm-t.cpp b/test/tap/tests/test_query_rules_fast_routing_algorithm-t.cpp index 81b8c39ee4..ce0faa6049 100644 --- a/test/tap/tests/test_query_rules_fast_routing_algorithm-t.cpp +++ b/test/tap/tests/test_query_rules_fast_routing_algorithm-t.cpp @@ -215,6 +215,9 @@ int test_fast_routing_algorithm( MYSQL_QUERY_T(admin, "SET admin-debug_output=3"); MYSQL_QUERY_T(admin, "LOAD ADMIN VARIABLES TO RUNTIME"); MYSQL_QUERY_T(admin, "UPDATE debug_levels SET verbosity=7 WHERE module='debug_mysql_query_processor'"); + // If there is a generic debug filter (line==0) on Query_Processor.cpp , process_mysql_query() , disable it. + // If the filter was present it will be automatically recreated by the tester. + MYSQL_QUERY_T(admin, "DELETE FROM debug_filters WHERE filename='Query_Processor.cpp' AND line=0 AND funct='process_mysql_query'"); MYSQL_QUERY_T(admin, "LOAD DEBUG TO RUNTIME"); // Open the SQLite3 db for debugging From ef3360f102850715788b214889808ce91609e77e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Fri, 3 May 2024 16:54:30 +0200 Subject: [PATCH 55/79] Several enhancements for TAP tests compilation - Split 'libtap' into different binaries. This allows for the reuse of 'TAP' utilities that make use of MySQL or other third party library versions. Each of these tests that require special library versions that aren't the common ones embedded into the dynamic library 'libtap.so' should be compiled defining special helper targets, in the 'tests' folder Makefile itself. These helper targets with specify the linking of the required version of the library for the test. Each MySQL client version supported by the 'tap' tests that breaks headers compatibility should be compiled into a different object file, following the previous described procedure. - Add MySQL 8.4.0 client library as a new TAP test dependency. - Multiple compilation fixes consequence of the introduced change to 'libtap.a'. - Added as ProxySQL dependency the forward declaration header for 'nlohmann::json'. This header is the proper way of including the library is headers which are sensible of being included in multiples files. This speeds compilation times of each TAP test. - Removed 'std::regex' dependency from 'utils.cpp', and replaced by project dependency 're2'. Library 'std::regex' should not be used in any place that introduces it as a dependency for later objects, it's heavy, slow, and should be avoided in general. --- deps/json/json_fwd.hpp | 175 ++++++++++++++++++ .../mysql-connector-c-8.4.0/mysql-connector-c | 1 + test/tap/Makefile | 5 + test/tap/tap/Makefile | 56 +++++- test/tap/tap/command_line.cpp | 1 - test/tap/tap/utils.cpp | 55 +++--- test/tap/tap/utils.h | 40 ++-- test/tap/tests/Makefile | 55 +++--- test/tap/tests/mysql-fast_forward-t.cpp | 1 - .../tap/tests/prepare_statement_err3024-t.cpp | 67 ------- .../tests/reg_test_3247-mycli_support-t.cpp | 15 -- .../tests/reg_test_3606-mysql_warnings-t.cpp | 3 +- .../tests/reg_test_4402-mysql_fields-t.cpp | 3 + .../reg_test_fast_forward_split_packet-t.cpp | 4 +- .../reg_test_mariadb_stmt_store_result-t.cpp | 67 ------- test/tap/tests/set_testing-240-t.cpp | 7 +- test/tap/tests/set_testing-240.h | 14 +- test/tap/tests/set_testing-multi-t.cpp | 5 +- test/tap/tests/set_testing.h | 14 +- test/tap/tests/test_auth_methods-t.cpp | 2 +- test/tap/tests/test_backend_conn_ping-t.cpp | 2 +- test/tap/tests/test_dns_cache-t.cpp | 18 +- test/tap/tests/test_flagOUT_weight-t.cpp | 2 +- test/tap/tests/test_format_utils-t.cpp | 4 +- .../tests/test_mysql_connect_retries-t.cpp | 3 +- .../test_mysql_connect_retries_delay-t.cpp | 4 +- .../test_mysql_query_digests_stages-t.cpp | 10 +- test/tap/tests/test_sqlite3_pass_exts-t.cpp | 1 + .../deprecate_eof_support/Makefile | 10 +- 29 files changed, 347 insertions(+), 297 deletions(-) create mode 100644 deps/json/json_fwd.hpp create mode 120000 test/deps/mysql-connector-c-8.4.0/mysql-connector-c diff --git a/deps/json/json_fwd.hpp b/deps/json/json_fwd.hpp new file mode 100644 index 0000000000..83c21f857b --- /dev/null +++ b/deps/json/json_fwd.hpp @@ -0,0 +1,175 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + +#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ +#define INCLUDE_NLOHMANN_JSON_FWD_HPP_ + +#include // int64_t, uint64_t +#include // map +#include // allocator +#include // string +#include // vector + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// This file contains all macro definitions affecting or depending on the ABI + +#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK + #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) + #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 2 + #warning "Already included a different version of the library!" + #endif + #endif +#endif + +#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_PATCH 2 // NOLINT(modernize-macro-to-enum) + +#ifndef JSON_DIAGNOSTICS + #define JSON_DIAGNOSTICS 0 +#endif + +#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0 +#endif + +#if JSON_DIAGNOSTICS + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag +#else + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS +#endif + +#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp +#else + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION + #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0 +#endif + +// Construct the namespace ABI tags component +#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b +#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \ + NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) + +#define NLOHMANN_JSON_ABI_TAGS \ + NLOHMANN_JSON_ABI_TAGS_CONCAT( \ + NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \ + NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON) + +// Construct the namespace version component +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \ + _v ## major ## _ ## minor ## _ ## patch +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) + +#if NLOHMANN_JSON_NAMESPACE_NO_VERSION +#define NLOHMANN_JSON_NAMESPACE_VERSION +#else +#define NLOHMANN_JSON_NAMESPACE_VERSION \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \ + NLOHMANN_JSON_VERSION_MINOR, \ + NLOHMANN_JSON_VERSION_PATCH) +#endif + +// Combine namespace components +#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b +#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \ + NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) + +#ifndef NLOHMANN_JSON_NAMESPACE +#define NLOHMANN_JSON_NAMESPACE \ + nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN +#define NLOHMANN_JSON_NAMESPACE_BEGIN \ + namespace nlohmann \ + { \ + inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) \ + { +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_END +#define NLOHMANN_JSON_NAMESPACE_END \ + } /* namespace (inline namespace) NOLINT(readability/namespace) */ \ + } // namespace nlohmann +#endif + + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +NLOHMANN_JSON_NAMESPACE_BEGIN + +/*! +@brief default JSONSerializer template argument + +This serializer ignores the template arguments and uses ADL +([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) +for serialization. +*/ +template +struct adl_serializer; + +/// a class to store JSON values +/// @sa https://json.nlohmann.me/api/basic_json/ +template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer, + class BinaryType = std::vector> +class basic_json; + +/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document +/// @sa https://json.nlohmann.me/api/json_pointer/ +template +class json_pointer; + +/*! +@brief default specialization +@sa https://json.nlohmann.me/api/json/ +*/ +using json = basic_json<>; + +/// @brief a minimal map-like container that preserves insertion order +/// @sa https://json.nlohmann.me/api/ordered_map/ +template +struct ordered_map; + +/// @brief specialization that maintains the insertion order of object keys +/// @sa https://json.nlohmann.me/api/ordered_json/ +using ordered_json = basic_json; + +NLOHMANN_JSON_NAMESPACE_END + +#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ diff --git a/test/deps/mysql-connector-c-8.4.0/mysql-connector-c b/test/deps/mysql-connector-c-8.4.0/mysql-connector-c new file mode 120000 index 0000000000..986e2fbe69 --- /dev/null +++ b/test/deps/mysql-connector-c-8.4.0/mysql-connector-c @@ -0,0 +1 @@ +mysql-8.4.0/ \ No newline at end of file diff --git a/test/tap/Makefile b/test/tap/Makefile index 6f8c64cb42..66f1195a09 100644 --- a/test/tap/Makefile +++ b/test/tap/Makefile @@ -25,6 +25,11 @@ tests_with_deps: tap test_deps cd tests_with_deps && CC=${CC} CXX=${CXX} ${MAKE} $(MAKECMDGOALS) +.PHONY: clean_utils +.SILENT: clean_utils +clean_utils: + cd tap && ${MAKE} -s clean_utils + .PHONY: clean .SILENT: clean clean: diff --git a/test/tap/tap/Makefile b/test/tap/tap/Makefile index 3a4be0e599..68c2625ba9 100644 --- a/test/tap/tap/Makefile +++ b/test/tap/tap/Makefile @@ -20,6 +20,18 @@ MARIADB_PATH := $(DEPS_PATH)/mariadb-client-library/mariadb_client MARIADB_IDIR := $(MARIADB_PATH)/include MARIADB_LDIR := $(MARIADB_PATH)/libmariadb +TEST_DEPS_PATH := $(PROXYSQL_PATH)/test/deps + +TEST_MYSQL_PATH := $(TEST_DEPS_PATH)/mysql-connector-c/mysql-connector-c +TEST_MYSQL_IDIR := $(TEST_MYSQL_PATH)/include +TEST_MYSQL_EDIR := $(TEST_MYSQL_PATH)/libbinlogevents/export/ +TEST_MYSQL_LDIR := $(TEST_MYSQL_PATH)/libmysql + +TEST_MYSQL8_PATH := $(TEST_DEPS_PATH)/mysql-connector-c-8.4.0/mysql-connector-c +TEST_MYSQL8_IDIR := $(TEST_MYSQL8_PATH)/include +TEST_MYSQL8_EDIR := $(TEST_MYSQL8_PATH)/libbinlogevents/export/ +TEST_MYSQL8_LDIR := $(TEST_MYSQL8_PATH)/libmysql + CURL_PATH := $(DEPS_PATH)/curl/curl CURL_IDIR := $(CURL_PATH)/include CURL_LDIR := $(CURL_PATH)/lib/.libs @@ -32,10 +44,13 @@ DOTENV_PATH := ./cpp-dotenv/static/cpp-dotenv DOTENV_IDIR := $(DOTENV_PATH)/include DOTENV_LDIR := $(DOTENV_PATH) +RE2_PATH := $(DEPS_PATH)/re2/re2 +RE2_IDIR := $(RE2_PATH) +RE2_LDIR := $(RE2_PATH)/obj LIBPROXYSQLAR := $(PROXYSQL_LDIR)/libproxysql.a -IDIRS := -I$(PROXYSQL_IDIR) -I$(JSON_IDIR) -I$(MARIADB_IDIR) -I${CURL_IDIR} -I${SQLITE3_IDIR} -I$(DOTENV_IDIR) +IDIRS := -I$(PROXYSQL_IDIR) -I$(JSON_IDIR) -I${CURL_IDIR} -I${SQLITE3_IDIR} -I$(DOTENV_IDIR) -I$(RE2_IDIR) ### detect compiler support for c++11/17 CPLUSPLUS := $(shell ${CC} -std=c++17 -dM -E -x c++ /dev/null 2>/dev/null | grep -F __cplusplus | grep -Po '\d\d\d\d\d\dL') @@ -76,10 +91,11 @@ endif default: all .PHONY: all -all: libtap.a libtap.so libssl.so.3 libcrypto.so.3 libcpp_dotenv.so +all: libtap_mariadb.a libtap_mysql57.a libtap_mysql8.a \ + libtap.so libssl.so.3 libcrypto.so.3 libcpp_dotenv.so libre2.so debug: OPT := $(STDCPP) -O0 -DDEBUG -ggdb -Wl,--no-as-needed $(WASAN) -debug: libtap.a libtap.so +debug: libtap_mariadb.a libtap_mysql57.a libtap_mysql8.a libtap.so ### helper targets @@ -87,17 +103,29 @@ debug: libtap.a libtap.so command_line.o: command_line.cpp cpp-dotenv/static/cpp-dotenv/libcpp_dotenv.a libcurl.so libssl.so.3 libcrypto.so.3 libcpp_dotenv.so $(CXX) -fPIC -c command_line.cpp $(IDIRS) $(OPT) -utils.o: utils.cpp cpp-dotenv/static/cpp-dotenv/libcpp_dotenv.a libcurl.so libssl.so.3 libcrypto.so.3 libcpp_dotenv.so - $(CXX) -fPIC -c utils.cpp $(IDIRS) $(OPT) +utils_mariadb.o: utils.cpp cpp-dotenv/static/cpp-dotenv/libcpp_dotenv.a libcurl.so libssl.so.3 libcrypto.so.3 libcpp_dotenv.so + $(CXX) -fPIC -c utils.cpp $(IDIRS) -I$(MARIADB_IDIR) $(OPT) -o $@ + +utils_mysql57.o: utils.cpp cpp-dotenv/static/cpp-dotenv/libcpp_dotenv.a libcurl.so libssl.so.3 libcrypto.so.3 libcpp_dotenv.so + $(CXX) -DDISABLE_WARNING_COUNT_LOGGING -fPIC -c utils.cpp $(IDIRS) -I$(TEST_MYSQL_IDIR) -I$(TEST_MYSQL_EDIR) $(OPT) -o $@ + +utils_mysql8.o: utils.cpp cpp-dotenv/static/cpp-dotenv/libcpp_dotenv.a libcurl.so libssl.so.3 libcrypto.so.3 libcpp_dotenv.so + $(CXX) -DDISABLE_WARNING_COUNT_LOGGING -fPIC -c utils.cpp $(IDIRS) -I$(TEST_MYSQL8_IDIR) -I$(TEST_MYSQL_EDIR) $(OPT) -o $@ tap.o: tap.cpp cpp-dotenv/static/cpp-dotenv/libcpp_dotenv.a libcurl.so libssl.so.3 libcrypto.so.3 libcpp_dotenv.so $(CXX) -fPIC -c tap.cpp $(IDIRS) $(OPT) -libtap.a: tap.o command_line.o utils.o cpp-dotenv/static/cpp-dotenv/libcpp_dotenv.a - ar rcs libtap.a tap.o command_line.o utils.o $(SQLITE3_LDIR)/sqlite3.o $(PROXYSQL_LDIR)/obj/sha256crypt.oo +libtap_mariadb.a: tap.o command_line.o utils_mariadb.o cpp-dotenv/static/cpp-dotenv/libcpp_dotenv.a + ar rcs libtap_mariadb.a tap.o command_line.o utils_mariadb.o $(SQLITE3_LDIR)/sqlite3.o $(PROXYSQL_LDIR)/obj/sha256crypt.oo -libtap.so: libtap.a cpp-dotenv/dynamic/cpp-dotenv/libcpp_dotenv.so - $(CXX) -shared -o libtap.so -Wl,--whole-archive libtap.a -Wl,--no-whole-archive $(LWGCOV) +libtap_mysql57.a: tap.o command_line.o utils_mysql57.o cpp-dotenv/static/cpp-dotenv/libcpp_dotenv.a + ar rcs libtap_mysql57.a tap.o command_line.o utils_mysql57.o $(SQLITE3_LDIR)/sqlite3.o $(PROXYSQL_LDIR)/obj/sha256crypt.oo + +libtap_mysql8.a: tap.o command_line.o utils_mysql8.o cpp-dotenv/static/cpp-dotenv/libcpp_dotenv.a + ar rcs libtap_mysql8.a tap.o command_line.o utils_mysql8.o $(SQLITE3_LDIR)/sqlite3.o $(PROXYSQL_LDIR)/obj/sha256crypt.oo + +libtap.so: libtap_mariadb.a cpp-dotenv/dynamic/cpp-dotenv/libcpp_dotenv.so + $(CXX) -shared -o libtap.so -Wl,--whole-archive libtap_mariadb.a -Wl,--no-whole-archive $(LWGCOV) ### tap deps targets @@ -114,6 +142,9 @@ libcpp_dotenv.so: cpp-dotenv/dynamic/cpp-dotenv/libcpp_dotenv.so libcurl.so: $(DEPS_PATH)/curl/curl/lib/.libs/libcurl.so cp -a $(DEPS_PATH)/curl/curl/lib/.libs/libcurl.so* . +libre2.so: $(DEPS_PATH)/re2/re2/obj/so/libre2.so + cp -a $(DEPS_PATH)/re2/re2/obj/so/libre2.so* . + cpp-dotenv/static/cpp-dotenv/libcpp_dotenv.a: cd cpp-dotenv/static && rm -rf cpp-dotenv-*/ || true cd cpp-dotenv/static && tar -zxf ../cpp-dotenv-*.tar.gz @@ -135,6 +166,13 @@ cpp-dotenv/dynamic/cpp-dotenv/libcpp_dotenv.so: ### clean targets +.SILENT: clean_utils +.PHONY: clean_utils +clean_utils: + find . -name 'utils_*.*' -delete || true + find . -name 'libtap_*.*' -delete || true + find . -name 'libtap.so' -delete || true + .SILENT: clean .PHONY: clean clean: diff --git a/test/tap/tap/command_line.cpp b/test/tap/tap/command_line.cpp index 13d6827099..ce473c77e7 100644 --- a/test/tap/tap/command_line.cpp +++ b/test/tap/tap/command_line.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include diff --git a/test/tap/tap/utils.cpp b/test/tap/tap/utils.cpp index 54b822a16b..e8633536f2 100644 --- a/test/tap/tap/utils.cpp +++ b/test/tap/tap/utils.cpp @@ -1,30 +1,28 @@ #include #include -#include #include #include #include #include -#include #include -#include +#include +#include #include - -#include "mysql.h" - -#include "tap.h" -#include "utils.h" - #include #include -#include -#include -#include + +#include "json.hpp" +#include "re2/re2.h" #include "proxysql_utils.h" +#include "mysql.h" +#include "utils.h" +#include "tap.h" + using std::pair; using std::map; +using std::fstream; using std::string; using std::vector; @@ -73,6 +71,10 @@ using nlohmann::json; vec_query.pop_back();\ } +#ifndef DISABLE_WARNING_COUNT_LOGGING + +extern "C" { + MYSQL* mysql_init_override(MYSQL* mysql, const char* file, int line) { static bool init = false; MYSQL* result = (*real_mysql_init)(mysql); @@ -181,6 +183,10 @@ my_bool mysql_stmt_close_override(MYSQL_STMT* stmt, const char* file, int line) return (*real_mysql_stmt_close)(stmt); } +} + +#endif + std::size_t count_matches(const string& str, const string& substr) { std::size_t result = 0; std::size_t pos = 0; @@ -1319,24 +1325,27 @@ int open_file_and_seek_end(const string& f_path, std::fstream& f_stream) { return EXIT_SUCCESS; } -std::vector get_matching_lines(std::fstream& f_stream, const std::string& regex) { - std::vector found_matches {}; +vector get_matching_lines(fstream& f_stream, const string& s_regex, bool get_matches) { + vector found_matches {}; - std::string next_line {}; - std::fstream::pos_type init_pos { f_stream.tellg() }; + string next_line {}; + fstream::pos_type init_pos { f_stream.tellg() }; - while (std::getline(f_stream, next_line)) { - std::regex regex_err_line { regex }; - std::smatch match_results {}; + while (getline(f_stream, next_line)) { + re2::RE2 regex { s_regex }; + re2::StringPiece match; - if (std::regex_search(next_line, match_results, regex_err_line)) { - found_matches.push_back({ f_stream.tellg(), next_line, match_results }); + if (get_matches && RE2::PartialMatch(next_line, regex, &match)) { + found_matches.push_back({ f_stream.tellg(), next_line, match.ToString() }); + } + if (!get_matches && RE2::PartialMatch(next_line, regex)) { + found_matches.push_back({ f_stream.tellg(), next_line, match.ToString() }); } } if (found_matches.empty() == false) { - const std::string& last_match { std::get(found_matches.back()) }; - const std::fstream::pos_type last_match_pos { std::get(found_matches.back()) }; + const string& last_match { std::get(found_matches.back()) }; + const fstream::pos_type last_match_pos { std::get(found_matches.back()) }; f_stream.clear(f_stream.rdstate() & ~std::ios_base::failbit); f_stream.seekg(last_match_pos); diff --git a/test/tap/tap/utils.h b/test/tap/tap/utils.h index 7422abe146..17518c4f8c 100644 --- a/test/tap/tap/utils.h +++ b/test/tap/tap/utils.h @@ -4,19 +4,21 @@ #include #include #include -#include #include -#include -#include #include "curl/curl.h" -#include + #include "sqlite3db.h" +#include "json_fwd.hpp" #include "command_line.h" -#include "json.hpp" +#include "mysql.h" +// Improve dependency failure compilation error #ifndef DISABLE_WARNING_COUNT_LOGGING + +extern "C" { + /* We are overriding some of the mariadb APIs to extract the warning count and print it in the log. This override will apply to all TAP tests, except when the TAP test is linked with the MySQL client library (LIBMYSQL_HELPER defined). */ @@ -49,6 +51,9 @@ my_bool mysql_stmt_close_override(MYSQL_STMT* stmt, const char* file, int line); #define mysql_stmt_execute(stmt) mysql_stmt_execute_override(stmt,__FILE__,__LINE__) #define mysql_stmt_store_result(stmt) mysql_stmt_store_result_override(stmt,__FILE__,__LINE__) #define mysql_stmt_close(stmt) mysql_stmt_close_override(stmt,__FILE__,__LINE__) + +} + #endif inline std::string get_formatted_time() { @@ -92,20 +97,12 @@ int mysql_query_t(MYSQL* mysql, const char* query); } \ } while(0) -#ifdef __cplusplus -extern "C" { -#endif - int show_variable(MYSQL *mysql, const std::string& var_name, std::string& var_value); int show_admin_global_variable(MYSQL *mysql, const std::string& var_name, std::string& var_value); int set_admin_global_variable(MYSQL *mysql, const std::string& var_name, const std::string& var_value); int get_server_version(MYSQL *mysql, std::string& version); int select_config_file(MYSQL* mysql, std::string& resultset); -#ifdef __cplusplus -} -#endif - /* * @return int Zero in case of success, or the errno returned by `execvp` in case of failure. */ @@ -120,8 +117,6 @@ int execvp(const std::string& file, const std::vector& argv, std::s */ int exec(const std::string& cmd, std::string& result); - - // create table test.sbtest1 with num_rows rows int create_table_test_sbtest1(int num_rows, MYSQL *mysql); int create_table_test_sqlite_sbtest1(int num_rows, MYSQL *mysql); // as above, but for SQLite3 server @@ -585,21 +580,26 @@ std::string get_env(const std::string& var); /** * @brief Opens the file in the supplied path in the provided stream, and seeks the end of it. * @param f_path Path to the file to open. - * @param f_logfile Output parameter with the stream to be updated with the oppened file. - * @return EXIT_SUCCESS in case of success, EXIT_FAILURE otherwise. Error casuse is logged. + * @param f_logfile Output parameter with the stream to be updated with the opened file. + * @return EXIT_SUCCESS in case of success, EXIT_FAILURE otherwise. Error cause is logged. */ int open_file_and_seek_end(const std::string& f_path, std::fstream& f_stream); -using line_match_t = std::tuple; -enum LINE_MATCH_T { POS, LINE, MATCHES }; +using line_match_t = std::tuple; +enum LINE_MATCH_T { POS, LINE, MATCH }; /** * @brief Extracts the lines matching the regex from the supplied stream till reaching EOF. * @param f_stream The stream to be matched with the regex. * @param regex The regex used to match the stream line-by-line. + * @param get_matches If matched patterns should be returned (LINE_MATCH_T::MATCH). If supplied, the regex + * should contain at least one sub-pattern, or be a complete sub-pattern, otherwise it will fail to match. + * For example, regex '\d+' should become '(\d+)'. * @return All the lines found matching the regex. */ -std::vector get_matching_lines(std::fstream& f_stream, const std::string& regex); +std::vector get_matching_lines( + std::fstream& f_stream, const std::string& regex, bool get_matches=false +); /** * @brief Opens a sqlite3 db file located in the supplied path with the provided flags. diff --git a/test/tap/tests/Makefile b/test/tap/tests/Makefile index b1f73e5c0b..1a8387f1c5 100644 --- a/test/tap/tests/Makefile +++ b/test/tap/tests/Makefile @@ -84,7 +84,6 @@ TAP_LDIR := ../tap DOTENV_DYN_PATH := $(TAP_LDIR)/cpp-dotenv/dynamic/cpp-dotenv DOTENV_DYN_IDIR := $(DOTENV_DYN_PATH)/include -#DOTENV_DYN_LDIR := $(DOTENV_DYN_PATH) DOTENV_DYN_LDIR := $(TAP_LDIR) TEST_DEPS_PATH := $(PROXYSQL_PATH)/test/deps @@ -98,6 +97,11 @@ TEST_MYSQL_IDIR := $(TEST_MYSQL_PATH)/include TEST_MYSQL_EDIR := $(TEST_MYSQL_PATH)/libbinlogevents/export/ TEST_MYSQL_LDIR := $(TEST_MYSQL_PATH)/libmysql +TEST_MYSQL8_PATH := $(TEST_DEPS_PATH)/mysql-connector-c-8.4.0/mysql-connector-c +TEST_MYSQL8_IDIR := $(TEST_MYSQL8_PATH)/include +TEST_MYSQL8_EDIR := $(TEST_MYSQL8_PATH)/libbinlogevents/export/ +TEST_MYSQL8_LDIR := $(TEST_MYSQL8_PATH)/libmysql + LIBPROXYSQLAR := $(PROXYSQL_LDIR)/libproxysql.a ODIR := $(PROXYSQL_PATH)/obj @@ -106,8 +110,14 @@ EXECUTABLE := proxysql OBJ := $(PROXYSQL_PATH)/src/obj/proxysql_global.o $(PROXYSQL_PATH)/src/obj/main.o $(PROXYSQL_PATH)/src/obj/proxy_tls.o -IDIRS := -I$(TAP_IDIR) -I$(RE2_IDIR) -I$(PROXYSQL_IDIR) -I$(JEMALLOC_IDIR) -I$(LIBCONFIG_IDIR) -I$(MARIADB_IDIR) -I$(DAEMONPATH_IDIR) -I$(MICROHTTPD_IDIR) -I$(LIBHTTPSERVER_IDIR) -I$(CURL_IDIR) -I$(EV_IDIR) -I$(PROMETHEUS_IDIR) -I$(DOTENV_DYN_IDIR) -I$(SSL_IDIR) -I$(SQLITE3_IDIR) -I$(JSON_IDIR) -LDIRS := -L$(TAP_LDIR) -L$(RE2_LDIR) -L$(PROXYSQL_LDIR) -L$(JEMALLOC_LDIR) -L$(LIBCONFIG_LDIR) -L$(MARIADB_LDIR) -L$(DAEMONPATH_LDIR) -L$(MICROHTTPD_LDIR) -L$(LIBHTTPSERVER_LDIR) -L$(CURL_LDIR) -L$(EV_LDIR) -L$(PROMETHEUS_LDIR) -L$(DOTENV_DYN_LDIR) -L$(SSL_LDIR) -L$(PCRE_LDIR) -L$(LIBINJECTION_LDIR) +SOURCES := ../tap/utils.cpp + +IDIRS := -I$(TAP_IDIR) -I$(RE2_IDIR) -I$(PROXYSQL_IDIR) -I$(JEMALLOC_IDIR) -I$(LIBCONFIG_IDIR) -I$(MARIADB_IDIR)\ + -I$(DAEMONPATH_IDIR) -I$(MICROHTTPD_IDIR) -I$(LIBHTTPSERVER_IDIR) -I$(CURL_IDIR) -I$(EV_IDIR)\ + -I$(PROMETHEUS_IDIR) -I$(DOTENV_DYN_IDIR) -I$(SSL_IDIR) -I$(SQLITE3_IDIR) -I$(JSON_IDIR) +LDIRS := -L$(TAP_LDIR) -L$(RE2_LDIR) -L$(PROXYSQL_LDIR) -L$(JEMALLOC_LDIR) -L$(LIBCONFIG_LDIR) -L$(MARIADB_LDIR)\ + -L$(DAEMONPATH_LDIR) -L$(MICROHTTPD_LDIR) -L$(LIBHTTPSERVER_LDIR) -L$(CURL_LDIR) -L$(EV_LDIR)\ + -L$(PROMETHEUS_LDIR) -L$(DOTENV_DYN_LDIR) -L$(SSL_LDIR) -L$(PCRE_LDIR) -L$(LIBINJECTION_LDIR) UNAME_S := $(shell uname -s) @@ -116,13 +126,13 @@ ifeq ($(UNAME_S),Linux) endif MYLIBS := -Wl,--export-dynamic -MYLIBS += -Wl,-Bdynamic -lgnutls -ltap -lcpp_dotenv -lcurl -lssl -lcrypto -MYLIBS += -Wl,-Bstatic -lconfig -lproxysql -ldaemon -lconfig++ -lre2 -lpcrecpp -lpcre -lmariadbclient -lhttpserver -lmicrohttpd -linjection -lev -lprometheus-cpp-pull -lprometheus-cpp-core -luuid -MYLIBS += -Wl,-Bdynamic -lpthread -lm -lz -lrt -ldl $(EXTRALINK) -#MYLIBS := -Wl,--export-dynamic -Wl,-Bdynamic -lssl -lcrypto -lgnutls -ltap -lcpp_dotenv -Wl,-Bstatic -lconfig -lproxysql -ldaemon -lconfig++ -lre2 -lpcrecpp -lpcre -lmariadbclient -lhttpserver -lmicrohttpd -linjection -lev -lprometheus-cpp-pull -lprometheus-cpp-core -luuid -Wl,-Bdynamic -lpthread -lm -lz -lrt -ldl $(EXTRALINK) +MYLIBS += -Wl,-Bdynamic -lgnutls -ltap -lcpp_dotenv -lcurl -lssl -lcrypto -lre2 +MYLIBS += -Wl,-Bstatic -lconfig -lproxysql -ldaemon -lconfig++ -lpcrecpp -lpcre -lmariadbclient -lhttpserver\ + -lmicrohttpd -linjection -lev -lprometheus-cpp-pull -lprometheus-cpp-core +MYLIBS += -Wl,-Bdynamic -lpthread -lm -lz -lrt -ldl $(EXTRALINK) -luuid + MYLIBSJEMALLOC := -Wl,-Bstatic -ljemalloc STATIC_LIBS := $(CITYHASH_LDIR)/libcityhash.a -#STATIC_LIBS := $(SSL_LDIR)/libssl.a $(SSL_LDIR)/libcrypto.a $(CITYHASH_LDIR)/libcityhash.a LIBCOREDUMPERAR := ifeq ($(UNAME_S),Linux) @@ -158,20 +168,23 @@ endif OPT := $(STDCPP) -O2 -ggdb -Wl,--no-as-needed -Wl,-rpath,$(TAP_LDIR) $(WGCOV) $(WASAN) -DGITVERSION=\"$(GIT_VERSION)\" - ### main targets .DEFAULT: default .PHONY: default default: all +CUSTOMARGS := -I$(TAP_IDIR) -I$(CURL_IDIR) -I$(SQLITE3_IDIR) -I$(PROXYSQL_IDIR) -I$(JSON_IDIR) -I$(SSL_IDIR) -I$(RE2_IDIR) +CUSTOMARGS += -L$(TAP_LDIR) -L$(CURL_LDIR) -L$(SSL_LDIR) -L$(RE2_LDIR) +CUSTOMARGS += -Wl,-Bdynamic -lcpp_dotenv -lcurl -lssl -lcrypto -lre2 -lpthread -lz -ldl + .PHONY: all all: tests debug: OPT := $(STDCPP) -O0 -DDEBUG -ggdb -Wl,--no-as-needed -Wl,-rpath,$(TAP_LDIR) $(WGCOV) $(WASAN) -DGITVERSION=\"$(GIT_VERSION)\" debug: tests -#tests: build_test_deps +tests: CUSTOMARGS += $(OPT) tests: tests-cpp \ tests-php \ tests-py \ @@ -224,7 +237,6 @@ py-%: %-t: %-t.cpp $(TAP_LDIR)/libtap.so $(CXX) $< $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS) $(STATIC_LIBS) -o $@ -# $(CXX) $< $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS) -lpthread -ldl $(STATIC_LIBS) $(TAP_LDIR)/libtap.so -o $@ galera_1_timeout_count: galera_1_timeout_count.cpp $(TAP_LDIR)/libtap.so $(CXX) -DTEST_GALERA $< ../tap/SQLite3_Server.cpp $(IDIRS) $(LDIRS) $(OPT) $(OBJ) $(MYLIBSJEMALLOC) $(MYLIBS) $(STATIC_LIBS) -o $@ @@ -262,42 +274,35 @@ setparser_test: setparser_test.cpp $(TAP_LDIR)/libtap.so $(RE2_PATH)/util/test.c setparser_test2-t: setparser_test2 ln -fs setparser_test2 setparser_test2-t -setparser_test2: setparser_test2.cpp $(TAP_LDIR)/libtap.so $(PROXYSQL_LDIR)/set_parser.cpp setparser_test_common.h $(LIBCOREDUMPERAR) +setparser_test2: setparser_test2.cpp $(TAP_LDIR)/libtap.so $(PROXYSQL_LDIR)/set_parser.cpp setparser_test_common.h $(LIBPROXYSQLAR) $(LIBCOREDUMPERAR) $(CXX) $< $(PROXYSQL_LDIR)/set_parser.cpp $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS) $(LIBCOREDUMPERAR) -o $@ setparser_test3-t: setparser_test3 ln -fs setparser_test3 setparser_test3-t -setparser_test3: setparser_test3.cpp $(TAP_LDIR)/libtap.so $(PROXYSQL_LDIR)/set_parser.cpp setparser_test_common.h $(LIBCOREDUMPERAR) +setparser_test3: setparser_test3.cpp $(TAP_LDIR)/libtap.so $(PROXYSQL_LDIR)/set_parser.cpp setparser_test_common.h $(LIBPROXYSQLAR) $(LIBCOREDUMPERAR) $(CXX) -DPARSERDEBUG $< $(PROXYSQL_LDIR)/set_parser.cpp $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS) $(LIBCOREDUMPERAR) -o $@ - -CUSTOMARGS := -I$(TAP_IDIR) -I$(CURL_IDIR) -I$(SQLITE3_IDIR) -I$(PROXYSQL_IDIR) -I$(JSON_IDIR) -I$(SSL_IDIR) -CUSTOMARGS += -L$(TAP_LDIR) -L$(CURL_LDIR) -L$(SSL_LDIR) -#CUSTOMARGS += -Wl,-Bstatic -lcurl -CUSTOMARGS += -Wl,-Bdynamic -ltap -lcpp_dotenv -lcurl -lssl -lcrypto -lpthread -lz -ldl -CUSTOMARGS += $(OPT) - reg_test_3504-change_user_libmariadb_helper: reg_test_3504-change_user_helper.cpp $(TAP_LDIR)/libtap.so $(CXX) -DDISABLE_WARNING_COUNT_LOGGING $< $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS) $(STATIC_LIBS) -o $@ reg_test_3504-change_user_libmysql_helper: reg_test_3504-change_user_helper.cpp $(TAP_LDIR)/libtap.so - $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING $< -I$(TEST_MYSQL_IDIR) -I$(TEST_MYSQL_EDIR) -L$(TEST_MYSQL_LDIR) -lmysqlclient $(CUSTOMARGS) -o $@ + $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING $< -I$(TEST_MYSQL_IDIR) -I$(TEST_MYSQL_EDIR) -L$(TEST_MYSQL_LDIR) -Wl,-Bstatic -lmysqlclient -ltap_mysql57 $(CUSTOMARGS) -o $@ test_clickhouse_server_libmysql-t: test_clickhouse_server-t.cpp $(TAP_LDIR)/libtap.so - $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING $< -I$(TEST_MYSQL_IDIR) -I$(TEST_MYSQL_EDIR) -L$(TEST_MYSQL_LDIR) -lmysqlclient $(CUSTOMARGS) -o $@ + $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING $< -I$(TEST_MYSQL_IDIR) -I$(TEST_MYSQL_EDIR) -L$(TEST_MYSQL_LDIR) -lmysqlclient -ltap_mysql57 $(CUSTOMARGS) -o $@ reg_test_stmt_resultset_err_no_rows_libmysql-t: reg_test_stmt_resultset_err_no_rows-t.cpp $(TAP_LDIR)/libtap.so - $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING $< -I$(TEST_MYSQL_IDIR) -I$(TEST_MYSQL_EDIR) -L$(TEST_MYSQL_LDIR) -lmysqlclient $(CUSTOMARGS) -o $@ + $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING $< -I$(TEST_MYSQL_IDIR) -I$(TEST_MYSQL_EDIR) -L$(TEST_MYSQL_LDIR) -lmysqlclient -ltap_mysql57 $(CUSTOMARGS) -o $@ reg_test_mariadb_stmt_store_result_libmysql-t: reg_test_mariadb_stmt_store_result-t.cpp $(TAP_LDIR)/libtap.so - $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING $< -I$(TEST_MYSQL_IDIR) -I$(TEST_MYSQL_EDIR) -L$(TEST_MYSQL_LDIR) -lmysqlclient $(CUSTOMARGS) -o $@ + $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING $< -I$(TEST_MYSQL_IDIR) -I$(TEST_MYSQL_EDIR) -L$(TEST_MYSQL_LDIR) -lmysqlclient -ltap_mysql57 $(CUSTOMARGS) -o $@ reg_test_mariadb_stmt_store_result_async-t: reg_test_mariadb_stmt_store_result-t.cpp $(TAP_LDIR)/libtap.so $(CXX) -DASYNC_API $< $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS) $(STATIC_LIBS) -o $@ prepare_statement_err3024_libmysql-t: prepare_statement_err3024-t.cpp $(TAP_LDIR)/libtap.so - $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING $< -I$(TEST_MYSQL_IDIR) -I$(TEST_MYSQL_EDIR) -L$(TEST_MYSQL_LDIR) -lmysqlclient $(CUSTOMARGS) -o $@ + $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING $< -I$(TEST_MYSQL_IDIR) -I$(TEST_MYSQL_EDIR) -L$(TEST_MYSQL_LDIR) -lmysqlclient -ltap_mysql57 $(CUSTOMARGS) -o $@ prepare_statement_err3024_async-t: prepare_statement_err3024-t.cpp $(TAP_LDIR)/libtap.so $(CXX) -DASYNC_API $< $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS) $(STATIC_LIBS) -o $@ diff --git a/test/tap/tests/mysql-fast_forward-t.cpp b/test/tap/tests/mysql-fast_forward-t.cpp index 6fa29c4eb0..20b1b6df49 100644 --- a/test/tap/tests/mysql-fast_forward-t.cpp +++ b/test/tap/tests/mysql-fast_forward-t.cpp @@ -1,4 +1,3 @@ -#include #include #include #include diff --git a/test/tap/tests/prepare_statement_err3024-t.cpp b/test/tap/tests/prepare_statement_err3024-t.cpp index 3938d4cf1e..3c4a1e0645 100644 --- a/test/tap/tests/prepare_statement_err3024-t.cpp +++ b/test/tap/tests/prepare_statement_err3024-t.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include "mysql.h" #include "tap.h" @@ -56,72 +55,6 @@ static int wait_for_mysql(MYSQL *mysql, int status) { } #endif -/** - * @brief Function is required to be duplicated in the test because multiple compilations using - * 'libmysqlclient' and 'libmariadbclient'. TODO: Being able to share helper functions targetting different - * connector libraries between tap tests. - */ -int add_more_rows_test_sbtest1(int num_rows, MYSQL *mysql, bool sqlite) { - std::random_device rd; - std::mt19937 mt(rd()); - std::uniform_int_distribution dist(0.0, 9.0); - - diag("Creating %d rows in sbtest1", num_rows); - while (num_rows) { - std::stringstream q; - - if (sqlite==false) { - q << "INSERT INTO test.sbtest1 (k, c, pad) values "; - } else { - q << "INSERT INTO sbtest1 (k, c, pad) values "; - } - bool put_comma = false; - int i=0; - unsigned int cnt=5+rand()%50; - if (cnt > num_rows) cnt = num_rows; - for (i=0; i -#include #include -#include #include #include "mysql.h" -#include "mysqld_error.h" #include "tap.h" #include "command_line.h" @@ -17,18 +14,6 @@ using std::string; -std::vector split(const std::string& s, char delimiter) -{ - std::vector tokens; - std::string token; - std::istringstream tokenStream(s); - while (std::getline(tokenStream, token, delimiter)) - { - tokens.push_back(token); - } - return tokens; -} - int main(int argc, char** argv) { CommandLine cl; diff --git a/test/tap/tests/reg_test_3606-mysql_warnings-t.cpp b/test/tap/tests/reg_test_3606-mysql_warnings-t.cpp index b5a1fc3166..caa4a50678 100644 --- a/test/tap/tests/reg_test_3606-mysql_warnings-t.cpp +++ b/test/tap/tests/reg_test_3606-mysql_warnings-t.cpp @@ -10,7 +10,7 @@ * * Multistatements queries mixed with the previous ones are also properly executed. */ -#include +#include #include #include #include @@ -19,7 +19,6 @@ #include #include "mysql.h" -#include "mysqld_error.h" #include "proxysql_utils.h" diff --git a/test/tap/tests/reg_test_4402-mysql_fields-t.cpp b/test/tap/tests/reg_test_4402-mysql_fields-t.cpp index 7ab0a0bbdb..bf1c0a1c16 100644 --- a/test/tap/tests/reg_test_4402-mysql_fields-t.cpp +++ b/test/tap/tests/reg_test_4402-mysql_fields-t.cpp @@ -8,6 +8,9 @@ #include #include #include + +#include "mysql.h" + #include "tap.h" #include "command_line.h" #include "utils.h" diff --git a/test/tap/tests/reg_test_fast_forward_split_packet-t.cpp b/test/tap/tests/reg_test_fast_forward_split_packet-t.cpp index 626545055b..906c89f998 100644 --- a/test/tap/tests/reg_test_fast_forward_split_packet-t.cpp +++ b/test/tap/tests/reg_test_fast_forward_split_packet-t.cpp @@ -13,10 +13,10 @@ * iterations of 'MySQL_Session::handler', which will force the 'CONNECTING_SERVER' situation. */ -#include +#include +#include #include #include -#include #include #include "mysql.h" diff --git a/test/tap/tests/reg_test_mariadb_stmt_store_result-t.cpp b/test/tap/tests/reg_test_mariadb_stmt_store_result-t.cpp index 292f98f137..aabf1c0e27 100644 --- a/test/tap/tests/reg_test_mariadb_stmt_store_result-t.cpp +++ b/test/tap/tests/reg_test_mariadb_stmt_store_result-t.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include "mysql.h" #include "tap.h" @@ -49,72 +48,6 @@ static int wait_for_mysql(MYSQL *mysql, int status) { } #endif -/** - * @brief Function is required to be duplicated in the test because multiple compilations using - * 'libmysqlclient' and 'libmariadbclient'. TODO: Being able to share helper functions targetting different - * connector libraries between tap tests. - */ -int add_more_rows_test_sbtest1(int num_rows, MYSQL *mysql, bool sqlite) { - std::random_device rd; - std::mt19937 mt(rd()); - std::uniform_int_distribution dist(0.0, 9.0); - - diag("Creating %d rows in sbtest1", num_rows); - while (num_rows) { - std::stringstream q; - - if (sqlite==false) { - q << "INSERT INTO test.sbtest1 (k, c, pad) values "; - } else { - q << "INSERT INTO sbtest1 (k, c, pad) values "; - } - bool put_comma = false; - int i=0; - unsigned int cnt=5+rand()%50; - if (cnt > num_rows) cnt = num_rows; - for (i=0; i #include #include -#include "mysql.h" #include #include #include #include #include #include -#include -#include -#include #include +#include "mysql.h" #include "json.hpp" -#include "re2/re2.h" -#include "re2/regexp.h" #include "tap.h" #include "utils.h" diff --git a/test/tap/tests/set_testing-240.h b/test/tap/tests/set_testing-240.h index 60254da37e..9956db4c75 100644 --- a/test/tap/tests/set_testing-240.h +++ b/test/tap/tests/set_testing-240.h @@ -1,14 +1,6 @@ -std::vector split(const std::string& s, char delimiter) -{ - std::vector tokens; - std::string token; - std::istringstream tokenStream(s); - while (std::getline(tokenStream, token, delimiter)) - { - tokens.push_back(token); - } - return tokens; -} +#include +#include +#include "json.hpp" using nlohmann::json; diff --git a/test/tap/tests/set_testing-multi-t.cpp b/test/tap/tests/set_testing-multi-t.cpp index 1f85d6a95b..845ce0be9c 100644 --- a/test/tap/tests/set_testing-multi-t.cpp +++ b/test/tap/tests/set_testing-multi-t.cpp @@ -1,21 +1,18 @@ #include #include #include -#include "mysql.h" #include #include #include #include #include #include -#include #include -#include #include +#include "mysql.h" #include "json.hpp" #include "re2/re2.h" -#include "re2/regexp.h" #include "tap.h" #include "utils.h" diff --git a/test/tap/tests/set_testing.h b/test/tap/tests/set_testing.h index b93d036d44..f6c8e65309 100644 --- a/test/tap/tests/set_testing.h +++ b/test/tap/tests/set_testing.h @@ -1,14 +1,6 @@ -std::vector split(const std::string& s, char delimiter) -{ - std::vector tokens; - std::string token; - std::istringstream tokenStream(s); - while (std::getline(tokenStream, token, delimiter)) - { - tokens.push_back(token); - } - return tokens; -} +#include +#include +#include "json.hpp" using nlohmann::json; diff --git a/test/tap/tests/test_auth_methods-t.cpp b/test/tap/tests/test_auth_methods-t.cpp index 306b8e4dbd..d12aedcd7f 100644 --- a/test/tap/tests/test_auth_methods-t.cpp +++ b/test/tap/tests/test_auth_methods-t.cpp @@ -31,8 +31,8 @@ #include #include -#include "proxysql_utils.h" #include "openssl/ssl.h" +#include "json.hpp" #include "mysql.h" #include "tap.h" diff --git a/test/tap/tests/test_backend_conn_ping-t.cpp b/test/tap/tests/test_backend_conn_ping-t.cpp index 5c2be5634c..4753339433 100644 --- a/test/tap/tests/test_backend_conn_ping-t.cpp +++ b/test/tap/tests/test_backend_conn_ping-t.cpp @@ -23,12 +23,12 @@ See note on wait_timeout #include #include #include -#include #include #include #include +#include "mysql.h" #include "tap.h" #include "utils.h" #include "json.hpp" diff --git a/test/tap/tests/test_dns_cache-t.cpp b/test/tap/tests/test_dns_cache-t.cpp index 652efa2425..7e14bfa0d2 100644 --- a/test/tap/tests/test_dns_cache-t.cpp +++ b/test/tap/tests/test_dns_cache-t.cpp @@ -6,12 +6,12 @@ #include #include -#include -#include +#include #include #include #include -#include +#include +#include #include #include "mysql.h" @@ -21,18 +21,6 @@ #include "command_line.h" #include "utils.h" -std::vector split(const std::string& s, char delimiter) { - std::vector tokens {}; - std::string token {}; - std::istringstream tokenStream(s); - - while (std::getline(tokenStream, token, delimiter)) { - tokens.push_back(token); - } - - return tokens; -} - /** * @brief Extract the metrics values from the output of the admin command * 'SHOW PROMETHEUS METRICS'. diff --git a/test/tap/tests/test_flagOUT_weight-t.cpp b/test/tap/tests/test_flagOUT_weight-t.cpp index f07da9f2cc..d81ed1f55b 100644 --- a/test/tap/tests/test_flagOUT_weight-t.cpp +++ b/test/tap/tests/test_flagOUT_weight-t.cpp @@ -1,11 +1,11 @@ #include #include #include +#include #include #include #include -#include #include "mysql.h" #include "tap.h" diff --git a/test/tap/tests/test_format_utils-t.cpp b/test/tap/tests/test_format_utils-t.cpp index 252bac5f29..41ab41179a 100644 --- a/test/tap/tests/test_format_utils-t.cpp +++ b/test/tap/tests/test_format_utils-t.cpp @@ -5,11 +5,13 @@ * supplied buffer size are properly tested. */ -#include +#include +#include #include #include #include #include +#include #include "proxysql_utils.h" #include "tap.h" diff --git a/test/tap/tests/test_mysql_connect_retries-t.cpp b/test/tap/tests/test_mysql_connect_retries-t.cpp index 54dbcc8656..386400318b 100644 --- a/test/tap/tests/test_mysql_connect_retries-t.cpp +++ b/test/tap/tests/test_mysql_connect_retries-t.cpp @@ -15,11 +15,10 @@ * established. This is for regular and 'fast_forward' connections. */ +#include #include -#include #include #include -#include #include #include diff --git a/test/tap/tests/test_mysql_connect_retries_delay-t.cpp b/test/tap/tests/test_mysql_connect_retries_delay-t.cpp index 014c2eff6b..87f3cb00a9 100644 --- a/test/tap/tests/test_mysql_connect_retries_delay-t.cpp +++ b/test/tap/tests/test_mysql_connect_retries_delay-t.cpp @@ -9,17 +9,15 @@ * 5. Repeat the previous 3 points for several values. */ -#include #include +#include #include #include #include #include #include -#include #include "mysql.h" -#include "mysqld_error.h" #include "tap.h" #include "command_line.h" diff --git a/test/tap/tests/test_mysql_query_digests_stages-t.cpp b/test/tap/tests/test_mysql_query_digests_stages-t.cpp index 1533faf02e..86c208f56e 100644 --- a/test/tap/tests/test_mysql_query_digests_stages-t.cpp +++ b/test/tap/tests/test_mysql_query_digests_stages-t.cpp @@ -25,20 +25,18 @@ * * By default all types of tests are executed. */ -#include -#include -#include #include -#include #include +#include #include -#include +#include +#include +#include #include #include "json.hpp" #include "proxysql.h" #include "proxysql_utils.h" -#include "utils.h" #include "command_line.h" #include "tap.h" diff --git a/test/tap/tests/test_sqlite3_pass_exts-t.cpp b/test/tap/tests/test_sqlite3_pass_exts-t.cpp index 352866c430..7bdd69b7c3 100644 --- a/test/tap/tests/test_sqlite3_pass_exts-t.cpp +++ b/test/tap/tests/test_sqlite3_pass_exts-t.cpp @@ -12,6 +12,7 @@ */ #include +#include #include #include #include diff --git a/test/tap/tests_with_deps/deprecate_eof_support/Makefile b/test/tap/tests_with_deps/deprecate_eof_support/Makefile index b4060cdb18..7c1d69da31 100644 --- a/test/tap/tests_with_deps/deprecate_eof_support/Makefile +++ b/test/tap/tests_with_deps/deprecate_eof_support/Makefile @@ -113,8 +113,12 @@ endif OPT := $(STDCPP) -O2 -ggdb -Wl,--no-as-needed -Wl,-rpath,"../../tap" $(WGCOV) $(WASAN) -IDIRS := -I$(TAP_IDIR) -I$(RE2_IDIR) -I$(PROXYSQL_IDIR) -I$(JEMALLOC_IDIR) -I$(LIBCONFIG_IDIR) -I$(MICROHTTPD_IDIR) -I$(LIBHTTPSERVER_IDIR) -I$(CURL_IDIR) -I$(EV_IDIR) -I$(PROMETHEUS_IDIR) -I$(DOTENV_DYN_IDIR) -I$(SSL_IDIR) -I$(SQLITE3_IDIR) -I$(JSON_IDIR) -LDIRS := -L$(TAP_LDIR) -L$(RE2_LDIR) -L$(PROXYSQL_LDIR) -L$(JEMALLOC_LDIR) -L$(LIBCONFIG_LDIR) -L$(MICROHTTPD_LDIR) -L$(LIBHTTPSERVER_LDIR) -L$(CURL_LDIR) -L$(EV_LDIR) -L$(PROMETHEUS_LDIR) -L$(DOTENV_DYN_LDIR) -L$(SSL_LDIR) -L$(SQLITE3_LDIR) -L$(PCRE_LDIR) +IDIRS := -I$(TAP_IDIR) -I$(RE2_IDIR) -I$(PROXYSQL_IDIR) -I$(JEMALLOC_IDIR) -I$(LIBCONFIG_IDIR)\ + -I$(MICROHTTPD_IDIR) -I$(LIBHTTPSERVER_IDIR) -I$(CURL_IDIR) -I$(EV_IDIR) -I$(PROMETHEUS_IDIR)\ + -I$(DOTENV_DYN_IDIR) -I$(SSL_IDIR) -I$(SQLITE3_IDIR) -I$(JSON_IDIR) +LDIRS := -L$(TAP_LDIR) -L$(RE2_LDIR) -L$(PROXYSQL_LDIR) -L$(JEMALLOC_LDIR) -L$(LIBCONFIG_LDIR)\ + -L$(MICROHTTPD_LDIR) -L$(LIBHTTPSERVER_LDIR) -L$(CURL_LDIR) -L$(EV_LDIR) -L$(PROMETHEUS_LDIR)\ + -L$(DOTENV_DYN_LDIR) -L$(SSL_LDIR) -L$(SQLITE3_LDIR) -L$(PCRE_LDIR) ### main targets @@ -151,7 +155,7 @@ $(TEST_MYSQL_LDIR)/libmysqlclient.a: #tests: build_test_deps tests: $(patsubst %.cpp,%,$(wildcard *-t.cpp)) ok_packet_mixed_queries-t fwd_eof_query fwd_eof_ok_query -COMMONARGS = $(OPT) -Wl,-Bdynamic -ltap -lcpp_dotenv -lcurl -lssl -lcrypto -lz -ldl -lpthread -DGITVERSION=\"$(GIT_VERSION)\" +COMMONARGS = $(OPT) -Wl,-Bdynamic -ltap -lcpp_dotenv -lcurl -lre2 -lssl -lcrypto -lz -ldl -lpthread -DGITVERSION=\"$(GIT_VERSION)\" ok_packet_mixed_queries-t: eof_packet_mixed_queries-t.cpp $(CXX) $< $(IDIRS) $(LDIRS) -I$(MARIADB_IDIR) -L$(MARIADB_LDIR) -lmariadbclient $(COMMONARGS) -o $@ From b4eab27686e48007745f19db03e375a8938eda56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Mon, 6 May 2024 15:10:15 +0200 Subject: [PATCH 56/79] Add new TAP test 'mysql_reconnect' Tests reconnect with and without SSL for 'libmariadb' and 'libmysql'. --- test/tap/tap/utils.cpp | 21 ++ test/tap/tap/utils.h | 7 + test/tap/tests/Makefile | 8 + test/tap/tests/mysql_reconnect.cpp | 200 ++++++++++++++++++ .../eof_mixed_flags_queries-t.cpp | 21 -- 5 files changed, 236 insertions(+), 21 deletions(-) create mode 100644 test/tap/tests/mysql_reconnect.cpp diff --git a/test/tap/tap/utils.cpp b/test/tap/tap/utils.cpp index e8633536f2..94c7a6ff8d 100644 --- a/test/tap/tap/utils.cpp +++ b/test/tap/tap/utils.cpp @@ -1033,6 +1033,27 @@ int get_variable_value( return res; } +vector> get_all_bin_vec(size_t tg_size) { + vector> all_bin_strs {}; + vector bin_vec(tg_size, 0); + + for (size_t i = 0; i < tg_size; i++) { + if (i == 0) { + bin_vec[i] = 0; + for (const vector p : get_permutations(bin_vec)) { + all_bin_strs.push_back(p); + } + } + + bin_vec[i] = 1; + for (const vector p : get_permutations(bin_vec)) { + all_bin_strs.push_back(p); + } + } + + return all_bin_strs; +} + string to_string(const conn_cnf_t& cnf) { return string { string { "{" } diff --git a/test/tap/tap/utils.h b/test/tap/tap/utils.h index 17518c4f8c..6f45e5572a 100644 --- a/test/tap/tap/utils.h +++ b/test/tap/tap/utils.h @@ -424,6 +424,13 @@ std::vector> get_permutations(const std::vector& elem_set) { return result; } +/** + * @brief Generates permutations of binary vectors of the specified size. + * @param tg_size The target size of the binary vectors. + * @return The generated permutations. + */ +std::vector> get_all_bin_vec(size_t tg_size); + /** * @brief Struct holding options on how to performs connections for 'EOF' tests. */ diff --git a/test/tap/tests/Makefile b/test/tap/tests/Makefile index 1a8387f1c5..a368a5d49d 100644 --- a/test/tap/tests/Makefile +++ b/test/tap/tests/Makefile @@ -191,6 +191,8 @@ tests: tests-cpp \ setparser_test \ reg_test_3504-change_user_libmariadb_helper \ reg_test_3504-change_user_libmysql_helper \ + mysql_reconnect_libmariadb-t \ + mysql_reconnect_libmysql-t \ setparser_test2 setparser_test2-t \ setparser_test3 setparser_test3-t \ set_testing-240.csv \ @@ -289,6 +291,12 @@ reg_test_3504-change_user_libmariadb_helper: reg_test_3504-change_user_helper.cp reg_test_3504-change_user_libmysql_helper: reg_test_3504-change_user_helper.cpp $(TAP_LDIR)/libtap.so $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING $< -I$(TEST_MYSQL_IDIR) -I$(TEST_MYSQL_EDIR) -L$(TEST_MYSQL_LDIR) -Wl,-Bstatic -lmysqlclient -ltap_mysql57 $(CUSTOMARGS) -o $@ +mysql_reconnect_libmariadb-t: mysql_reconnect.cpp $(TAP_LDIR)/libtap.so + $(CXX) -DDISABLE_WARNING_COUNT_LOGGING $< $(IDIRS) $(LDIRS) $(OPT) $(MYLIBS) $(STATIC_LIBS) -o $@ + +mysql_reconnect_libmysql-t: mysql_reconnect.cpp $(TAP_LDIR)/libtap_mysql8.a + $(CXX) -DLIBMYSQL_HELPER8 -DDISABLE_WARNING_COUNT_LOGGING $< -I$(TEST_MYSQL8_IDIR) -I$(TEST_MYSQL8_EDIR) -L$(TEST_MYSQL8_LDIR) -lmysqlclient -ltap_mysql8 -lresolv $(CUSTOMARGS) -o $@ + test_clickhouse_server_libmysql-t: test_clickhouse_server-t.cpp $(TAP_LDIR)/libtap.so $(CXX) -DLIBMYSQL_HELPER -DDISABLE_WARNING_COUNT_LOGGING $< -I$(TEST_MYSQL_IDIR) -I$(TEST_MYSQL_EDIR) -L$(TEST_MYSQL_LDIR) -lmysqlclient -ltap_mysql57 $(CUSTOMARGS) -o $@ diff --git a/test/tap/tests/mysql_reconnect.cpp b/test/tap/tests/mysql_reconnect.cpp new file mode 100644 index 0000000000..8962d009d7 --- /dev/null +++ b/test/tap/tests/mysql_reconnect.cpp @@ -0,0 +1,200 @@ +/** + * @file mysql_reconnect.cpp + * @brief Check that reconnect works against ProxySQL with/without SSL enabled. + * @details The test requires to be compiled against libmariadb and libmysql. This allows to perform a + * regression test against libmysql regarding reconnect and SSL session tickets. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef LIBMYSQL_HELPER8 +#include +#else +#include "mysql.h" +#endif + +#include "utils.h" +#include "tap.h" +#include "command_line.h" + +using std::string; +using std::vector; + +struct _conn_cnf_t { + bool ssl; + bool eof; +}; + +int test_reconnect(const CommandLine& cl, const _conn_cnf_t& cnf) { + MYSQL* proxy = mysql_init(NULL); + + bool reconnect = 1; + int cflags = 0; + + if (cnf.ssl) { +#ifdef LIBMYSQL_HELPER8 + enum mysql_ssl_mode ssl_mode = SSL_MODE_REQUIRED; + mysql_options(proxy, MYSQL_OPT_SSL_MODE, &ssl_mode); +#else + mysql_ssl_set(proxy, NULL, NULL, NULL, NULL, NULL); + cflags |= CLIENT_SSL; +#endif + } + + if (cnf.eof) { + proxy->options.client_flag |= CLIENT_DEPRECATE_EOF; + } + + mysql_options(proxy, MYSQL_OPT_RECONNECT, &reconnect); + cflags |= CLIENT_REMEMBER_OPTIONS; + + const string TG_BACKEND { get_env_str("TG_BACKEND", "PROXYSQL") }; + + const char* user = cl.username; + const char* pass = cl.password; + const char* host = cl.host; + int port = cl.port; + + if (TG_BACKEND == "MYSQL") { + port = cl.mysql_port; + } + + diag( + "Creating initial conn against ProxySQL host:'%s', port:'%d', user:'%s', pass:'%s'", + cl.host, cl.port, cl.username, cl.password + ); + + if (!mysql_real_connect(proxy, host, user, pass, NULL, port, NULL, cflags)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxy)); + return EXIT_FAILURE; + } + +#ifdef LIBMYSQL_HELPER8 + void* ssl_session_data = nullptr; + + if (cnf.ssl) { + ssl_session_data = mysql_get_ssl_session_data(proxy, 0, nullptr); + if (ssl_session_data) { + mysql_options(proxy, MYSQL_OPT_SSL_SESSION_DATA, ssl_session_data); + } + } +#endif + + const char* admin_user = cl.admin_username; + const char* admin_pass = cl.admin_password; + const char* admin_host = cl.admin_host; + int admin_port = cl.admin_port; + + if (TG_BACKEND == "MYSQL") { + admin_user = cl.mysql_username; + admin_pass = cl.mysql_password; + admin_port = cl.mysql_port; + } + + MYSQL* admin = mysql_init(NULL); + + diag( + "Creating Admin conn against ProxySQL host:'%s', port:'%d', user:'%s', pass:'%s'", + admin_host, admin_port, admin_user, admin_pass + ); + if (!mysql_real_connect(admin, admin_host, admin_user, admin_pass, NULL, admin_port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin)); + return EXIT_FAILURE; + } + + pid_t pid = getpid(); + const string s_pid { std::to_string(pid) }; + + std::thread query_thread([proxy, s_pid] () { + const string query { "/* client_pid=" + s_pid + " */ SELECT SLEEP(20)" }; + int rc = mysql_query(proxy, query.c_str()); + + ok(rc != 0, "Query should exit with error rc:%d, err:'%s'", rc, mysql_error(proxy)); + + rc = mysql_query(proxy, "DO 1"); + if (rc) { + diag("Simple query failed after reconnect query:'%s', err:'%s'", "DO 1", mysql_error(proxy)); + } + + ok(rc == 0, "Second query should succeed (reconnect) rc:%d, err:'%s'", rc, mysql_error(proxy)); + }); + + const string cond_query { + TG_BACKEND == "PROXYSQL" ? + "SELECT IIF(" + "(SELECT COUNT(*) FROM stats_mysql_processlist WHERE" + " info LIKE '%client_pid=" + s_pid + "%')=1, 'TRUE', 'FALSE')" : + "SELECT IF(" + "(SELECT COUNT(*) FROM information_schema.processlist WHERE" + " info LIKE '%client_pid=" + s_pid + "%' AND state='User sleep')=1, 'TRUE', 'FALSE')" + }; + + int wres = wait_for_cond(admin, cond_query.c_str(), 60); + + const string ext_query { + TG_BACKEND == "PROXYSQL" ? + "SELECT SessionID FROM stats_mysql_processlist WHERE info LIKE '%client_pid=" + s_pid + "%'" : + "SELECT ID FROM information_schema.processlist WHERE info LIKE '%client_pid=" + s_pid + "%'" + " AND state='User sleep'" + }; + + ext_val_t ext_sess_id = mysql_query_ext_val(admin, ext_query, int64_t(0)); + + if (ext_sess_id.err != EXIT_SUCCESS) { + const string err { get_ext_val_err(admin, ext_sess_id) }; + diag("Failed getting 'SessionID' query:`%s`, err:`%s`", ext_query.c_str(), err.c_str()); + goto cleanup; + } + + { + const string kill_sess_query { "KILL CONNECTION " + std::to_string(ext_sess_id.val) }; + mysql_query(admin, kill_sess_query.c_str()); + } + +cleanup: + + { + query_thread.join(); + + mysql_close(admin); +#ifdef LIBMYSQL_HELPER8 + if (ssl_session_data) { + mysql_free_ssl_session_data(proxy, ssl_session_data); + } +#endif + mysql_close(proxy); + } + + return EXIT_SUCCESS; +} + + +int main(int argc, char** argv) { + CommandLine cl; + + if (cl.getEnv()) { + diag("Failed to get the required environmental variables."); + return EXIT_FAILURE; + } + + const auto& bin_vecs { get_all_bin_vec(2) }; + plan(bin_vecs.size() * 2); + + for (const vector vec : bin_vecs) { + _conn_cnf_t conf { vec[0], vec[1] }; + diag("Testing reconnect with config ssl:%d, eof:%d", conf.ssl, conf.eof); + + int rc = test_reconnect(cl, conf); + if (rc) { + diag("Reconnect failed, aborting further testing... rc:%d, ssl:%d, eof:%d", rc, conf.ssl, conf.eof); + break; + } + } + + return exit_status(); +} diff --git a/test/tap/tests_with_deps/deprecate_eof_support/eof_mixed_flags_queries-t.cpp b/test/tap/tests_with_deps/deprecate_eof_support/eof_mixed_flags_queries-t.cpp index f6ce4b329c..556d77ba50 100644 --- a/test/tap/tests_with_deps/deprecate_eof_support/eof_mixed_flags_queries-t.cpp +++ b/test/tap/tests_with_deps/deprecate_eof_support/eof_mixed_flags_queries-t.cpp @@ -28,27 +28,6 @@ using std::string; using std::vector; -vector> get_all_bin_vec(size_t tg_size) { - vector> all_bin_strs {}; - vector bin_vec(tg_size, 0); - - for (size_t i = 0; i < tg_size; i++) { - if (i == 0) { - bin_vec[i] = 0; - for (const vector p : get_permutations(bin_vec)) { - all_bin_strs.push_back(p); - } - } - - bin_vec[i] = 1; - for (const vector p : get_permutations(bin_vec)) { - all_bin_strs.push_back(p); - } - } - - return all_bin_strs; -} - vector gen_all_configs(const string& ff_user) { vector> all_bin_vec { get_all_bin_vec(5) }; std::sort(all_bin_vec.begin(), all_bin_vec.end()); From 3316c8ffe1294b25037fb0868320d80a4c747d71 Mon Sep 17 00:00:00 2001 From: Miro Stauder Date: Fri, 10 May 2024 21:28:48 +0200 Subject: [PATCH 57/79] change gh actions conurency --- .github/workflows/CI-3p-django-framework.yml | 3 ++- .github/workflows/CI-3p-laravel-framework.yml | 1 + .github/workflows/CI-3p-mariadb-connector-c.yml | 1 + .github/workflows/CI-3p-mysql-connector-j.yml | 1 + .github/workflows/CI-3p-php-pdo-mysql.yml | 1 + .github/workflows/CI-3p-sqlalchemy.yml | 1 + .github/workflows/CI-basictests.yml | 1 + .github/workflows/CI-builds.yml | 1 + .github/workflows/CI-codeql.yml | 1 + .github/workflows/CI-maketest.yml | 1 + .github/workflows/CI-package-build.yml | 1 + .github/workflows/CI-repltests.yml | 1 + .github/workflows/CI-selftests.yml | 1 + .github/workflows/CI-shuntest.yml | 1 + .github/workflows/CI-taptests-asan.yml | 1 + .github/workflows/CI-taptests-groups.yml | 1 + .github/workflows/CI-taptests-ssl.yml | 1 + .github/workflows/CI-taptests.yml | 1 + 18 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/workflows/CI-3p-django-framework.yml b/.github/workflows/CI-3p-django-framework.yml index c62a3f4c09..8c50f9cf5b 100644 --- a/.github/workflows/CI-3p-django-framework.yml +++ b/.github/workflows/CI-3p-django-framework.yml @@ -17,7 +17,8 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} - + cancel-in-progress: true + jobs: run: uses: sysown/proxysql/.github/workflows/ci-3p-django-framework.yml@GH-Actions diff --git a/.github/workflows/CI-3p-laravel-framework.yml b/.github/workflows/CI-3p-laravel-framework.yml index 48b6246d13..fb6f3a8c70 100644 --- a/.github/workflows/CI-3p-laravel-framework.yml +++ b/.github/workflows/CI-3p-laravel-framework.yml @@ -17,6 +17,7 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} + cancel-in-progress: true jobs: run: diff --git a/.github/workflows/CI-3p-mariadb-connector-c.yml b/.github/workflows/CI-3p-mariadb-connector-c.yml index ad8062e036..048f94f899 100644 --- a/.github/workflows/CI-3p-mariadb-connector-c.yml +++ b/.github/workflows/CI-3p-mariadb-connector-c.yml @@ -17,6 +17,7 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} + cancel-in-progress: true jobs: run: diff --git a/.github/workflows/CI-3p-mysql-connector-j.yml b/.github/workflows/CI-3p-mysql-connector-j.yml index 2389589849..d327a9f506 100644 --- a/.github/workflows/CI-3p-mysql-connector-j.yml +++ b/.github/workflows/CI-3p-mysql-connector-j.yml @@ -17,6 +17,7 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} + cancel-in-progress: true jobs: run: diff --git a/.github/workflows/CI-3p-php-pdo-mysql.yml b/.github/workflows/CI-3p-php-pdo-mysql.yml index a0f88244f9..207ff1bdb9 100644 --- a/.github/workflows/CI-3p-php-pdo-mysql.yml +++ b/.github/workflows/CI-3p-php-pdo-mysql.yml @@ -17,6 +17,7 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} + cancel-in-progress: true jobs: run: diff --git a/.github/workflows/CI-3p-sqlalchemy.yml b/.github/workflows/CI-3p-sqlalchemy.yml index 83a937fae0..335fb88bac 100644 --- a/.github/workflows/CI-3p-sqlalchemy.yml +++ b/.github/workflows/CI-3p-sqlalchemy.yml @@ -17,6 +17,7 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} + cancel-in-progress: true jobs: run: diff --git a/.github/workflows/CI-basictests.yml b/.github/workflows/CI-basictests.yml index 1195a0ac36..9ff6bc13e2 100644 --- a/.github/workflows/CI-basictests.yml +++ b/.github/workflows/CI-basictests.yml @@ -17,6 +17,7 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} + cancel-in-progress: true jobs: run: diff --git a/.github/workflows/CI-builds.yml b/.github/workflows/CI-builds.yml index 0afd290b79..020606da9f 100644 --- a/.github/workflows/CI-builds.yml +++ b/.github/workflows/CI-builds.yml @@ -17,6 +17,7 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} + cancel-in-progress: true jobs: run: diff --git a/.github/workflows/CI-codeql.yml b/.github/workflows/CI-codeql.yml index 1d71497b44..b283cb7bee 100644 --- a/.github/workflows/CI-codeql.yml +++ b/.github/workflows/CI-codeql.yml @@ -17,6 +17,7 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} + cancel-in-progress: true jobs: run: diff --git a/.github/workflows/CI-maketest.yml b/.github/workflows/CI-maketest.yml index 8c2b25a97b..5194374f8a 100644 --- a/.github/workflows/CI-maketest.yml +++ b/.github/workflows/CI-maketest.yml @@ -17,6 +17,7 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} + cancel-in-progress: true jobs: run: diff --git a/.github/workflows/CI-package-build.yml b/.github/workflows/CI-package-build.yml index 5ee9b902d8..2b68ee483c 100644 --- a/.github/workflows/CI-package-build.yml +++ b/.github/workflows/CI-package-build.yml @@ -17,6 +17,7 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} + cancel-in-progress: true jobs: run: diff --git a/.github/workflows/CI-repltests.yml b/.github/workflows/CI-repltests.yml index ea067e70fc..c4d2adbb05 100644 --- a/.github/workflows/CI-repltests.yml +++ b/.github/workflows/CI-repltests.yml @@ -17,6 +17,7 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} + cancel-in-progress: true jobs: run: diff --git a/.github/workflows/CI-selftests.yml b/.github/workflows/CI-selftests.yml index 1e9bab45e0..93f96712c7 100644 --- a/.github/workflows/CI-selftests.yml +++ b/.github/workflows/CI-selftests.yml @@ -17,6 +17,7 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} + cancel-in-progress: true jobs: run: diff --git a/.github/workflows/CI-shuntest.yml b/.github/workflows/CI-shuntest.yml index 406ac6af30..724eec8467 100644 --- a/.github/workflows/CI-shuntest.yml +++ b/.github/workflows/CI-shuntest.yml @@ -17,6 +17,7 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} + cancel-in-progress: true jobs: run: diff --git a/.github/workflows/CI-taptests-asan.yml b/.github/workflows/CI-taptests-asan.yml index b17d881eae..fd819701ae 100644 --- a/.github/workflows/CI-taptests-asan.yml +++ b/.github/workflows/CI-taptests-asan.yml @@ -17,6 +17,7 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} + cancel-in-progress: true jobs: run: diff --git a/.github/workflows/CI-taptests-groups.yml b/.github/workflows/CI-taptests-groups.yml index d5d2d2459e..b3cc108e8f 100644 --- a/.github/workflows/CI-taptests-groups.yml +++ b/.github/workflows/CI-taptests-groups.yml @@ -17,6 +17,7 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} + cancel-in-progress: true jobs: run: diff --git a/.github/workflows/CI-taptests-ssl.yml b/.github/workflows/CI-taptests-ssl.yml index c1dfc1a333..0c903a72d1 100644 --- a/.github/workflows/CI-taptests-ssl.yml +++ b/.github/workflows/CI-taptests-ssl.yml @@ -17,6 +17,7 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} + cancel-in-progress: true jobs: run: diff --git a/.github/workflows/CI-taptests.yml b/.github/workflows/CI-taptests.yml index 5749c3fc8a..b9531eb825 100644 --- a/.github/workflows/CI-taptests.yml +++ b/.github/workflows/CI-taptests.yml @@ -17,6 +17,7 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} + cancel-in-progress: true jobs: run: From cf7da4e9a238a53523844cd514f2f759512e9822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Tue, 14 May 2024 09:01:54 +0200 Subject: [PATCH 58/79] Fix memory leak in 'pull_mysql_query_rules_from_peer' - Simplified memory managament in Cluster credentials API. - Moved several variables into local scopes. --- include/ProxySQL_Cluster.hpp | 7 +- lib/ProxySQL_Cluster.cpp | 177 +++++++++++++++-------------------- 2 files changed, 81 insertions(+), 103 deletions(-) diff --git a/include/ProxySQL_Cluster.hpp b/include/ProxySQL_Cluster.hpp index eab5292cce..92d81efdec 100644 --- a/include/ProxySQL_Cluster.hpp +++ b/include/ProxySQL_Cluster.hpp @@ -378,6 +378,11 @@ struct fetch_query { std::string msgs[3]; }; +struct cluster_creds_t { + string user; + string pass; +}; + class ProxySQL_Cluster { private: SQLite3DB* mydb; @@ -444,7 +449,7 @@ class ProxySQL_Cluster { MySQL_Monitor::trigger_dns_cache_update(); } - void get_credentials(char**, char**); + cluster_creds_t get_credentials(); void set_username(char*); void set_password(char*); void set_admin_mysql_ifaces(char*); diff --git a/lib/ProxySQL_Cluster.cpp b/lib/ProxySQL_Cluster.cpp index 514182b491..f805ed9dab 100644 --- a/lib/ProxySQL_Cluster.cpp +++ b/lib/ProxySQL_Cluster.cpp @@ -63,8 +63,6 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { 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 query_error_counter = 0; char *query_error = NULL; @@ -76,13 +74,9 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { goto __exit_monitor_thread; } 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); - if (strlen(username)) { // do not monitor if the username is empty + cluster_creds_t creds { GloProxyCluster->get_credentials() }; + + if (creds.user.size()) { // do not monitor if the username is empty unsigned int timeout = 1; // unsigned int timeout_long = 60; if (conn == NULL) { @@ -101,14 +95,14 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { } //rc_conn = mysql_real_connect(conn, node->hostname, username, password, NULL, node->port, NULL, CLIENT_COMPRESS); // FIXME: add optional support for compression proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Connecting to peer %s:%d\n", node->hostname, node->port); - rc_conn = mysql_real_connect(conn, node->get_host_address(), username, password, NULL, node->port, NULL, 0); + MYSQL* rc_conn = mysql_real_connect(conn, node->get_host_address(), creds.user.c_str(), creds.pass.c_str(), NULL, node->port, NULL, 0); // if (rc_conn) { // } //char *query = query1; if (rc_conn) { MySQL_Monitor::update_dns_cache_from_mysql_conn(conn); - rc_query = mysql_query(conn,(char *)"SELECT @@version"); + int rc_query = mysql_query(conn,(char *)"SELECT @@version"); if (rc_query == 0) { query_error = NULL; query_error_counter = 0; @@ -159,7 +153,7 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { 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); - update_checksum = GloProxyCluster->Update_Global_Checksum(node->hostname, node->port, result); + bool update_checksum = GloProxyCluster->Update_Global_Checksum(node->hostname, node->port, result); mysql_free_result(result); // 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); @@ -202,7 +196,10 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { if (query_error_counter == 0) { unsigned long long after_query_time=monotonic_time(); unsigned long long elapsed_time_us = (after_query_time - before_query_time); - proxy_error("Cluster: unable to run query on %s:%d using user %s after %llums : %s . Error: %s\n", node->hostname, node->port , username, elapsed_time_us/1000 , query_error, mysql_error(conn)); + proxy_error( + "Cluster: unable to run query on %s:%d using user %s after %llums : %s . Error: %s\n", + node->hostname, node->port, creds.user.c_str(), elapsed_time_us/1000, query_error, mysql_error(conn) + ); } if (++query_error_counter == QUERY_ERROR_RATE) query_error_counter = 0; } @@ -235,7 +232,10 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { if (query_error_counter == 0) { unsigned long long after_query_time=monotonic_time(); unsigned long long elapsed_time_us = (after_query_time - before_query_time); - proxy_error("Cluster: unable to run query on %s:%d using user %s after %llums : %s . Error: %s\n", node->hostname, node->port , username, elapsed_time_us/1000 , query_error, mysql_error(conn)); + proxy_error( + "Cluster: unable to run query on %s:%d using user %s after %llums : %s . Error: %s\n", + node->hostname, node->port, creds.user.c_str(), elapsed_time_us/1000, query_error, mysql_error(conn) + ); } if (++query_error_counter == QUERY_ERROR_RATE) query_error_counter = 0; } @@ -246,7 +246,10 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { if (query_error_counter == 0) { unsigned long long after_query_time=monotonic_time(); unsigned long long elapsed_time_us = (after_query_time - start_time); - proxy_error("Cluster: unable to run query on %s:%d using user %s after %llums : %s . Error: %s\n", node->hostname, node->port , username, elapsed_time_us/1000, query_error, mysql_error(conn)); + proxy_error( + "Cluster: unable to run query on %s:%d using user %s after %llums : %s . Error: %s\n", + node->hostname, node->port, creds.user.c_str(), elapsed_time_us/1000, query_error, mysql_error(conn) + ); } if (++query_error_counter == QUERY_ERROR_RATE) query_error_counter = 0; } @@ -1176,19 +1179,16 @@ void ProxySQL_Cluster::pull_mysql_query_rules_from_peer(const string& expected_c pthread_mutex_lock(&GloProxyCluster->update_mysql_query_rules_mutex); nodes.get_peer_to_sync_mysql_query_rules(&hostname, &port, &ip_address); if (hostname) { - char *username = NULL; - char *password = NULL; - // bool rc_bool = true; - MYSQL *rc_conn; - int rc_query; - int rc; + cluster_creds_t creds {}; + 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 + + creds = GloProxyCluster->get_credentials(); + if (creds.user.size()) { // 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); @@ -1200,16 +1200,18 @@ void ProxySQL_Cluster::pull_mysql_query_rules_from_peer(const string& expected_c } proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching MySQL Query Rules from peer %s:%d started. Expected checksum: %s\n", hostname, port, expected_checksum.c_str()); proxy_info("Cluster: Fetching MySQL Query Rules from peer %s:%d started. Expected checksum: %s\n", hostname, port, expected_checksum.c_str()); - rc_conn = mysql_real_connect(conn, ip_address ? ip_address : hostname, username, password, NULL, port, NULL, 0); + MYSQL* rc_conn = mysql_real_connect( + conn, ip_address ? ip_address : hostname, creds.user.c_str(), creds.pass.c_str(), NULL, port, NULL, 0 + ); if (rc_conn) { MySQL_Monitor::update_dns_cache_from_mysql_conn(conn); MYSQL_RES *result1 = NULL; MYSQL_RES *result2 = NULL; //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, cache_empty_result, cache_timeout, reconnect, timeout, retries, delay, next_query_flagIN, mirror_flagOUT, mirror_hostgroup, error_msg, ok_msg, sticky_conn, multiplex, gtid_from_hostgroup, log, apply, attributes, comment FROM runtime_mysql_query_rules"); - rc_query = mysql_query(conn,CLUSTER_QUERY_MYSQL_QUERY_RULES); + int rc_query = mysql_query(conn,CLUSTER_QUERY_MYSQL_QUERY_RULES); if ( rc_query == 0 ) { - MYSQL_RES *result1 = mysql_store_result(conn); + result1 = mysql_store_result(conn); rc_query = mysql_query(conn,CLUSTER_QUERY_MYSQL_QUERY_RULES_FAST_ROUTING); if ( rc_query == 0) { result2 = mysql_store_result(conn); @@ -1237,7 +1239,7 @@ void ProxySQL_Cluster::pull_mysql_query_rules_from_peer(const string& expected_c sqlite3_stmt *statement1 = NULL; //sqlite3 *mydb3 = GloAdmin->admindb->get_db(); //rc=(*proxy_sqlite3_prepare_v2)(mydb3, q, -1, &statement1, 0); - rc = GloAdmin->admindb->prepare_v2(q, &statement1); + int rc = GloAdmin->admindb->prepare_v2(q, &statement1); ASSERT_SQLITE_OK(rc, GloAdmin->admindb); GloAdmin->admindb->execute("BEGIN TRANSACTION"); while ((row = mysql_fetch_row(result1))) { @@ -1470,17 +1472,16 @@ void ProxySQL_Cluster::pull_mysql_users_from_peer(const string& expected_checksu pthread_mutex_lock(&GloProxyCluster->update_mysql_users_mutex); nodes.get_peer_to_sync_mysql_users(&hostname, &port, &ip_address); if (hostname) { - char *username = NULL; - char *password = NULL; - MYSQL *rc_conn; - int rc_query; + cluster_creds_t creds {}; + 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 + + creds = GloProxyCluster->get_credentials(); + if (creds.user.size()) { // 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); @@ -1493,7 +1494,7 @@ void ProxySQL_Cluster::pull_mysql_users_from_peer(const string& expected_checksu proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching MySQL Users from peer %s:%d started. Expected checksum: %s\n", hostname, port, expected_checksum.c_str()); proxy_info("Cluster: Fetching MySQL Users from peer %s:%d started. Expected checksum: %s\n", hostname, port, expected_checksum.c_str()); - rc_conn = mysql_real_connect(conn, ip_address ? ip_address : hostname, username, password, NULL, port, NULL, 0); + MYSQL* rc_conn = mysql_real_connect(conn, ip_address ? ip_address : hostname, creds.user.c_str(), creds.pass.c_str(), NULL, port, NULL, 0); if (rc_conn == nullptr) { proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching MySQL Users from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); proxy_info("Cluster: Fetching MySQL Users from peer %s:%d failed: %s\n", hostname, port, mysql_error(conn)); @@ -1512,7 +1513,7 @@ void ProxySQL_Cluster::pull_mysql_users_from_peer(const string& expected_checksu MySQL_Monitor::update_dns_cache_from_mysql_conn(conn); - rc_query = mysql_query(conn, CLUSTER_QUERY_MYSQL_USERS); + int rc_query = mysql_query(conn, CLUSTER_QUERY_MYSQL_USERS); if (rc_query == 0) { MYSQL_RES* mysql_users_result = mysql_store_result(conn); MYSQL_RES* ldap_mapping_result = nullptr; @@ -1621,12 +1622,6 @@ void ProxySQL_Cluster::pull_mysql_users_from_peer(const string& expected_checksu } } } - if (username) { - free(username); - } - if (password) { - free(password); - } __exit_pull_mysql_users_from_peer: if (conn) { if (conn->net.pvio) { @@ -1771,17 +1766,16 @@ void ProxySQL_Cluster::pull_runtime_mysql_servers_from_peer(const runtime_mysql_ pthread_mutex_lock(&GloProxyCluster->update_runtime_mysql_servers_mutex); nodes.get_peer_to_sync_runtime_mysql_servers(&hostname, &port, &peer_checksum, &ip_address); if (hostname) { - char *username = NULL; - char *password = NULL; - // bool rc_bool = true; - MYSQL *rc_conn; + cluster_creds_t creds {}; + 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 + + creds = GloProxyCluster->get_credentials(); + if (creds.user.size()) { // do not monitor if the username is empty unsigned int timeout = 1; mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); { @@ -1790,7 +1784,9 @@ void ProxySQL_Cluster::pull_runtime_mysql_servers_from_peer(const runtime_mysql_ } proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching 'MySQL Servers' from peer %s:%d started. Expected checksum %s\n", hostname, port, peer_checksum); proxy_info("Cluster: Fetching 'MySQL Servers' from peer %s:%d started. Expected checksum %s\n", hostname, port, peer_checksum); - rc_conn = mysql_real_connect(conn, ip_address ? ip_address : hostname, username, password, NULL, port, NULL, 0); + MYSQL* rc_conn = mysql_real_connect( + conn, ip_address ? ip_address : hostname, creds.user.c_str(), creds.pass.c_str(), NULL, port, NULL, 0 + ); if (rc_conn) { MySQL_Monitor::update_dns_cache_from_mysql_conn(conn); @@ -1856,12 +1852,6 @@ void ProxySQL_Cluster::pull_runtime_mysql_servers_from_peer(const runtime_mysql_ fetch_failed = true; } } - if (username) { - free(username); - } - if (password) { - free(password); - } __exit_pull_mysql_servers_from_peer: if (conn) { if (conn->net.pvio) { @@ -1924,17 +1914,16 @@ void ProxySQL_Cluster::pull_mysql_servers_v2_from_peer(const mysql_servers_v2_ch nodes.get_peer_to_sync_mysql_servers_v2(&hostname, &port, &peer_mysql_servers_v2_checksum, &peer_runtime_mysql_servers_checksum, &ip_address); if (hostname) { - char* username = NULL; - char* password = NULL; - // bool rc_bool = true; - MYSQL* rc_conn; + cluster_creds_t creds {}; + MYSQL* conn = mysql_init(NULL); if (conn == NULL) { proxy_error("Unable to run mysql_init()\n"); goto __exit_pull_mysql_servers_v2_from_peer; } - GloProxyCluster->get_credentials(&username, &password); - if (strlen(username)) { // do not monitor if the username is empty + + creds = GloProxyCluster->get_credentials(); + if (creds.user.size()) { // 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); @@ -1946,7 +1935,9 @@ void ProxySQL_Cluster::pull_mysql_servers_v2_from_peer(const mysql_servers_v2_ch } proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Fetching MySQL Servers v2 from peer %s:%d started. Expected checksum %s\n", hostname, port, peer_mysql_servers_v2_checksum); proxy_info("Cluster: Fetching MySQL Servers v2 from peer %s:%d started. Expected checksum %s\n", hostname, port, peer_mysql_servers_v2_checksum); - rc_conn = mysql_real_connect(conn, ip_address ? ip_address : hostname, username, password, NULL, port, NULL, 0); + MYSQL* rc_conn = mysql_real_connect( + conn, ip_address ? ip_address : hostname, creds.user.c_str(), creds.pass.c_str(), NULL, port, NULL, 0 + ); if (rc_conn) { MySQL_Monitor::update_dns_cache_from_mysql_conn(conn); @@ -2395,12 +2386,6 @@ void ProxySQL_Cluster::pull_mysql_servers_v2_from_peer(const mysql_servers_v2_ch fetch_failed = true; } } - if (username) { - free(username); - } - if (password) { - free(password); - } __exit_pull_mysql_servers_v2_from_peer: if (conn) { if (conn->net.pvio) { @@ -2461,20 +2446,16 @@ void ProxySQL_Cluster::pull_global_variables_from_peer(const string& var_type, c } if (hostname) { - char *username = NULL; - char *password = NULL; - MYSQL *rc_conn = nullptr; - int rc_query = 0; - int rc = 0; - MYSQL *conn = mysql_init(NULL); + cluster_creds_t creds {}; + MYSQL *conn = mysql_init(NULL); if (conn == NULL) { proxy_error("Unable to run mysql_init()\n"); goto __exit_pull_mysql_variables_from_peer; } - GloProxyCluster->get_credentials(&username, &password); - if (strlen(username)) { // do not monitor if the username is empty + creds = GloProxyCluster->get_credentials(); + if (creds.user.size()) { // 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); @@ -2485,7 +2466,9 @@ void ProxySQL_Cluster::pull_global_variables_from_peer(const string& var_type, c mysql_options(conn, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); } proxy_info("Cluster: Fetching %s variables from peer %s:%d started\n", vars_type_str, hostname, port); - rc_conn = mysql_real_connect(conn, ip_address ? ip_address : hostname, username, password, NULL, port, NULL, 0); + MYSQL* rc_conn = mysql_real_connect( + conn, ip_address ? ip_address : hostname, creds.user.c_str(), creds.pass.c_str(), NULL, port, NULL, 0 + ); if (rc_conn) { MySQL_Monitor::update_dns_cache_from_mysql_conn(conn); @@ -2503,7 +2486,7 @@ void ProxySQL_Cluster::pull_global_variables_from_peer(const string& var_type, c } } s_query += " ORDER BY variable_name"; - mysql_query(conn, s_query.c_str()); + int rc_query = mysql_query(conn, s_query.c_str()); if (rc_query == 0) { MYSQL_RES *result = mysql_store_result(conn); @@ -2535,7 +2518,7 @@ void ProxySQL_Cluster::pull_global_variables_from_peer(const string& var_type, c MYSQL_ROW row; char *q = (char *)"INSERT OR REPLACE INTO global_variables (variable_name, variable_value) VALUES (?1 , ?2)"; sqlite3_stmt *statement1 = NULL; - rc = GloAdmin->admindb->prepare_v2(q, &statement1); + int rc = GloAdmin->admindb->prepare_v2(q, &statement1); ASSERT_SQLITE_OK(rc, GloAdmin->admindb); while ((row = mysql_fetch_row(result))) { @@ -2604,12 +2587,6 @@ void ProxySQL_Cluster::pull_global_variables_from_peer(const string& var_type, c fetch_failed = true; } } - if (username) { - free(username); - } - if (password) { - free(password); - } __exit_pull_mysql_variables_from_peer: if (conn) { if (conn->net.pvio) { @@ -2633,18 +2610,16 @@ void ProxySQL_Cluster::pull_proxysql_servers_from_peer(const std::string& expect pthread_mutex_lock(&GloProxyCluster->update_proxysql_servers_mutex); nodes.get_peer_to_sync_proxysql_servers(&hostname, &port, &ip_address); if (hostname) { - char *username = NULL; - char *password = NULL; - // bool rc_bool = true; - MYSQL *rc_conn; - int rc_query; + cluster_creds_t creds {}; + 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 + + creds = GloProxyCluster->get_credentials(); + if (creds.user.size()) { // 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); @@ -2660,11 +2635,13 @@ void ProxySQL_Cluster::pull_proxysql_servers_from_peer(const std::string& expect "Cluster: Fetching ProxySQL Servers from peer %s:%d started. Expected checksum: %s\n", hostname, port, expected_checksum.c_str() ); - rc_conn = mysql_real_connect(conn, ip_address ? ip_address : hostname, username, password, NULL, port, NULL, 0); + MYSQL* rc_conn = mysql_real_connect( + conn, ip_address ? ip_address : hostname, creds.user.c_str(), creds.pass.c_str(), NULL, port, NULL, 0 + ); if (rc_conn) { MySQL_Monitor::update_dns_cache_from_mysql_conn(conn); - rc_query = mysql_query(conn,"SELECT hostname, port, weight, comment FROM runtime_proxysql_servers ORDER BY hostname, port"); + int rc_query = mysql_query(conn,"SELECT hostname, port, weight, comment FROM runtime_proxysql_servers ORDER BY hostname, port"); if ( rc_query == 0 ) { MYSQL_RES* result = mysql_store_result(conn); uint64_t proxy_servers_hash = mysql_raw_checksum(result); @@ -2738,12 +2715,6 @@ void ProxySQL_Cluster::pull_proxysql_servers_from_peer(const std::string& expect fetch_failed = true; } } - if (username) { - free(username); - } - if (password) { - free(password); - } __exit_pull_proxysql_servers_from_peer: if (conn) { if (conn->net.pvio) { @@ -4467,11 +4438,13 @@ void ProxySQL_Cluster::p_update_metrics() { }; // this function returns credentials to the caller, used by monitoring threads -void ProxySQL_Cluster::get_credentials(char **username, char **password) { +cluster_creds_t ProxySQL_Cluster::get_credentials() { pthread_mutex_lock(&mutex); - *username = strdup(cluster_username); - *password = strdup(cluster_password); + const string user { cluster_username }; + const string pass { cluster_password }; pthread_mutex_unlock(&mutex); + + return { user, pass }; } void ProxySQL_Cluster::set_username(char *_username) { From 317ef31e5a57852aab27608856b8aedccbf49ab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Tue, 14 May 2024 09:39:37 +0200 Subject: [PATCH 59/79] Fix memory leak on GTID 'new_connector' --- lib/GTID_Server_Data.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/GTID_Server_Data.cpp b/lib/GTID_Server_Data.cpp index 6dbf572354..528059b2da 100644 --- a/lib/GTID_Server_Data.cpp +++ b/lib/GTID_Server_Data.cpp @@ -142,6 +142,7 @@ struct ev_io * new_connector(char *address, uint16_t gtid_port, uint16_t mysql_p char str_port[NI_MAXSERV+1]; sprintf(str_port,"%d", gtid_port); + int gai_rc = getaddrinfo(address, str_port, &hints, &res); if (gai_rc) { freeaddrinfo(res); @@ -151,6 +152,10 @@ struct ev_io * new_connector(char *address, uint16_t gtid_port, uint16_t mysql_p //int status = connect(s, (struct sockaddr *) &a, sizeof(a)); int status = connect(s, res->ai_addr, res->ai_addrlen); + + // Free linked list + freeaddrinfo(res); + if ((status == 0) || ((status == -1) && (errno == EINPROGRESS))) { struct ev_io *c = (struct ev_io *)malloc(sizeof(struct ev_io)); if (c) { From 742ad8a2ce22f2b5fe9271981d5e94a8114df28f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Tue, 14 May 2024 09:40:36 +0200 Subject: [PATCH 60/79] Fix memory leak on 'pull_mysql_query_rules_from_peer' statements --- lib/ProxySQL_Cluster.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/ProxySQL_Cluster.cpp b/lib/ProxySQL_Cluster.cpp index f805ed9dab..52409d30a6 100644 --- a/lib/ProxySQL_Cluster.cpp +++ b/lib/ProxySQL_Cluster.cpp @@ -1282,6 +1282,7 @@ void ProxySQL_Cluster::pull_mysql_query_rules_from_peer(const string& expected_c rc=(*proxy_sqlite3_clear_bindings)(statement1); ASSERT_SQLITE_OK(rc, GloAdmin->admindb); rc=(*proxy_sqlite3_reset)(statement1); ASSERT_SQLITE_OK(rc, GloAdmin->admindb); } + (*proxy_sqlite3_finalize)(statement1); GloAdmin->admindb->execute("COMMIT"); @@ -1325,6 +1326,8 @@ void ProxySQL_Cluster::pull_mysql_query_rules_from_peer(const string& expected_c } row_idx++; } + (*proxy_sqlite3_finalize)(statement1fr); + (*proxy_sqlite3_finalize)(statement32fr); //GloAdmin->admindb->execute("PRAGMA integrity_check"); GloAdmin->admindb->execute("COMMIT"); From 3e8d672c33c61e5b1be51076ce3705650e443cf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Tue, 14 May 2024 09:42:26 +0200 Subject: [PATCH 61/79] Remove commented code from original GTID impl --- lib/GTID_Server_Data.cpp | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/lib/GTID_Server_Data.cpp b/lib/GTID_Server_Data.cpp index 528059b2da..d721bfd1b0 100644 --- a/lib/GTID_Server_Data.cpp +++ b/lib/GTID_Server_Data.cpp @@ -113,7 +113,6 @@ void connect_cb(EV_P_ ev_io *w, int revents) { } struct ev_io * new_connector(char *address, uint16_t gtid_port, uint16_t mysql_port) { - //struct sockaddr_in a; int s; if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) { @@ -121,16 +120,7 @@ struct ev_io * new_connector(char *address, uint16_t gtid_port, uint16_t mysql_p close(s); return NULL; } -/* - memset(&a, 0, sizeof(a)); - a.sin_port = htons(gtid_port); - a.sin_family = AF_INET; - if (!inet_aton(address, (struct in_addr *) &a.sin_addr.s_addr)) { - perror("bad IP address format"); - close(s); - return NULL; - } -*/ + ioctl_FIONBIO(s,1); struct addrinfo hints; @@ -150,7 +140,6 @@ struct ev_io * new_connector(char *address, uint16_t gtid_port, uint16_t mysql_p return NULL; } - //int status = connect(s, (struct sockaddr *) &a, sizeof(a)); int status = connect(s, res->ai_addr, res->ai_addrlen); // Free linked list From d9430ff333b04893922e9760e424c0bc3a9bf78c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Tue, 14 May 2024 09:51:29 +0200 Subject: [PATCH 62/79] Remove commented code from original Cluster POC --- lib/ProxySQL_Cluster.cpp | 44 +++++----------------------------------- 1 file changed, 5 insertions(+), 39 deletions(-) diff --git a/lib/ProxySQL_Cluster.cpp b/lib/ProxySQL_Cluster.cpp index 52409d30a6..a0ada5cd5f 100644 --- a/lib/ProxySQL_Cluster.cpp +++ b/lib/ProxySQL_Cluster.cpp @@ -68,7 +68,7 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { char *query_error = NULL; int cluster_check_status_frequency_count = 0; MYSQL *conn = mysql_init(NULL); -// goto __exit_monitor_thread; + if (conn==NULL) { proxy_error("Unable to run mysql_init()\n"); goto __exit_monitor_thread; @@ -93,12 +93,10 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { unsigned char val = 1; mysql_options(conn, MYSQL_OPT_SSL_ENFORCE, &val); mysql_options(conn, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); } - //rc_conn = mysql_real_connect(conn, node->hostname, username, password, NULL, node->port, NULL, CLIENT_COMPRESS); // FIXME: add optional support for compression + // FIXME: add optional support for compression proxy_debug(PROXY_DEBUG_CLUSTER, 5, "Connecting to peer %s:%d\n", node->hostname, node->port); MYSQL* rc_conn = mysql_real_connect(conn, node->get_host_address(), creds.user.c_str(), creds.pass.c_str(), NULL, node->port, NULL, 0); -// if (rc_conn) { -// } - //char *query = query1; + if (rc_conn) { MySQL_Monitor::update_dns_cache_from_mysql_conn(conn); @@ -145,7 +143,7 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { } while ( glovars.shutdown == 0 && rc_query == 0 && rc_bool == true) { 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 ) { query_error = NULL; @@ -157,19 +155,7 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { mysql_free_result(result); // 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); - 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) { unsigned long long before_query_time=monotonic_time(); rc_query = mysql_query(conn,query3); @@ -177,20 +163,8 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { query_error = NULL; query_error_counter = 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 { query_error = query3; if (query_error_counter == 0) { @@ -205,12 +179,6 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { } } 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++; @@ -293,9 +261,7 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { } proxy_info("Cluster: closing thread for peer %s:%d\n", node->hostname, node->port); delete node; - //pthread_exit(0); mysql_thread_end(); - //GloProxyCluster->thread_ending(node->thrid); __sync_fetch_and_sub(&GloVars.statuses.stack_memory_cluster_threads,tmp_stack_size); From 2b1d576726cf17872a35395c9250c792dd978462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Tue, 14 May 2024 10:04:09 +0200 Subject: [PATCH 63/79] Replace commented code on Cluster timeouts with comments --- lib/ProxySQL_Cluster.cpp | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/lib/ProxySQL_Cluster.cpp b/lib/ProxySQL_Cluster.cpp index a0ada5cd5f..252b0638f3 100644 --- a/lib/ProxySQL_Cluster.cpp +++ b/lib/ProxySQL_Cluster.cpp @@ -77,8 +77,6 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { cluster_creds_t creds { GloProxyCluster->get_credentials() }; if (creds.user.size()) { // do not monitor if the username is empty - unsigned int timeout = 1; - // unsigned int timeout_long = 60; if (conn == NULL) { conn = mysql_init(NULL); if (conn==NULL) { @@ -86,9 +84,10 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { goto __exit_monitor_thread; } } + // READ/WRITE timeouts were enforced as an attempt to prevent deadlocks in the original + // implementation. They were proven unnecessary, leaving only 'CONNECT_TIMEOUT'. + unsigned int timeout = 1; 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); { unsigned char val = 1; mysql_options(conn, MYSQL_OPT_SSL_ENFORCE, &val); mysql_options(conn, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); @@ -1155,11 +1154,10 @@ void ProxySQL_Cluster::pull_mysql_query_rules_from_peer(const string& expected_c creds = GloProxyCluster->get_credentials(); if (creds.user.size()) { // do not monitor if the username is empty + // READ/WRITE timeouts were enforced as an attempt to prevent deadlocks in the original + // implementation. They were proven unnecessary, leaving only 'CONNECT_TIMEOUT'. 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); { unsigned char val = 1; mysql_options(conn, MYSQL_OPT_SSL_ENFORCE, &val); mysql_options(conn, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); @@ -1451,11 +1449,10 @@ void ProxySQL_Cluster::pull_mysql_users_from_peer(const string& expected_checksu creds = GloProxyCluster->get_credentials(); if (creds.user.size()) { // do not monitor if the username is empty + // READ/WRITE timeouts were enforced as an attempt to prevent deadlocks in the original + // implementation. They were proven unnecessary, leaving only 'CONNECT_TIMEOUT'. 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); { unsigned char val = 1; mysql_options(conn, MYSQL_OPT_SSL_ENFORCE, &val); mysql_options(conn, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); @@ -1745,6 +1742,8 @@ void ProxySQL_Cluster::pull_runtime_mysql_servers_from_peer(const runtime_mysql_ creds = GloProxyCluster->get_credentials(); if (creds.user.size()) { // do not monitor if the username is empty + // READ/WRITE timeouts were enforced as an attempt to prevent deadlocks in the original + // implementation. They were proven unnecessary, leaving only 'CONNECT_TIMEOUT'. unsigned int timeout = 1; mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); { @@ -1893,11 +1892,10 @@ void ProxySQL_Cluster::pull_mysql_servers_v2_from_peer(const mysql_servers_v2_ch creds = GloProxyCluster->get_credentials(); if (creds.user.size()) { // do not monitor if the username is empty + // READ/WRITE timeouts were enforced as an attempt to prevent deadlocks in the original + // implementation. They were proven unnecessary, leaving only 'CONNECT_TIMEOUT'. 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); { unsigned char val = 1; mysql_options(conn, MYSQL_OPT_SSL_ENFORCE, &val); mysql_options(conn, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); @@ -2425,11 +2423,10 @@ void ProxySQL_Cluster::pull_global_variables_from_peer(const string& var_type, c creds = GloProxyCluster->get_credentials(); if (creds.user.size()) { // do not monitor if the username is empty + // READ/WRITE timeouts were enforced as an attempt to prevent deadlocks in the original + // implementation. They were proven unnecessary, leaving only 'CONNECT_TIMEOUT'. 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); { unsigned char val = 1; mysql_options(conn, MYSQL_OPT_SSL_ENFORCE, &val); mysql_options(conn, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); @@ -2589,11 +2586,10 @@ void ProxySQL_Cluster::pull_proxysql_servers_from_peer(const std::string& expect creds = GloProxyCluster->get_credentials(); if (creds.user.size()) { // do not monitor if the username is empty + // READ/WRITE timeouts were enforced as an attempt to prevent deadlocks in the original + // implementation. They were proven unnecessary, leaving only 'CONNECT_TIMEOUT'. 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); { unsigned char val = 1; mysql_options(conn, MYSQL_OPT_SSL_ENFORCE, &val); mysql_options(conn, MARIADB_OPT_SSL_KEYLOG_CALLBACK, (void*)proxysql_keylog_write_line_callback); From 80c30d76f02ff352fb601636f0f967a44cb7add6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Tue, 14 May 2024 12:30:36 +0200 Subject: [PATCH 64/79] Fix un-initialized variable 'all_modules_started' --- lib/ProxySQL_Admin.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index 39e4175201..869e5fafbb 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -6033,6 +6033,7 @@ ProxySQL_Admin::ProxySQL_Admin() : variables.p_memory_metrics_interval = 61; #ifdef DEBUG variables.debug=GloVars.global.gdbg; + all_modules_started = false; debug_output = 1; proxysql_set_admin_debug_output(debug_output); #endif /* DEBUG */ From 8c83dacd9b2045d82f1621685f8dbd1a7a3a1577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Tue, 14 May 2024 12:51:48 +0200 Subject: [PATCH 65/79] Several improvements for 'test_backend_conn_ping' - Improved test documentation, including doc about ProxySQL backend connection pinging algorithm. - Fixed math for working with different number of threads. - Fixed estimations for number of backend connections that should be maintained, and test goal. - Fixed test early return. The test original goal was monitoring the number of connections maintained during an interval, ensuring the behavior was consistent during multiple checks. Any early return or shortcut defeats this purpose, this has increased the test duration. - Added more overloads for helper function 'ext_single_row_val'. - Improved logging. --- test/tap/tap/utils.cpp | 35 ++- test/tap/tap/utils.h | 2 + test/tap/tests/test_backend_conn_ping-t.cpp | 260 ++++++++++++-------- 3 files changed, 188 insertions(+), 109 deletions(-) diff --git a/test/tap/tap/utils.cpp b/test/tap/tap/utils.cpp index 60fda98e4f..a3f7e9d78d 100644 --- a/test/tap/tap/utils.cpp +++ b/test/tap/tap/utils.cpp @@ -522,6 +522,39 @@ ext_val_t ext_single_row_val(const mysql_res_row& row, const string& def } } +ext_val_t ext_single_row_val(const mysql_res_row& row, const int32_t& def_val) { + if (row.empty() || row.front().empty()) { + return { -1, def_val, {} }; + } else { + errno = 0; + char* p_end {}; + const int32_t val = std::strtol(row.front().c_str(), &p_end, 10); + + if (row[0] == p_end || errno == ERANGE) { + return { -2, def_val, string { row[0] } }; + } else { + return { EXIT_SUCCESS, val, string { row[0] } }; + } + } +} + +ext_val_t ext_single_row_val(const mysql_res_row& row, const uint32_t& def_val) { + if (row.empty() || row.front().empty()) { + return { -1, def_val, {} }; + } else { + errno = 0; + char* p_end {}; + const uint32_t val = std::strtoul(row.front().c_str(), &p_end, 10); + + if (row[0] == p_end || errno == ERANGE) { + return { -2, def_val, string { row[0] } }; + } else { + return { EXIT_SUCCESS, val, string { row[0] } }; + } + } +} + + ext_val_t ext_single_row_val(const mysql_res_row& row, const int64_t& def_val) { if (row.empty() || row.front().empty()) { return { -1, def_val, {} }; @@ -544,7 +577,7 @@ ext_val_t ext_single_row_val(const mysql_res_row& row, const uint64_t& } else { errno = 0; char* p_end {}; - const uint64_t val = std::strtoll(row.front().c_str(), &p_end, 10); + const uint64_t val = std::strtoull(row.front().c_str(), &p_end, 10); if (row[0] == p_end || errno == ERANGE) { return { -2, def_val, string { row[0] } }; diff --git a/test/tap/tap/utils.h b/test/tap/tap/utils.h index 7422abe146..afa804074e 100644 --- a/test/tap/tap/utils.h +++ b/test/tap/tap/utils.h @@ -171,6 +171,8 @@ struct ext_val_t { * @return An `ext_val_t` where T is the type of the provided default value. */ ext_val_t ext_single_row_val(const mysql_res_row& row, const std::string& def_val); +ext_val_t ext_single_row_val(const mysql_res_row& row, const int32_t& def_val); +ext_val_t ext_single_row_val(const mysql_res_row& row, const uint32_t& def_val); ext_val_t ext_single_row_val(const mysql_res_row& row, const int64_t& def_val); ext_val_t ext_single_row_val(const mysql_res_row& row, const uint64_t& def_val); diff --git a/test/tap/tests/test_backend_conn_ping-t.cpp b/test/tap/tests/test_backend_conn_ping-t.cpp index 5c2be5634c..fc09701920 100644 --- a/test/tap/tests/test_backend_conn_ping-t.cpp +++ b/test/tap/tests/test_backend_conn_ping-t.cpp @@ -11,19 +11,60 @@ * over time. I.e. connections are not getting destroyed due to not being kept alive. * 3. Perform a query per each created connection exahusting the backend connections while checking for * any error reported by the client due to broken connections. + * + * Backend Connection Pinging Algorithm: + * ==================================== + * + * The algorithm used for pinging backend connections is simple: + * + * 1. Pinging is done for 'idle_session' in batches of SESSIONS_FOR_CONNECTIONS_HANDLER. + * 2. The pinging interval is determined by 'ping_interval_server_msec'. + * 3. Oldest sessions are always selected first for pinging. + * + * With this approach, it should be possible to ping a very large number of backend connections before + * losing them due to inactivity (wait_timeout), this will be defined by: + * + * ``` + * (NUM_THREADS * SESSIONS_FOR_CONNECTIONS_HANDLER)*floor((MYSQL_wait_timeout-1)/ping_interval_server_msec) + * ``` + * + * This means that, for example, for a config like: `num_threads=4,wait_timeout=60,batch_size=64`, we can + * expect: `15104` connections. Of course, this suggests that this algorithm wont have any issues holding a + * healthy connection pool of any size when using real `wait_timeout` values. + * + * Algorithm - Special Cases: + * ========================= + * + * This algorithm has a expected behavior that might seems unexpected when pushing the limits of the + * connections that are being maintained; like we can accidentally do in this test. If the number of + * connections being maintained exceeds the number that can be pinged within the `MySQL_wait_timeout` + * interval, this will cause a cascading effect, that will result in extra connections timing out. The + * number of connections from the connection pool that will timeout will be proportional to the fraction of + * the batch (NUM_THREADS*SESSIONS_FOR_CONNECTIONS_HANDLER) filled with connections that exceeded the + * interval processing capacity. + * + * If for example, the number of connections exceeded the ones that can be maintained by 50% of the batching + * interval, for the case of 4 threads, this will be 128 connections, then 50% of the connection pool will + * be lost due to this cascading effect. Elaborating a little bit further, when the number of connections + * being maintained exceeds the number of connections that can be pinged, the connections exceeding the + * interval capabilities will timeout, since these connections will be the oldest, they will be the first to + * be selected for the next interval. This will shift all subsequent batching intervals by this number of + * connections, since we are operating at maximum capacity (maximum number exceeded), all the intervals were + * selecting connections that should be pinged, otherwise will timeout. This is why the previously mentioned + * shift will result in the timeout of these connections, causing the mentioned cascading effect. + * + * This is expected behavior, and **it's not an issue**. The number of connections ProxySQL can ping is + * mainly determined by the interval that defines `wait_timeout` in the MySQL server and + * `ping_interval_server_msec`, which default value is `1000`. With reasonable values for these two + * parameters this number is very high, even with extremely low values for `wait_timeout`, like + * `num_threads=4,wait_timeout=60,batch_size=64`, a large number of connections(`15104`) would still be + * maintained without issues. This is what makes this issue an artificial one. */ -/* -NOTE: the parameters in this test are tuned in a way that if proxysql starts -with only 1 worker thread, it is unlikely to ping all connections on time. -See note on wait_timeout -*/ - #include #include #include #include -#include #include #include @@ -39,49 +80,20 @@ using std::pair; using srv_cfg = vector>; -int wait_timeout = 10; - -#ifndef SESSIONS_FOR_CONNECTIONS_HANDLER -#define SESSIONS_FOR_CONNECTIONS_HANDLER 64 -#endif +#define SESSIONS_FOR_CONNECTIONS_HANDLER 64 -// if only 1 worker thread is running, wait_timeout should be bigger -// 1 worker thread : wait_timeout = 45 -// 4 worker threads : wait_timeout = 10 -int compute_wait_timeout(MYSQL *my_conn) { - int res = EXIT_SUCCESS; - res = mysql_query(my_conn, "SELECT @@mysql-threads"); - if (res != EXIT_SUCCESS) { - fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(my_conn)); - res = EXIT_FAILURE; - return res; - } - MYSQL_RES* my_res = mysql_store_result(my_conn); - if (my_res == nullptr) { - fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(my_conn)); - res = EXIT_FAILURE; - return res; - } +// IMPORTANT: We **always** gives ourselves `1` second grace period. Depending on MySQL connection killing +// policy for `wait_timeout`, connections could already be killed on the edge of the interval. Also, for extra +// safety, we gave `1` extra second to avoid possible rounding errors on time computations within MySQL. +uint32_t grace_period = 2; +int wait_timeout = 10 + grace_period; - MYSQL_ROW row = mysql_fetch_row(my_res); - if (row == nullptr || row[0] == nullptr) { - fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(my_conn)); - res = EXIT_FAILURE; - return res; - } else { - const char *val = row[0]; - diag("mysql-threads = %s", val); - if (strcmp(val,"1")==0) { - diag("Setting wait_timeout to 45 instead of 10"); - wait_timeout = 45; - } - } - mysql_free_result(my_res); +int max_pinged_conns(uint32_t num_threads, uint32_t ping_interval_server_msec, uint32_t wait_timeout) { + uint32_t ping_proc_batches = floor((wait_timeout- (grace_period+1))/float(ping_interval_server_msec/1000.0)); - return res; + return (num_threads * SESSIONS_FOR_CONNECTIONS_HANDLER) * ping_proc_batches; } - int change_mysql_cfg( const CommandLine& cl, const string& host, const string& port, const srv_cfg& new_srv_cfg, srv_cfg& out_old_srv_cfg ) { @@ -233,7 +245,6 @@ int check_backend_conns( // 2. Check that the connections remain steady for a period of time MYSQL* admin = mysql_init(NULL); - vector svrs_conns {}; { if (!mysql_real_connect(admin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { @@ -241,19 +252,6 @@ int check_backend_conns( return EXIT_FAILURE; } - for (const auto& svr_addr : svrs_addrs) { - MYSQL* mysql = mysql_init(NULL); - -// if (!mysql_real_connect(mysql, svr_addr.first.c_str(), cl.username, cl.password, NULL, svr_addr.second, NULL, 0)) { - if (!mysql_real_connect(mysql, svr_addr.first.c_str(), cl.mysql_username, cl.mysql_password, NULL, svr_addr.second, NULL, 0)) { - fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(mysql)); - res = EXIT_FAILURE; - goto cleanup; - } - - svrs_conns.push_back(mysql); - } - sleep(5); uint64_t exp_conn_count = test_params.init_batch_size + test_params.batch_size * test_params.its; @@ -263,8 +261,8 @@ int check_backend_conns( uint64_t act_proxy_free_conn_count = 0; uint64_t act_proxy_used_conn_count = 0; + uint32_t intv = 5; uint32_t total_wait_time = 40; - uint32_t intv = 10; uint32_t total_checks = total_wait_time / intv; for (uint32_t check_num = 0; check_num < total_checks; check_num++) { @@ -272,12 +270,30 @@ int check_backend_conns( act_mysql_conn_count = 0; const string mysql_query_string { - "SELECT count(*) FROM information_schema.processlist WHERE" + "SELECT count(*) FROM INFORMATION_SCHEMA.PROCESSLIST WHERE" " USER=\"" + string { cl.username } + "\"" //" USER=\"" + string { cl.username } + "\" and DB=\"backend_conn_ping_test\"" //" COMMAND=\"Sleep\" and USER=\"" + string { cl.username } + "\" and DB=\"backend_conn_ping_test\"" }; diag("Line:%d : Running: %s", __LINE__ , mysql_query_string.c_str()); + + vector svrs_conns {}; + + // Recreate the connections at each interval + for (const auto& svr_addr : svrs_addrs) { + const char* c_svr_addr { svr_addr.first.c_str() }; + + MYSQL* mysql = mysql_init(NULL); + + if (!mysql_real_connect(mysql, c_svr_addr, cl.mysql_username, cl.mysql_password, NULL, svr_addr.second, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(mysql)); + res = EXIT_FAILURE; + goto cleanup; + } + + svrs_conns.push_back(mysql); + } + for (MYSQL* mysql : svrs_conns) { uint64_t tmp_mysql_conn_count = 0; @@ -290,6 +306,10 @@ int check_backend_conns( act_mysql_conn_count += tmp_mysql_conn_count; } + for (MYSQL* mysql : svrs_conns) { + mysql_close(mysql); + } + const string srv_ports { std::accumulate(std::begin(svrs_addrs), std::end(svrs_addrs), string {}, [](const string& s1, const svr_addr& addr) -> string { @@ -342,12 +362,6 @@ int check_backend_conns( diag("act_proxy_free_conn_count = %lu", act_proxy_free_conn_count); diag("act_proxy_used_conn_count = %lu", act_proxy_used_conn_count); - if ( - act_mysql_conn_count >= exp_conn_count || - (act_proxy_free_conn_count + act_proxy_used_conn_count + SESSIONS_FOR_CONNECTIONS_HANDLER) >= exp_conn_count - ) { - break; - } if (intv) { diag("Line:%d : Sleeping %d" , __LINE__ , intv); sleep(intv); @@ -357,9 +371,7 @@ int check_backend_conns( ok( q_res == EXIT_SUCCESS && act_mysql_conn_count >= ((float) exp_conn_count * 0.95) // allow 5% margin of error && - ((act_proxy_free_conn_count + act_proxy_used_conn_count + SESSIONS_FOR_CONNECTIONS_HANDLER) >= exp_conn_count) - //&& act_mysql_conn_count == act_proxy_free_conn_count // they can't be equal - , + ((act_proxy_free_conn_count + act_proxy_used_conn_count) >= exp_conn_count), "Created server connections should be properly maintained (pinged) by ProxySQL:" " { ExpConns: %ld, ActMySQLConns: %ld, ActProxyConns: %ld }", exp_conn_count, act_mysql_conn_count, act_proxy_free_conn_count @@ -390,9 +402,6 @@ int check_backend_conns( cleanup: mysql_close(admin); - for (MYSQL* mysql : svrs_conns) { - mysql_close(mysql); - } for (MYSQL* mysql : mysql_conns) { mysql_close(mysql); @@ -457,38 +466,65 @@ int main(int, char**) { // Close no longer required connection mysql_close(proxy_mysql); - MYSQL* proxy_admin = mysql_init(NULL); - if (!proxy_admin) { - fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxy_admin)); - return exit_status(); - } - - if (!mysql_real_connect(proxy_admin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { - fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(proxy_admin)); + MYSQL* admin = mysql_init(NULL); + if (!admin) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin)); return exit_status(); } - if (compute_wait_timeout(proxy_admin) != EXIT_SUCCESS) { + if (!mysql_real_connect(admin, cl.host, cl.admin_username, cl.admin_password, NULL, cl.admin_port, NULL, 0)) { + fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin)); return exit_status(); } - double intv = 5; - double b = 128; double b_0 = 256; + double b = 128; double freq = 1000; - double rate = 64 / ( freq / 1000 ); - double conn_creation_intv = 30; + double rate = 64 / (freq / 1000); + + const char q_mysql_threads[] { "SELECT @@mysql-threads" }; + ext_val_t ext_threads { mysql_query_ext_val(admin, q_mysql_threads, int32_t(0)) }; + + if (ext_threads.err != EXIT_SUCCESS) { + const string err { get_ext_val_err(admin, ext_threads) }; + diag("Failed query query:`%s`, err:`%s`", q_mysql_threads, err.c_str()); + return EXIT_FAILURE; + } + + // IMPORTANT-NOTE: + // Variable 'ping_interval_server_msec' isn't relevant because ProxySQL isn't under load. The relevant + // variable passes to be `mysql-poll_timeout`; with each timeout threads will process idle sessions. If we + // wished to extend this test with a dummy load on ProxySQL, this should be uncommented. + // =================================================================================================== + // const char q_ping_intv[] { "SELECT @@mysql-ping_interval_server_msec" }; + const char q_ping_intv[] { "SELECT @@mysql-poll_timeout" }; + // =================================================================================================== + ext_val_t ext_ping_intv { mysql_query_ext_val(admin, q_ping_intv, int32_t(0)) }; - double its = (conn_creation_intv - b_0/rate) / ( b / rate ); + if (ext_ping_intv.err != EXIT_SUCCESS) { + const string err { get_ext_val_err(admin, ext_ping_intv) }; + diag("Failed query query:`%s`, err:`%s`", q_ping_intv, err.c_str()); + return EXIT_FAILURE; + } + + uint32_t max_conns = max_pinged_conns(ext_threads.val, 2000, wait_timeout); + uint32_t server_max_conns = max_conns + 1000; + double its = floor((max_conns - b_0) / b); + + double conn_creation_intv = b_0/rate + (b/rate)*its; double delay_s = conn_creation_intv / its; + diag("ProxySQL config mysql_threads:%d, ping_intv:%d", ext_threads.val, ext_ping_intv.val); + diag("Selected test params b_0:%lf, b:%lf, freq:%lf, rate:%lf", b_0, b, freq, rate); + diag("Computed test params max_conns=%d, intv=%lf, its=%lf", max_conns, conn_creation_intv, its); + // Cleanup previous backend connections diag("Cleaning up previous backend connections..."); - MYSQL_QUERY(proxy_admin, "UPDATE mysql_servers SET max_connections=0"); - MYSQL_QUERY(proxy_admin, "LOAD MYSQL SERVERS TO RUNTIME"); + MYSQL_QUERY(admin, "UPDATE mysql_servers SET max_connections=0"); + MYSQL_QUERY(admin, "LOAD MYSQL SERVERS TO RUNTIME"); // Wait for backend connection cleanup - int w_res = wait_target_backend_conns(proxy_admin, 0, 10); + int w_res = wait_target_backend_conns(admin, 0, 10); if (w_res != EXIT_SUCCESS) { if (w_res == -2) { const char* err_msg = "'wait_target_backend_conns()' timed out"; @@ -499,12 +535,12 @@ int main(int, char**) { } diag("Setting mysql_servers config..."); { - string query = "UPDATE mysql_servers SET max_connections=2500"; + string query { "UPDATE mysql_servers SET max_connections=" + std::to_string(server_max_conns) }; diag("Running: %s", query.c_str()); - MYSQL_QUERY(proxy_admin, query.c_str()); + MYSQL_QUERY(admin, query.c_str()); query = "LOAD MYSQL SERVERS TO RUNTIME"; diag("Running: %s", query.c_str()); - MYSQL_QUERY(proxy_admin, query.c_str()); + MYSQL_QUERY(admin, query.c_str()); } diag("Setting ProxySQL config..."); @@ -512,27 +548,30 @@ int main(int, char**) { // Set the backend connections ping frequency string query = string { "SET mysql-ping_interval_server_msec=" + std::to_string(freq) }; diag("%s", query.c_str()); - MYSQL_QUERY(proxy_admin, query.c_str()); + MYSQL_QUERY(admin, query.c_str()); // Make sure no connection cleanup takes place query = "SET mysql-free_connections_pct=100"; diag("%s", query.c_str()); - MYSQL_QUERY(proxy_admin, query.c_str()); + MYSQL_QUERY(admin, query.c_str()); // Don't retry on failure query = "SET mysql-query_retries_on_failure=0"; diag("%s", query.c_str()); - MYSQL_QUERY(proxy_admin, query.c_str()); + MYSQL_QUERY(admin, query.c_str()); + query = "SET mysql-max_connections=" + std::to_string(server_max_conns*3); + diag("%s", query.c_str()); + MYSQL_QUERY(admin, query.c_str()); // Set a higher max_connection number for the servers query = "LOAD MYSQL VARIABLES TO RUNTIME"; diag("%s", query.c_str()); - MYSQL_QUERY(proxy_admin, query.c_str()); + MYSQL_QUERY(admin, query.c_str()); } // Configure MySQL infra servers with: 'wait_timeout' and 'max_connections' vector> servers_old_configs {}; diag("Configure 'MYSQL' infra servers..."); { - MYSQL_QUERY(proxy_admin, "SELECT DISTINCT hostname, port FROM mysql_servers WHERE hostgroup_id IN (0,1)"); - MYSQL_RES* my_servers_res = mysql_store_result(proxy_admin); + MYSQL_QUERY(admin, "SELECT DISTINCT hostname, port FROM mysql_servers WHERE hostgroup_id IN (0,1)"); + MYSQL_RES* my_servers_res = mysql_store_result(admin); vector servers_rows = extract_mysql_rows(my_servers_res); mysql_free_result(my_servers_res); @@ -541,7 +580,7 @@ int main(int, char**) { return exit_status(); } - srv_cfg new_srv_cfg { { "wait_timeout", wait_timeout }, { "max_connections", 2500 } }; + srv_cfg new_srv_cfg { { "wait_timeout", wait_timeout }, { "max_connections", 5000 } }; for (const mysql_res_row& srv_row : servers_rows) { srv_cfg old_srv_cfg {}; @@ -565,7 +604,11 @@ int main(int, char**) { const string docker_mode = getenv("DOCKER_MODE"); if (docker_mode.find("dns") == docker_mode.size() - 3) { s_server_test.assign({ { "mysql1.infra-mysql57", 3306 } }); - m_server_test.assign({ { "mysql1.infra-mysql57", 3306 }, { "mysql2.infra-mysql57", 3306 }, { "mysql3.infra-mysql57", 3306 } }); + m_server_test.assign({ + { "mysql1.infra-mysql57", 3306 }, + { "mysql2.infra-mysql57", 3306 }, + { "mysql3.infra-mysql57", 3306 } + }); } else { s_server_test.assign({ { "127.0.0.1", 13306 } }); m_server_test.assign({ { "127.0.0.1", 13306 }, { "127.0.0.1", 13307 }, { "127.0.0.1", 13308 } }); @@ -584,12 +627,12 @@ int main(int, char**) { diag("Cleaning up previous backend connections..."); string query = "UPDATE mysql_servers SET max_connections=0"; diag("Line:%d : Running: %s", __LINE__ , query.c_str()); - MYSQL_QUERY(proxy_admin, query.c_str()); + MYSQL_QUERY(admin, query.c_str()); query = "LOAD MYSQL SERVERS TO RUNTIME"; diag("Line:%d : Running: %s", __LINE__ , query.c_str()); - MYSQL_QUERY(proxy_admin, query.c_str()); + MYSQL_QUERY(admin, query.c_str()); - int w_res = wait_target_backend_conns(proxy_admin, 0, 10); + int w_res = wait_target_backend_conns(admin, 0, 10); if (w_res != EXIT_SUCCESS) { string err_msg {}; if (w_res == -2) { @@ -600,12 +643,13 @@ int main(int, char**) { fprintf(stderr, "File %s, line %d, Error: \"%s\"\n", __FILE__, __LINE__, err_msg.c_str()); } - query = "UPDATE mysql_servers SET max_connections=2500"; + diag("Reconfiguring ProxySQL 'mysql_servers' after connection cleanup"); + query = "UPDATE mysql_servers SET max_connections=" + std::to_string(server_max_conns); diag("Line:%d : Running: %s", __LINE__ , query.c_str()); - MYSQL_QUERY(proxy_admin, query.c_str()); + MYSQL_QUERY(admin, query.c_str()); query = "LOAD MYSQL SERVERS TO RUNTIME"; diag("Line:%d : Running: %s", __LINE__ , query.c_str()); - MYSQL_QUERY(proxy_admin, query.c_str()); + MYSQL_QUERY(admin, query.c_str()); if (w_res == EXIT_SUCCESS) { diag("Performing 'check_backend_conns()' for servers: '%s'", nlohmann::json(m_server_test).dump().c_str()); @@ -634,7 +678,7 @@ int main(int, char**) { } } - mysql_close(proxy_admin); + mysql_close(admin); return exit_status(); } From c6fb194fc9f1561da6969fd86238ad0ac49ff117 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Tue, 14 May 2024 12:58:05 +0200 Subject: [PATCH 66/79] Several improvements for 'test_cluster_sync-t' - Fix race condition on 'check_module_checksums_sync'. - Replaced hardcoded value for CHECKSUM_SYNC_TIMEOUT in favor of computation. - Added extra logging to synchronization checks. --- test/tap/tests/test_cluster_sync-t.cpp | 51 ++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/test/tap/tests/test_cluster_sync-t.cpp b/test/tap/tests/test_cluster_sync-t.cpp index 32a449fc30..bf2337bec5 100644 --- a/test/tap/tests/test_cluster_sync-t.cpp +++ b/test/tap/tests/test_cluster_sync-t.cpp @@ -575,6 +575,8 @@ const vector module_sync_payloads { }; int wait_for_node_sync(MYSQL* admin, const vector queries, uint32_t timeout) { + diag("Starting wait for node synchronization"); + uint waited = 0; bool not_synced = false; std::string failed_query {}; @@ -584,7 +586,7 @@ int wait_for_node_sync(MYSQL* admin, const vector queries, uint32_t time // Check that all the entries have been synced for (const auto& query : queries) { - if (mysql_query(admin, query.c_str())) { + if (mysql_query_t(admin, query.c_str())) { fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin)); return -1; } @@ -691,17 +693,39 @@ int check_module_checksums_sync( ) { const char new_remote_checksum_query_t[] { "SELECT count(*) FROM stats_proxysql_servers_checksums WHERE " - "hostname='%s' AND port='%d' AND name='%s' AND checksum!='%s'" + "hostname='%s' AND port='%d' AND name='%s' AND checksum!='%s' AND checksum='%s'" }; const char synced_runtime_checksums_query_t[] { "SELECT COUNT(*) FROM runtime_checksums_values WHERE name='%s' AND checksum='%s'" }; + const char q_check_intv[] { + "SELECT variable_value FROM global_variables WHERE variable_name='admin-cluster_check_interval_ms'" + }; + ext_val_t ext_check_intv { mysql_query_ext_val(admin, q_check_intv, int64_t(0)) }; + + if (ext_check_intv.err != EXIT_SUCCESS) { + const string err { get_ext_val_err(admin, ext_check_intv) }; + diag("Failed getting 'cluster_check_interval_ms' query:`%s`, err:`%s`", q_check_intv, err.c_str()); + return EXIT_FAILURE; + } + + const char q_sts_freq[] { + "SELECT variable_value FROM global_variables WHERE variable_name='admin-cluster_check_status_frequency'" + }; + ext_val_t ext_sts_freq { mysql_query_ext_val(admin, q_sts_freq, int64_t(0)) }; + + if (ext_sts_freq.err != EXIT_SUCCESS) { + const string err { get_ext_val_err(admin, ext_check_intv) }; + diag("Failed getting 'cluster_check_status_frequency' query:`%s`, err:`%s`", q_check_intv, err.c_str()); + return EXIT_FAILURE; + } + // Store current remote checksum value const string& module { module_sync.module }; // Checksum can not be present if we have just added the remote - uint32_t CHECKSUM_SYNC_TIMEOUT = 3; + uint32_t CHECKSUM_SYNC_TIMEOUT = ((ext_check_intv.val/1000) * ext_sts_freq.val) + 1; const char wait_remote_checksums_init_t[] { "SELECT LENGTH(checksum) FROM stats_proxysql_servers_checksums WHERE " @@ -736,6 +760,20 @@ int check_module_checksums_sync( return EXIT_FAILURE; } + // Get the new checksum computed after previous 'UPDATE' operation + const char q_module_checksum_t[] { + "SELECT checksum FROM main.runtime_checksums_values WHERE name='%s'" + }; + + cfmt_t q_module_checksum { cstr_format(q_module_checksum_t, module.c_str()) }; + ext_val_t ext_checksum { mysql_query_ext_val(admin, q_module_checksum.str, string()) }; + + if (ext_checksum.err != EXIT_SUCCESS) { + const string err { get_ext_val_err(admin, ext_checksum) }; + diag("Failed query query:`%s`, err:`%s`", q_check_intv, err.c_str()); + return EXIT_FAILURE; + } + // Wait for new checksum to be detected cfmt_t new_remote_checksum_query { cstr_format( @@ -743,7 +781,8 @@ int check_module_checksums_sync( conn_opts.host.c_str(), conn_opts.port, module.c_str(), - cur_remote_checksum.c_str() + cur_remote_checksum.c_str(), + ext_checksum.val.c_str() ) }; int sync_res = wait_for_node_sync(r_admin, { new_remote_checksum_query.str }, CHECKSUM_SYNC_TIMEOUT); @@ -1008,6 +1047,10 @@ int check_modules_checksums_sync( check_module_checksums_sync( admin, r_admin, m_conn_opts.first, module_sync_payloads[dis_module], def_mod_diffs_sync, remote_stderr ); + // A wait IS NOT required. The checks perform the required waiting, ensuring that the new computed + // checksum after the module update has been propagated to the other server. Previously the check + // didn't take into account the exact checksum, only the change, this led to invalid change + // detections. check_module_checksums_sync( r_admin, admin, r_conn_opts.first, module_sync_payloads[dis_module], def_mod_diffs_sync, main_stderr ); From e930d98856150d0fe21b1f6b0925192a78dba47a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Tue, 14 May 2024 14:47:47 +0200 Subject: [PATCH 67/79] Several improvements for test 'eof_packet_mixed_queries' - Improved logging on error handling. - Fixed replication issues in the presence of query rules. --- .../eof_packet_mixed_queries-t.cpp | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/test/tap/tests_with_deps/deprecate_eof_support/eof_packet_mixed_queries-t.cpp b/test/tap/tests_with_deps/deprecate_eof_support/eof_packet_mixed_queries-t.cpp index d55fa7f2f6..bba026eba2 100644 --- a/test/tap/tests_with_deps/deprecate_eof_support/eof_packet_mixed_queries-t.cpp +++ b/test/tap/tests_with_deps/deprecate_eof_support/eof_packet_mixed_queries-t.cpp @@ -11,7 +11,6 @@ * - Fast forward */ -#include #include #include #include @@ -150,13 +149,14 @@ pair perform_stmt_select_query( p_binds[0].is_null = 0; p_binds[0].length = 0; - if (mysql_stmt_bind_param(stmts, p_binds)) { - string_format("'mysql_stmt_bind_param' at line %d failed: %s", err_msg, __LINE__, mysql_stmt_error(stmts)); + int myerr = 0; + + if ((myerr = mysql_stmt_bind_param(stmts, p_binds))) { + string_format("'mysql_stmt_bind_param' at line %d failed with error %d", err_msg, __LINE__, myerr); return { EXIT_FAILURE, err_msg }; } - int s_err = mysql_stmt_execute(stmts); - if (s_err != EXIT_SUCCESS) { + if ((myerr = mysql_stmt_execute(stmts))) { string_format("'mysql_stmt_execute' at line %d failed: %s", err_msg, __LINE__, mysql_stmt_error(stmts)); return { EXIT_FAILURE, err_msg }; } @@ -184,20 +184,18 @@ pair perform_stmt_select_query( r_binds[2].length= &length[2]; r_binds[2].error= &error[2]; - s_err = mysql_stmt_bind_result(stmts, r_binds); - if (s_err != EXIT_SUCCESS) { - string_format("'mysql_stmt_bind_result' at line %d failed: %s", err_msg, __LINE__, mysql_stmt_error(stmts)); + if ((myerr = mysql_stmt_bind_result(stmts, r_binds))) { + string_format("'mysql_stmt_bind_result' at line %d failed with error %d", err_msg, __LINE__, myerr); return { EXIT_FAILURE, err_msg }; } - if (mysql_stmt_store_result(stmts) || mysql_errno(mysql_server) != EXIT_SUCCESS) { - string_format("'mysql_stmt_store_result' at line %d failed: %s", err_msg, __LINE__, mysql_stmt_error(stmts)); + if ((myerr = mysql_stmt_store_result(stmts)) || mysql_errno(mysql_server) != EXIT_SUCCESS) { + string_format("'mysql_stmt_store_result' at line %d failed with error %d", err_msg, __LINE__, myerr); return { EXIT_FAILURE, err_msg }; } - s_err = mysql_stmt_fetch(stmts); - if (s_err != EXIT_SUCCESS) { - string_format("'mysql_stmt_fetch' at line %d failed: %s", err_msg, __LINE__, mysql_stmt_error(stmts)); + if ((myerr = mysql_stmt_fetch(stmts))) { + string_format("'mysql_stmt_fetch' at line %d failed with error %d", err_msg, __LINE__, myerr); return { EXIT_FAILURE, err_msg }; } @@ -313,8 +311,10 @@ pair perform_stmt_update_query( bindsu[2].is_null = 0; bindsu[2].length = 0; - if (mysql_stmt_bind_param(stmtu, bindsu)) { - string_format("'mysql_stmt_bind_param' at line %d failed: %s", err_msg, __LINE__, mysql_stmt_error(stmtu)); + int myerr = 0; + + if ((myerr=mysql_stmt_bind_param(stmtu, bindsu))) { + string_format("'mysql_stmt_bind_param' at line %d failed with error %d", err_msg, __LINE__, myerr); return { EXIT_FAILURE, err_msg }; } @@ -497,9 +497,6 @@ int test_target_queries(MYSQL* proxy) { return !(op_res.first == EXIT_SUCCESS); } -// NOTE: Test hardcoded to hostgroup '0' -const uint32_t HG_ID = 0; - int main(int argc, char** argv) { CommandLine cl; @@ -524,8 +521,8 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } - // Change default query rules to avoid replication issues - MYSQL_QUERY(admin, "UPDATE mysql_query_rules SET destination_hostgroup=0 WHERE rule_id=2"); + // Change default query rules to avoid replication issues; This test only requires the default hostgroup + MYSQL_QUERY(admin, "UPDATE mysql_query_rules SET active=0"); MYSQL_QUERY(admin, "LOAD MYSQL QUERY RULES TO RUNTIME"); MYSQL* proxy = mysql_init(NULL); @@ -578,7 +575,7 @@ int main(int argc, char** argv) { cleanup: // Recover default query rules - MYSQL_QUERY(admin, "UPDATE mysql_query_rules SET destination_hostgroup=0 WHERE rule_id=2"); + MYSQL_QUERY(admin, "UPDATE mysql_query_rules SET active=1"); MYSQL_QUERY(admin, "LOAD MYSQL QUERY RULES TO RUNTIME"); mysql_close(admin); From b86bbaeb367de648c7dc8c3ae0cd012070ccddad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Tue, 14 May 2024 14:49:27 +0200 Subject: [PATCH 68/79] Minor cleanup in some TAP tests - Removed unused dependencies. - Removed commented code. --- .../deprecate_eof_support/deprecate_eof_cache-t.cpp | 3 --- .../deprecate_eof_support/eof_fast_forward-t.cpp | 3 --- 2 files changed, 6 deletions(-) diff --git a/test/tap/tests_with_deps/deprecate_eof_support/deprecate_eof_cache-t.cpp b/test/tap/tests_with_deps/deprecate_eof_support/deprecate_eof_cache-t.cpp index e46e2d3f94..51c68ade70 100644 --- a/test/tap/tests_with_deps/deprecate_eof_support/deprecate_eof_cache-t.cpp +++ b/test/tap/tests_with_deps/deprecate_eof_support/deprecate_eof_cache-t.cpp @@ -1,11 +1,8 @@ -#include #include #include #include #include #include -#include -#include #include "mysql.h" #include "mysqld_error.h" diff --git a/test/tap/tests_with_deps/deprecate_eof_support/eof_fast_forward-t.cpp b/test/tap/tests_with_deps/deprecate_eof_support/eof_fast_forward-t.cpp index 3a50ce1d80..82b5cf14dc 100644 --- a/test/tap/tests_with_deps/deprecate_eof_support/eof_fast_forward-t.cpp +++ b/test/tap/tests_with_deps/deprecate_eof_support/eof_fast_forward-t.cpp @@ -18,7 +18,6 @@ #include #include "mysql.h" -#include "mysqld_error.h" #include "proxysql_utils.h" @@ -57,7 +56,6 @@ int create_testing_tables(MYSQL* mysql_server) { int perform_workload_on_connection(MYSQL* proxy, MYSQL* admin) { // Change default query rules to avoid replication issues -// MYSQL_QUERY(admin, "UPDATE mysql_query_rules SET destination_hostgroup=0 WHERE rule_id=2"); MYSQL_QUERY(admin, "UPDATE mysql_query_rules SET active=0"); MYSQL_QUERY(admin, "LOAD MYSQL QUERY RULES TO RUNTIME"); @@ -175,7 +173,6 @@ int perform_workload_on_connection(MYSQL* proxy, MYSQL* admin) { ok(ops_err_msg.empty() == true, "Operations should complete successfully - '%s'", ops_err_msg.c_str()); // Recover default query rules -// MYSQL_QUERY(admin, "UPDATE mysql_query_rules SET destination_hostgroup=0 WHERE rule_id=2"); MYSQL_QUERY(admin, "UPDATE mysql_query_rules SET active=1"); MYSQL_QUERY(admin, "LOAD MYSQL QUERY RULES TO RUNTIME"); From 19e4ac2ead8da6da59db56ed76b7c6c2a96bba3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Tue, 14 May 2024 15:03:13 +0200 Subject: [PATCH 69/79] Mitigate invalid memory accesses from Monitor GR on 'SHUTDOWN SLOW' --- lib/MySQL_Monitor.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/MySQL_Monitor.cpp b/lib/MySQL_Monitor.cpp index a943f10a98..2d518109b0 100644 --- a/lib/MySQL_Monitor.cpp +++ b/lib/MySQL_Monitor.cpp @@ -4091,6 +4091,14 @@ void* monitor_GR_thread_HG(void *arg) { // 3. Delegate the async fetching + actions of 'MySQL_Monitor_State_Data' with conns on 'Monitor_Poll'. /////////////////////////////////////////////////////////////////////////////////////// + // NOTE: This is just a best effort to avoid invalid memory accesses during 'SHUTDOWN SLOW'. Since the + // previous section is 'time consuming', there are good changes that we can detect a shutdown before + // trying to perform the monitoring actions on the acquired 'mmsd'. This exact scenario and timing has + // been previously observed in the CI. + if (GloMyMon->shutdown) { + break; + } + // Handle 'mmsds' that failed to optain conns for (const unique_ptr& mmsd : fail_mmsds) { async_gr_mon_actions_handler(mmsd.get()); From f8fb6fda75423973743da5f9f9f573a2af97a013 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Tue, 14 May 2024 15:54:57 +0200 Subject: [PATCH 70/79] Fix compilation for Centos 7 --- lib/ProxySQL_Cluster.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ProxySQL_Cluster.cpp b/lib/ProxySQL_Cluster.cpp index 252b0638f3..64a870c9e3 100644 --- a/lib/ProxySQL_Cluster.cpp +++ b/lib/ProxySQL_Cluster.cpp @@ -74,7 +74,7 @@ void * ProxySQL_Cluster_Monitor_thread(void *args) { goto __exit_monitor_thread; } while (glovars.shutdown == 0 && rc_bool == true) { - cluster_creds_t creds { GloProxyCluster->get_credentials() }; + cluster_creds_t creds(GloProxyCluster->get_credentials()); if (creds.user.size()) { // do not monitor if the username is empty if (conn == NULL) { From f962d13148560eba5dd9578254d61161cc01a9cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Thu, 16 May 2024 12:39:20 +0200 Subject: [PATCH 71/79] Fix crashes during shutdown due to uninitialized 'AdminHTTPServer' --- lib/ProxySQL_Admin.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index 869e5fafbb..61fe3af6ed 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -6389,6 +6389,8 @@ bool ProxySQL_Admin::init(const bootstrap_info_t& bootstrap_info) { Admin_HTTP_Server = NULL; AdminRestApiServer = NULL; + AdminHTTPServer = NULL; + /* AdminRestApiServer = new ProxySQL_RESTAPI_Server(); AdminRestApiServer->print_version(); From 54ca795d9cdf71c161756b2f8867935220779fc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Thu, 16 May 2024 12:41:09 +0200 Subject: [PATCH 72/79] Fix shutdown crashes due to 'debug_filters' destruction --- lib/debug.cpp | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/lib/debug.cpp b/lib/debug.cpp index 7d236f6a0f..e4eda648f5 100644 --- a/lib/debug.cpp +++ b/lib/debug.cpp @@ -46,24 +46,28 @@ static inline unsigned long long debug_monotonic_time() { #ifdef DEBUG -// this set will have all the filters related to debug -// for convention, the key is: -// filename:line:function -// this key structure applies also if line is 0 or function is empty -// filename is mandatory -std::set debug_filters; +/** + * @brief Contains all filters related to debug. + * @details The convention for key value is `filename:line:function`. This key structure also applies also + * applies if the line is `0` or function is empty, the `filename` is always mandatory. + * + * IMPORTANT: This structure is a pointer to avoid race conditions during process termination, otherwise the + * destruction of the object may be performed before working threads have exited. This structure will leak, + * this is intentional, since we can't synchronize the exit of the working threads with its destruction. + */ +std::set* debug_filters = nullptr; static bool filter_debug_entry(const char *__file, int __line, const char *__func) { //pthread_mutex_lock(&debug_mutex); pthread_rwlock_rdlock(&filters_rwlock); bool to_filter = false; - if (debug_filters.size()) { // if the set is empty we aren't performing any filter, so we won't search + if (debug_filters && debug_filters->size()) { // if the set is empty we aren't performing any filter, so we won't search std::string key(__file); key += ":" + std::to_string(__line); key += ":"; key += __func; // we start with a full search - if (debug_filters.find(key) != debug_filters.end()) { + if (debug_filters->find(key) != debug_filters->end()) { to_filter = true; } else { // we now search filename + line @@ -71,7 +75,7 @@ static bool filter_debug_entry(const char *__file, int __line, const char *__fun key += ":" + std::to_string(__line); // remember to add the final ":" key += ":"; - if (debug_filters.find(key) != debug_filters.end()) { + if (debug_filters->find(key) != debug_filters->end()) { to_filter = true; } else { // we now search filename + function @@ -79,14 +83,14 @@ static bool filter_debug_entry(const char *__file, int __line, const char *__fun // no line = 0 key += ":0:"; key += __func; - if (debug_filters.find(key) != debug_filters.end()) { + if (debug_filters->find(key) != debug_filters->end()) { to_filter = true; } else { // we now search filename only key = __file; // remember to add ":" even if no line key += ":0:"; - if (debug_filters.find(key) != debug_filters.end()) { + if (debug_filters->find(key) != debug_filters->end()) { to_filter = true; } else { // if we reached here, we couldn't find any filter @@ -105,7 +109,9 @@ static bool filter_debug_entry(const char *__file, int __line, const char *__fun void proxy_debug_get_filters(std::set& f) { //pthread_mutex_lock(&debug_mutex); pthread_rwlock_rdlock(&filters_rwlock); - f = debug_filters; + if (debug_filters) { + f = *debug_filters; + } pthread_rwlock_unlock(&filters_rwlock); //pthread_mutex_unlock(&debug_mutex); } @@ -115,8 +121,12 @@ void proxy_debug_get_filters(std::set& f) { void proxy_debug_load_filters(std::set& f) { //pthread_mutex_lock(&debug_mutex); pthread_rwlock_wrlock(&filters_rwlock); - debug_filters.erase(debug_filters.begin(), debug_filters.end()); - debug_filters = f; + if (debug_filters) { + debug_filters->erase(debug_filters->begin(), debug_filters->end()); + *debug_filters = f; + } else { + debug_filters = new std::set(f); + } pthread_rwlock_unlock(&filters_rwlock); //pthread_mutex_unlock(&debug_mutex); } From abaea17a049546d8031ced76650df4f800c96d00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Thu, 16 May 2024 19:45:53 +0200 Subject: [PATCH 73/79] Fix race condition on 'test_cluster_sync-t' --- test/tap/tests/test_cluster_sync-t.cpp | 63 +++++++++++++++++--------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/test/tap/tests/test_cluster_sync-t.cpp b/test/tap/tests/test_cluster_sync-t.cpp index bf2337bec5..4c31f4b0a8 100644 --- a/test/tap/tests/test_cluster_sync-t.cpp +++ b/test/tap/tests/test_cluster_sync-t.cpp @@ -683,22 +683,7 @@ string fetch_runtime_checksum(MYSQL* admin, const string& module) { const int def_mod_diffs_sync = 2; -int check_module_checksums_sync( - MYSQL* admin, - MYSQL* r_admin, - const conn_opts_t& conn_opts, - const sync_payload_t& module_sync, - int diffs_sync, - const string& logfile_path -) { - const char new_remote_checksum_query_t[] { - "SELECT count(*) FROM stats_proxysql_servers_checksums WHERE " - "hostname='%s' AND port='%d' AND name='%s' AND checksum!='%s' AND checksum='%s'" - }; - const char synced_runtime_checksums_query_t[] { - "SELECT COUNT(*) FROM runtime_checksums_values WHERE name='%s' AND checksum='%s'" - }; - +int32_t get_checksum_sync_timeout(MYSQL* admin) { const char q_check_intv[] { "SELECT variable_value FROM global_variables WHERE variable_name='admin-cluster_check_interval_ms'" }; @@ -707,7 +692,7 @@ int check_module_checksums_sync( if (ext_check_intv.err != EXIT_SUCCESS) { const string err { get_ext_val_err(admin, ext_check_intv) }; diag("Failed getting 'cluster_check_interval_ms' query:`%s`, err:`%s`", q_check_intv, err.c_str()); - return EXIT_FAILURE; + return -1; } const char q_sts_freq[] { @@ -718,14 +703,37 @@ int check_module_checksums_sync( if (ext_sts_freq.err != EXIT_SUCCESS) { const string err { get_ext_val_err(admin, ext_check_intv) }; diag("Failed getting 'cluster_check_status_frequency' query:`%s`, err:`%s`", q_check_intv, err.c_str()); - return EXIT_FAILURE; + return -1; } + return ((ext_check_intv.val/1000) * ext_sts_freq.val) + 1; +} + +int check_module_checksums_sync( + MYSQL* admin, + MYSQL* r_admin, + const conn_opts_t& conn_opts, + const sync_payload_t& module_sync, + int diffs_sync, + const string& logfile_path +) { + const char new_remote_checksum_query_t[] { + "SELECT count(*) FROM stats_proxysql_servers_checksums WHERE " + "hostname='%s' AND port='%d' AND name='%s' AND checksum!='%s' AND checksum='%s'" + }; + const char synced_runtime_checksums_query_t[] { + "SELECT COUNT(*) FROM runtime_checksums_values WHERE name='%s' AND checksum='%s'" + }; + // Store current remote checksum value const string& module { module_sync.module }; // Checksum can not be present if we have just added the remote - uint32_t CHECKSUM_SYNC_TIMEOUT = ((ext_check_intv.val/1000) * ext_sts_freq.val) + 1; + uint32_t CHECKSUM_SYNC_TIMEOUT = get_checksum_sync_timeout(admin); + if (CHECKSUM_SYNC_TIMEOUT == -1) { + diag("Failed fetching values to compute 'CHECKSUM_SYNC_TIMEOUT'"); + return EXIT_FAILURE; + } const char wait_remote_checksums_init_t[] { "SELECT LENGTH(checksum) FROM stats_proxysql_servers_checksums WHERE " @@ -735,7 +743,7 @@ int check_module_checksums_sync( cstr_format(wait_remote_checksums_init_t, conn_opts.host.c_str(), conn_opts.port, module.c_str()) }; - int checksum_present = wait_for_node_sync( r_admin, { wait_remote_checksums_init.str }, CHECKSUM_SYNC_TIMEOUT); + int checksum_present = wait_for_node_sync(r_admin, { wait_remote_checksums_init.str }, CHECKSUM_SYNC_TIMEOUT); if (checksum_present) { diag("No checksum (or zero) detected int the target remote server for module '%s'", module.c_str()); return EXIT_FAILURE; @@ -770,7 +778,7 @@ int check_module_checksums_sync( if (ext_checksum.err != EXIT_SUCCESS) { const string err { get_ext_val_err(admin, ext_checksum) }; - diag("Failed query query:`%s`, err:`%s`", q_check_intv, err.c_str()); + diag("Failed query query:`%s`, err:`%s`", q_module_checksum.str.c_str(), err.c_str()); return EXIT_FAILURE; } @@ -949,6 +957,19 @@ int check_all_modules_sync( const sync_payload_t& sync_payload = module_sync_payloads[j]; const int diffs_sync = j == dis_module ? 0 : def_mod_diffs_sync; + // REQUIRE-WAIT: All checks make use of the 'admin_variables' to enable/disable module + // synchronization. The previous module check only waits for the module synchronization itself, but + // not for the propagation of the changed 'admin_variables'; not waiting the propagation of this + // previous change could interfere with the checks target to this same module. + if (module_sync_payloads[j].module == "admin_variables") { + uint32_t CHECKSUM_SYNC_TIMEOUT = get_checksum_sync_timeout(admin); + if (CHECKSUM_SYNC_TIMEOUT == -1) { + diag("Failed fetching values to compute 'CHECKSUM_SYNC_TIMEOUT'"); + return EXIT_FAILURE; + } + sleep(CHECKSUM_SYNC_TIMEOUT); + } + int check_sync = check_module_checksums_sync(admin, r_admin, conn_opts, sync_payload, diffs_sync, remote_stderr); if (check_sync) { if (diffs_sync) { From 6a77d7d95aa3d4be0f0774c7a7584ecf3ce8e770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Thu, 16 May 2024 19:48:40 +0200 Subject: [PATCH 74/79] Increase logging for 'test_cluster_sync-t' --- test/tap/tests/test_cluster_sync-t.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/tap/tests/test_cluster_sync-t.cpp b/test/tap/tests/test_cluster_sync-t.cpp index 4c31f4b0a8..09b4bdd027 100644 --- a/test/tap/tests/test_cluster_sync-t.cpp +++ b/test/tap/tests/test_cluster_sync-t.cpp @@ -1028,7 +1028,8 @@ int check_modules_checksums_sync( for (size_t dis_module = 0; dis_module < module_sync_payloads.size(); dis_module++) { printf("\n"); - diag("Start test with sync DISABLED for module '%s'", module_sync_payloads[dis_module].module.c_str()); + const string dis_module_str { module_sync_payloads[dis_module].module }; + diag("Start test with sync DISABLED for module '%s'", dis_module_str.c_str()); for (const sync_payload_t& sync_payload : module_sync_payloads) { const string set_query { "SET " + sync_payload.sync_variable + "=" + def_syncs }; @@ -1041,9 +1042,11 @@ int check_modules_checksums_sync( MYSQL_QUERY_T(r_admin, "LOAD ADMIN VARIABLES TO RUNTIME"); // Check that ALL modules sync, but 'dis_module' in both ways - Main-To-Remote and Remote-To-Main + diag("Checking ALL modules SYNC but DISABLED module '%s'", dis_module_str.c_str()); check_all_modules_sync(admin, r_admin, m_conn_opts.first, dis_module, main_stderr, remote_stderr); // Enable back the module + diag("Renable module '%s' synchronization", dis_module_str.c_str()); const string enable_query { "SET " + module_sync_payloads[dis_module].sync_variable + "=" + std::to_string(def_mod_diffs_sync) }; @@ -1065,6 +1068,7 @@ int check_modules_checksums_sync( } // Check that the module syncs again in both ways + diag("Checking module '%s' syncs again - MAIN to REMOTE", dis_module_str.c_str()); check_module_checksums_sync( admin, r_admin, m_conn_opts.first, module_sync_payloads[dis_module], def_mod_diffs_sync, remote_stderr ); @@ -1072,12 +1076,14 @@ int check_modules_checksums_sync( // checksum after the module update has been propagated to the other server. Previously the check // didn't take into account the exact checksum, only the change, this led to invalid change // detections. + diag("Checking module '%s' syncs again - REMOTE to MAIN", dis_module_str.c_str()); check_module_checksums_sync( r_admin, admin, r_conn_opts.first, module_sync_payloads[dis_module], def_mod_diffs_sync, main_stderr ); if (module_sync_payloads[dis_module].module != "proxysql_servers") { // Disable the module using checksums + diag("Disable module '%s' using checksums", dis_module_str.c_str()); const string disable_checksum_query { "SET " + module_sync_payloads[dis_module].checksum_variable + "=false" }; @@ -1085,6 +1091,7 @@ int check_modules_checksums_sync( MYSQL_QUERY_T(r_admin, "LOAD ADMIN VARIABLES TO RUNTIME"); // Check that ALL modules sync, but 'dis_module' in both ways - Main-To-Remote and Remote-To-Main + diag("Checking ALL modules SYNC but DISABLED module '%s'", dis_module_str.c_str()); check_all_modules_sync(admin, r_admin, m_conn_opts.first, dis_module, main_stderr, remote_stderr); // Enable back the module @@ -1108,9 +1115,11 @@ int check_modules_checksums_sync( } // Check that the module syncs again in both ways + diag("Checking module '%s' syncs again - MAIN to REMOTE", dis_module_str.c_str()); check_module_checksums_sync( admin, r_admin, m_conn_opts.first, module_sync_payloads[dis_module], def_mod_diffs_sync, remote_stderr ); + diag("Checking module '%s' syncs again - REMOTE to MAIN", dis_module_str.c_str()); check_module_checksums_sync( r_admin, admin, r_conn_opts.first, module_sync_payloads[dis_module], def_mod_diffs_sync, main_stderr ); From d7f2c44bc8bf0e8418ed274506f41be557d181dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Thu, 16 May 2024 19:49:41 +0200 Subject: [PATCH 75/79] Increase tolerance of 'reg_test_3765_ssl_pollout' for ASAN builds --- test/tap/tests/reg_test_3765_ssl_pollout-t.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/tap/tests/reg_test_3765_ssl_pollout-t.cpp b/test/tap/tests/reg_test_3765_ssl_pollout-t.cpp index 4a42904d97..898c56f4ed 100644 --- a/test/tap/tests/reg_test_3765_ssl_pollout-t.cpp +++ b/test/tap/tests/reg_test_3765_ssl_pollout-t.cpp @@ -109,6 +109,11 @@ int main(int argc, char** argv) { plan(6); + // For ASAN builds we don't care about correctness in this measurement. + if (get_env_int("WITHASAN", 0)) { + MAX_ALLOWED_CPU_USAGE = 80; + } + double idle_cpu_ms = 0; double final_cpu_ms = 0; From f8674517543a44381b339c6e973bf7549af61784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Thu, 16 May 2024 19:51:14 +0200 Subject: [PATCH 76/79] Increase tolerance of 'test_cacert_load_and_verify_duration-t' for ASAN builds --- test/tap/tests/test_cacert_load_and_verify_duration-t.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/tap/tests/test_cacert_load_and_verify_duration-t.cpp b/test/tap/tests/test_cacert_load_and_verify_duration-t.cpp index 19da85d3d0..f75b913ee0 100644 --- a/test/tap/tests/test_cacert_load_and_verify_duration-t.cpp +++ b/test/tap/tests/test_cacert_load_and_verify_duration-t.cpp @@ -24,6 +24,10 @@ int main() { plan(1); + int32_t WASAN = get_env_int("WITHASAN", 0); + // Double the value of previously failed ASAN run: '89415ms' + int32_t EXP_TIME = WASAN == 0 ? 20000 : 180000; + MYSQL* proxysql_admin = mysql_init(NULL); // Initialize connection @@ -57,7 +61,7 @@ int main() { if (start_pos != std::string::npos && end_pos != std::string::npos) { uint64_t time = std::stoull(msg.substr(start_pos + 5, end_pos - (start_pos + 5))); - ok(time < 20000, "Total duration is '%lu ms' should be less than 20 Seconds", time); + ok(time < EXP_TIME, "Total duration is '%lu ms' should be less than %d Seconds", time, EXP_TIME/1000); } } mysql_close(proxysql_admin); From 63968d63dedf6e58ea9d0193e5da5ec1b0fd1fdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Thu, 16 May 2024 19:52:21 +0200 Subject: [PATCH 77/79] Increase timeouts of 'restapi_return_codes-t' for ASAN builds --- .../tap/tests/reg_test_3223-restapi_return_codes-t.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/tap/tests/reg_test_3223-restapi_return_codes-t.cpp b/test/tap/tests/reg_test_3223-restapi_return_codes-t.cpp index 6eac9c9d73..fbc7ae2750 100644 --- a/test/tap/tests/reg_test_3223-restapi_return_codes-t.cpp +++ b/test/tap/tests/reg_test_3223-restapi_return_codes-t.cpp @@ -48,7 +48,7 @@ const vector honest_requests { { { "partial_output_flush_script", "%s.py", "POST", 10000 }, { "{}" } }, }; -const vector invalid_requests { +vector invalid_requests { // Checks that 'POST' fails for: // 1 - Empty parameters. // 2 - Invalid JSON input. @@ -279,6 +279,14 @@ int main(int argc, char** argv) { vector i_epts_info {}; const auto ext_i_epts_info = [] (const faulty_req_t& req) { return req.ept_info; }; + + int wasan = get_env_int("WITHASAN", 0); + if (wasan) { + for (auto& req : invalid_requests) { + req.ept_info.timeout += 2000; + } + } + std::transform( invalid_requests.begin(), invalid_requests.end(), std::back_inserter(i_epts_info), ext_i_epts_info ); From 3d185ec4854bfe85aa6ef0ff2073e33a5fcaae5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Fri, 17 May 2024 01:59:14 +0200 Subject: [PATCH 78/79] Further increase timeouts of 'restapi_return_codes-t' for ASAN builds Added doc elaborating on the limitation. --- test/tap/tests/reg_test_3223-restapi_return_codes-t.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/tap/tests/reg_test_3223-restapi_return_codes-t.cpp b/test/tap/tests/reg_test_3223-restapi_return_codes-t.cpp index fbc7ae2750..bd6fa98ba5 100644 --- a/test/tap/tests/reg_test_3223-restapi_return_codes-t.cpp +++ b/test/tap/tests/reg_test_3223-restapi_return_codes-t.cpp @@ -280,10 +280,13 @@ int main(int argc, char** argv) { vector i_epts_info {}; const auto ext_i_epts_info = [] (const faulty_req_t& req) { return req.ept_info; }; + // Failed scripts may require to read the full output from ASAN leaks report. This in combination with the + // forked process shutdown slowdown can take a considerable amount of time. A cleaner solution would be + // to disable 'detect_leaks' at runtime, but doesn't look feasible at the moment. int wasan = get_env_int("WITHASAN", 0); if (wasan) { for (auto& req : invalid_requests) { - req.ept_info.timeout += 2000; + req.ept_info.timeout += 8000; } } From 4f419e60ea437d03d328ed335d4d41e91bc0f7da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Fri, 17 May 2024 13:29:55 +0200 Subject: [PATCH 79/79] Add 'libre2.so' as dependency for TAP tests target 'libtap.so' TAP tests dep 'libre2.so' wasn't created when building TAP deps in debug. --- test/tap/tap/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tap/tap/Makefile b/test/tap/tap/Makefile index 68c2625ba9..f4e148f522 100644 --- a/test/tap/tap/Makefile +++ b/test/tap/tap/Makefile @@ -124,7 +124,7 @@ libtap_mysql57.a: tap.o command_line.o utils_mysql57.o cpp-dotenv/static/cpp-dot libtap_mysql8.a: tap.o command_line.o utils_mysql8.o cpp-dotenv/static/cpp-dotenv/libcpp_dotenv.a ar rcs libtap_mysql8.a tap.o command_line.o utils_mysql8.o $(SQLITE3_LDIR)/sqlite3.o $(PROXYSQL_LDIR)/obj/sha256crypt.oo -libtap.so: libtap_mariadb.a cpp-dotenv/dynamic/cpp-dotenv/libcpp_dotenv.so +libtap.so: libtap_mariadb.a cpp-dotenv/dynamic/cpp-dotenv/libcpp_dotenv.so libre2.so $(CXX) -shared -o libtap.so -Wl,--whole-archive libtap_mariadb.a -Wl,--no-whole-archive $(LWGCOV)