From 7eccd347e33f68a7592a22e9453064784559c55e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Sat, 9 Dec 2017 22:39:55 +0100 Subject: [PATCH] Add support for Percona Heartbeat #1248 This commits adds a new variable: mysql-monitor_replication_lag_use_percona_heartbeat This variable defines the percona heartbeat table used to check replication lag. If set, replication lag is checked against the defined table, otherwise `SHOW SLAVE STATUS` is used. To be set, the value should match the following regex: ``` `?([a-z\d_]+)`?\.`?([a-z\d_]+)`? ``` --- include/MySQL_Thread.h | 1 + include/proxysql_structs.h | 2 ++ lib/MySQL_HostGroups_Manager.cpp | 1 + lib/MySQL_Monitor.cpp | 17 +++++++++++++- lib/MySQL_Thread.cpp | 40 ++++++++++++++++++++++++++++++++ 5 files changed, 60 insertions(+), 1 deletion(-) diff --git a/include/MySQL_Thread.h b/include/MySQL_Thread.h index 383afa643..fca9d3858 100644 --- a/include/MySQL_Thread.h +++ b/include/MySQL_Thread.h @@ -331,6 +331,7 @@ class MySQL_Threads_Handler int monitor_slave_lag_when_null; char *monitor_username; char *monitor_password; + char * monitor_replication_lag_use_percona_heartbeat; int ping_interval_server_msec; int ping_timeout_server; int shun_on_failures; diff --git a/include/proxysql_structs.h b/include/proxysql_structs.h index e54aca9c2..500caacb2 100644 --- a/include/proxysql_structs.h +++ b/include/proxysql_structs.h @@ -677,6 +677,7 @@ __thread int mysql_thread___monitor_query_timeout; __thread int mysql_thread___monitor_slave_lag_when_null; __thread char * mysql_thread___monitor_username; __thread char * mysql_thread___monitor_password; +__thread char * mysql_thread___monitor_replication_lag_use_percona_heartbeat; #ifdef DEBUG __thread bool mysql_thread___session_debug; @@ -781,6 +782,7 @@ extern __thread int mysql_thread___monitor_query_timeout; extern __thread int mysql_thread___monitor_slave_lag_when_null; extern __thread char * mysql_thread___monitor_username; extern __thread char * mysql_thread___monitor_password; +extern __thread char * mysql_thread___monitor_replication_lag_use_percona_heartbeat; #ifdef DEBUG extern __thread bool mysql_thread___session_debug; diff --git a/lib/MySQL_HostGroups_Manager.cpp b/lib/MySQL_HostGroups_Manager.cpp index 907749c36..566970674 100644 --- a/lib/MySQL_HostGroups_Manager.cpp +++ b/lib/MySQL_HostGroups_Manager.cpp @@ -1516,6 +1516,7 @@ void MySQL_HostGroups_Manager::replication_lag_action(int _hid, char *address, u (current_replication_lag==-2) // see issue 959 ) { mysrvc->status=MYSQL_SERVER_STATUS_ONLINE; + proxy_warning("Re-enabling server %s:%d with replication lag of %d second\n", address, port, current_replication_lag); } } } diff --git a/lib/MySQL_Monitor.cpp b/lib/MySQL_Monitor.cpp index 52287e068..912b97c10 100644 --- a/lib/MySQL_Monitor.cpp +++ b/lib/MySQL_Monitor.cpp @@ -970,6 +970,8 @@ void * monitor_replication_lag_thread(void *arg) { mmsd->mysql=GloMyMon->My_Conn_Pool->get_connection(mmsd->hostname, mmsd->port); unsigned long long start_time=mysql_thr->curtime; + bool use_percona_heartbeat = false; + char * percona_heartbeat_table = mysql_thread___monitor_replication_lag_use_percona_heartbeat; mmsd->t1=start_time; @@ -984,7 +986,20 @@ void * monitor_replication_lag_thread(void *arg) { } mmsd->t1=monotonic_time(); - mmsd->async_exit_status=mysql_query_start(&mmsd->interr,mmsd->mysql,"SHOW SLAVE STATUS"); + if (percona_heartbeat_table) { + int l = strlen(percona_heartbeat_table); + if (l) { + use_percona_heartbeat = true; + char *base_query = (char *)"SELECT MIN(ROUND(TIMESTAMPDIFF(MICROSECOND, ts, SYSDATE(6))/1000000)) AS Seconds_Behind_Master FROM %s"; + char *replication_query = (char *)malloc(strlen(base_query)+l); + sprintf(replication_query,base_query,percona_heartbeat_table); + mmsd->async_exit_status=mysql_query_start(&mmsd->interr,mmsd->mysql,replication_query); + free(replication_query); + } + } + if (use_percona_heartbeat == false) { + mmsd->async_exit_status=mysql_query_start(&mmsd->interr,mmsd->mysql,"SHOW SLAVE STATUS"); + } while (mmsd->async_exit_status) { mmsd->async_exit_status=wait_for_mysql(mmsd->mysql, mmsd->async_exit_status); unsigned long long now=monotonic_time(); diff --git a/lib/MySQL_Thread.cpp b/lib/MySQL_Thread.cpp index 0d4aaddd0..b646fad71 100644 --- a/lib/MySQL_Thread.cpp +++ b/lib/MySQL_Thread.cpp @@ -6,6 +6,8 @@ #include "SpookyV2.h" #include #include +#include "re2/re2.h" +#include "re2/regexp.h" #ifdef DEBUG MySQL_Session *sess_stopat; @@ -240,6 +242,7 @@ static char * mysql_thread_variables_names[]= { (char *)"monitor_groupreplication_healthcheck_timeout", (char *)"monitor_username", (char *)"monitor_password", + (char *)"monitor_replication_lag_use_percona_heartbeat", (char *)"monitor_query_interval", (char *)"monitor_query_timeout", (char *)"monitor_slave_lag_when_null", @@ -358,6 +361,7 @@ MySQL_Threads_Handler::MySQL_Threads_Handler() { variables.monitor_slave_lag_when_null=60; variables.monitor_username=strdup((char *)"monitor"); variables.monitor_password=strdup((char *)"monitor"); + variables.monitor_replication_lag_use_percona_heartbeat=strdup((char *)""); variables.monitor_wait_timeout=true; variables.monitor_writer_is_also_reader=true; variables.max_allowed_packet=4*1024*1024; @@ -515,6 +519,7 @@ char * MySQL_Threads_Handler::get_variable_string(char *name) { if (!strncasecmp(name,"monitor_",8)) { if (!strcasecmp(name,"monitor_username")) return strdup(variables.monitor_username); if (!strcasecmp(name,"monitor_password")) return strdup(variables.monitor_password); + if (!strcasecmp(name,"monitor_replication_lag_use_percona_heartbeat")) return strdup(variables.monitor_replication_lag_use_percona_heartbeat); } if (!strncasecmp(name,"ssl_",4)) { if (!strcasecmp(name,"ssl_p2s_ca")) { @@ -741,6 +746,7 @@ char * MySQL_Threads_Handler::get_variable(char *name) { // this is the public f if (!strncasecmp(name,"monitor_",8)) { if (!strcasecmp(name,"monitor_username")) return strdup(variables.monitor_username); if (!strcasecmp(name,"monitor_password")) return strdup(variables.monitor_password); + if (!strcasecmp(name,"monitor_replication_lag_use_percona_heartbeat")) return strdup(variables.monitor_replication_lag_use_percona_heartbeat); if (!strcasecmp(name,"monitor_enabled")) { return strdup((variables.monitor_enabled ? "true" : "false")); } @@ -1080,6 +1086,30 @@ bool MySQL_Threads_Handler::set_variable(char *name, char *value) { // this is t variables.monitor_password=strdup(value); return true; } + if (!strcasecmp(name,"monitor_replication_lag_use_percona_heartbeat")) { + if (vallen==0) { // empty string + free(variables.monitor_replication_lag_use_percona_heartbeat); + variables.monitor_replication_lag_use_percona_heartbeat=strdup((value)); + return true; + } else { + re2::RE2::Options *opt2=new re2::RE2::Options(RE2::Quiet); + opt2->set_case_sensitive(false); + char *patt = (char *)"`?([a-z\\d_]+)`?\\.`?([a-z\\d_]+)`?"; + RE2 *re = new RE2(patt, *opt2); + bool rc=false; + rc = RE2::FullMatch(value,*re); + delete re; + delete opt2; + if(rc) { + free(variables.monitor_replication_lag_use_percona_heartbeat); + variables.monitor_replication_lag_use_percona_heartbeat=strdup(value); + return true; + } else { + proxy_error("%s is an invalid value for %s, not matching regex \"%s\"\n", value, name, patt); + } + } + return false; + } if (!strcasecmp(name,"monitor_enabled")) { if (strcasecmp(value,"true")==0 || strcasecmp(value,"1")==0) { variables.monitor_enabled=true; @@ -2112,6 +2142,10 @@ void MySQL_Threads_Handler::stop_listeners() { MySQL_Threads_Handler::~MySQL_Threads_Handler() { if (variables.monitor_username) { free(variables.monitor_username); variables.monitor_username=NULL; } if (variables.monitor_password) { free(variables.monitor_password); variables.monitor_password=NULL; } + if (variables.monitor_replication_lag_use_percona_heartbeat) { + free(variables.monitor_replication_lag_use_percona_heartbeat); + variables.monitor_replication_lag_use_percona_heartbeat=NULL; + } if (variables.default_schema) free(variables.default_schema); if (variables.interfaces) free(variables.interfaces); if (variables.server_version) free(variables.server_version); @@ -2223,6 +2257,10 @@ MySQL_Thread::~MySQL_Thread() { if (mysql_thread___monitor_username) { free(mysql_thread___monitor_username); mysql_thread___monitor_username=NULL; } if (mysql_thread___monitor_password) { free(mysql_thread___monitor_password); mysql_thread___monitor_password=NULL; } + 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=NULL; + } if (mysql_thread___default_schema) { free(mysql_thread___default_schema); mysql_thread___default_schema=NULL; } if (mysql_thread___server_version) { free(mysql_thread___server_version); mysql_thread___server_version=NULL; } if (mysql_thread___init_connect) { free(mysql_thread___init_connect); mysql_thread___init_connect=NULL; } @@ -3246,6 +3284,8 @@ void MySQL_Thread::refresh_variables() { 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"); // SSL proxy to server if (mysql_thread___ssl_p2s_ca) free(mysql_thread___ssl_p2s_ca);