From 4a93861c6cc1dfa4726640693e68ce2bc3946f86 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 31 Mar 2026 15:27:34 +0000 Subject: [PATCH] feat: add mysql resolution_family setting Agent-Logs-Url: https://github.com/sysown/proxysql/sessions/7d0ebed6-886c-4e06-add5-d74b44eced4a Co-authored-by: renecannao <3645227+renecannao@users.noreply.github.com> --- include/MySQL_Resolution.h | 34 +++++++++++++++++++ include/MySQL_Thread.h | 1 + lib/MySQL_Monitor.cpp | 13 ++++--- lib/MySQL_Thread.cpp | 15 ++++++++ test/tap/tests/unit/Makefile | 6 ++++ .../tests/unit/mysql_resolution_unit-t.cpp | 23 +++++++++++++ 6 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 include/MySQL_Resolution.h create mode 100644 test/tap/tests/unit/mysql_resolution_unit-t.cpp diff --git a/include/MySQL_Resolution.h b/include/MySQL_Resolution.h new file mode 100644 index 000000000..fe1f5568c --- /dev/null +++ b/include/MySQL_Resolution.h @@ -0,0 +1,34 @@ +#ifndef MYSQL_RESOLUTION_H +#define MYSQL_RESOLUTION_H + +#include +#include + +inline bool mysql_resolution_family_is_valid(const char* value) { + return value && + (strcasecmp(value, "system") == 0 || + strcasecmp(value, "ipv4") == 0 || + strcasecmp(value, "ipv6") == 0); +} + +inline const char* mysql_resolution_family_normalize(const char* value) { + if (value && strcasecmp(value, "ipv4") == 0) { + return "ipv4"; + } + if (value && strcasecmp(value, "ipv6") == 0) { + return "ipv6"; + } + return "system"; +} + +inline int mysql_resolution_family_to_ai_family(const char* value) { + if (value && strcasecmp(value, "ipv4") == 0) { + return AF_INET; + } + if (value && strcasecmp(value, "ipv6") == 0) { + return AF_INET6; + } + return AF_UNSPEC; +} + +#endif // MYSQL_RESOLUTION_H diff --git a/include/MySQL_Thread.h b/include/MySQL_Thread.h index 6725ff730..c7fa1a817 100644 --- a/include/MySQL_Thread.h +++ b/include/MySQL_Thread.h @@ -437,6 +437,7 @@ class MySQL_Threads_Handler int monitor_local_dns_cache_ttl; int monitor_local_dns_cache_refresh_interval; int monitor_local_dns_resolver_queue_maxsize; + char *resolution_family; char *monitor_username; char *monitor_password; char * monitor_replication_lag_use_percona_heartbeat; diff --git a/lib/MySQL_Monitor.cpp b/lib/MySQL_Monitor.cpp index 99b9e6ae4..f55cfef62 100644 --- a/lib/MySQL_Monitor.cpp +++ b/lib/MySQL_Monitor.cpp @@ -26,6 +26,7 @@ using json = nlohmann::json; #include "ProxySQL_Cluster.hpp" #include "proxysql.h" #include "cpp.h" +#include "MySQL_Resolution.h" #include "proxysql_utils.h" #include "thread.h" @@ -4673,17 +4674,21 @@ void* monitor_dns_resolver_thread(const std::vector& dns_reso /* set hints for getaddrinfo */ memset(&hints, 0, sizeof(hints)); hints.ai_protocol = IPPROTO_TCP; - hints.ai_family = AF_UNSPEC; /*includes: IPv4, IPv6*/ hints.ai_socktype = SOCK_STREAM; /* AI_ADDRCONFIG: IPv4 addresses are returned in the list pointed to by res only if the local system has at least one IPv4 address configured, and IPv6 addresses are returned only if the local system has at least one IPv6 address configured. The loopback address is not considered for this case as valid as a configured address. This flag is - useful on, for example, IPv4-only systems, to ensure that - getaddrinfo() does not return IPv6 socket addresses that would - always fail in connect or bind. */ + useful on, for example, IPv4-only systems, to ensure that + getaddrinfo() does not return IPv6 socket addresses that would + always fail in connect or bind. */ hints.ai_flags = AI_ADDRCONFIG; + char* resolution_family = GloMTH ? GloMTH->get_variable_string((char *)"resolution_family") : NULL; + hints.ai_family = mysql_resolution_family_to_ai_family(resolution_family); /*includes: IPv4, IPv6*/ + if (resolution_family) { + free(resolution_family); + } proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Resolving hostname:[%s] to its mapped IP address.\n", dns_resolve_data->hostname.c_str()); int gai_rc = getaddrinfo(dns_resolve_data->hostname.c_str(), NULL, &hints, &res); diff --git a/lib/MySQL_Thread.cpp b/lib/MySQL_Thread.cpp index 079f108dc..12a93caf8 100644 --- a/lib/MySQL_Thread.cpp +++ b/lib/MySQL_Thread.cpp @@ -27,6 +27,7 @@ using json = nlohmann::json; #include "StatCounters.h" #include "MySQL_PreparedStatement.h" #include "MySQL_Logger.hpp" +#include "MySQL_Resolution.h" #include @@ -421,6 +422,7 @@ static char * mysql_thread_variables_names[]= { (char *)"monitor_local_dns_cache_ttl", (char *)"monitor_local_dns_cache_refresh_interval", (char *)"monitor_local_dns_resolver_queue_maxsize", + (char *)"resolution_family", (char *)"monitor_wait_timeout", (char *)"monitor_writer_is_also_reader", (char *)"max_allowed_packet", @@ -1303,6 +1305,7 @@ MySQL_Threads_Handler::MySQL_Threads_Handler() { variables.monitor_local_dns_cache_ttl = 300000; variables.monitor_local_dns_cache_refresh_interval = 60000; variables.monitor_local_dns_resolver_queue_maxsize = 128; + variables.resolution_family=strdup((char *)"system"); variables.monitor_username=strdup((char *)"monitor"); variables.monitor_password=strdup((char *)"monitor"); variables.monitor_replication_lag_use_percona_heartbeat=strdup((char *)""); @@ -1668,6 +1671,7 @@ char * MySQL_Threads_Handler::get_variable_string(char *name) { if (!strcmp(name,"eventslog_filename")) return strdup(variables.eventslog_filename); if (!strcmp(name,"auditlog_filename")) return strdup(variables.auditlog_filename); if (!strcmp(name,"interfaces")) return strdup(variables.interfaces); + if (!strcmp(name,"resolution_family")) return strdup(variables.resolution_family); if (!strcmp(name,"keep_multiplexing_variables")) return strdup(variables.keep_multiplexing_variables); if (!strcmp(name,"default_authentication_plugin")) return strdup(variables.default_authentication_plugin); if (!strcmp(name,"proxy_protocol_networks")) return strdup(variables.proxy_protocol_networks); @@ -1825,6 +1829,7 @@ char * MySQL_Threads_Handler::get_variable(char *name) { // this is the public f if (!strcasecmp(name,"auditlog_filename")) return strdup(variables.auditlog_filename); if (!strcasecmp(name,"eventslog_filename")) return strdup(variables.eventslog_filename); if (!strcasecmp(name,"default_schema")) return strdup(variables.default_schema); + if (!strcasecmp(name,"resolution_family")) return strdup(variables.resolution_family); if (!strcasecmp(name,"keep_multiplexing_variables")) return strdup(variables.keep_multiplexing_variables); if (!strcasecmp(name,"default_authentication_plugin")) return strdup(variables.default_authentication_plugin); if (!strcasecmp(name,"proxy_protocol_networks")) return strdup(variables.proxy_protocol_networks); @@ -2261,6 +2266,15 @@ bool MySQL_Threads_Handler::set_variable(char *name, const char *value) { // thi return false; } } + if (!strcasecmp(name,"resolution_family")) { + if (mysql_resolution_family_is_valid(value)) { + free(variables.resolution_family); + variables.resolution_family=strdup(mysql_resolution_family_normalize(value)); + return true; + } + proxy_error("%s is an invalid value for %s. Supported values are system, ipv4, ipv6\n", value, name); + return false; + } if (!strcasecmp(name,"proxy_protocol_networks")) { bool ret = false; if (vallen == 0) { @@ -3108,6 +3122,7 @@ MySQL_Threads_Handler::~MySQL_Threads_Handler() { free(variables.monitor_replication_lag_use_percona_heartbeat); variables.monitor_replication_lag_use_percona_heartbeat=NULL; } + if (variables.resolution_family) { free(variables.resolution_family); variables.resolution_family=NULL; } if (variables.default_schema) free(variables.default_schema); if (variables.interfaces) free(variables.interfaces); if (variables.server_version) free(variables.server_version); diff --git a/test/tap/tests/unit/Makefile b/test/tap/tests/unit/Makefile index 30f678877..970d1e1e4 100644 --- a/test/tap/tests/unit/Makefile +++ b/test/tap/tests/unit/Makefile @@ -259,6 +259,7 @@ UNIT_TESTS := smoke_test-t query_cache_unit-t query_processor_unit-t \ mysql_error_classifier_unit-t \ backend_sync_unit-t \ mysql_encode_unit-t \ + mysql_resolution_unit-t \ pgsql_variables_validator_unit-t \ proxysql_utils_unit-t \ gen_utils_unit-t \ @@ -312,6 +313,11 @@ ezoption_parser_unit-t: ezoption_parser_unit-t.cpp $(ODIR)/tap.o $(ODIR)/tap_noi -I$(TAP_IDIR) -I$(PROXYSQL_PATH)/include \ $(STDCPP) -O0 -ggdb $(WGCOV) $(LWGCOV) -lpthread -o $@ +mysql_resolution_unit-t: mysql_resolution_unit-t.cpp $(ODIR)/tap.o $(ODIR)/tap_noise_stubs.o + $(CXX) $< $(ODIR)/tap.o $(ODIR)/tap_noise_stubs.o \ + -I$(TAP_IDIR) -I$(PROXYSQL_PATH)/include \ + $(STDCPP) -O0 -ggdb $(WGCOV) $(LWGCOV) -lpthread -o $@ + # Pattern rule: all unit tests use the same compile + link flags. # Each test binary is built from its .cpp source, linked against # the test harness objects and libproxysql.a with all dependencies. diff --git a/test/tap/tests/unit/mysql_resolution_unit-t.cpp b/test/tap/tests/unit/mysql_resolution_unit-t.cpp new file mode 100644 index 000000000..6dad5366e --- /dev/null +++ b/test/tap/tests/unit/mysql_resolution_unit-t.cpp @@ -0,0 +1,23 @@ +#include "tap.h" + +#include + +#include "MySQL_Resolution.h" + +int main() { + plan(9); + + ok(mysql_resolution_family_is_valid("system"), "system is accepted"); + ok(mysql_resolution_family_is_valid("ipv4"), "ipv4 is accepted"); + ok(mysql_resolution_family_is_valid("ipv6"), "ipv6 is accepted"); + ok(!mysql_resolution_family_is_valid("invalid"), "invalid value is rejected"); + + ok(strcmp(mysql_resolution_family_normalize("IPv4"), "ipv4") == 0, "ipv4 values are normalized"); + ok(strcmp(mysql_resolution_family_normalize("IPv6"), "ipv6") == 0, "ipv6 values are normalized"); + ok(strcmp(mysql_resolution_family_normalize("SYSTEM"), "system") == 0, "system values are normalized"); + + ok(mysql_resolution_family_to_ai_family("ipv4") == AF_INET, "ipv4 maps to AF_INET"); + ok(mysql_resolution_family_to_ai_family("invalid") == AF_UNSPEC, "invalid values fall back to AF_UNSPEC"); + + return exit_status(); +}