From 18f6826ef79991dfa85cdf9bf67a4d64a416cd55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Tue, 24 Oct 2017 22:07:59 +0200 Subject: [PATCH] Added new variable to limit the number of backend connections per second Variable name is `mysql-throttle_connections_per_sec_to_hostgroup` . Curently it is a global variable and limits the number of new connections per hostgroup, and not per specific node. For example, if mysql-throttle_connections_per_sec_to_hostgroup=100, no more than 100 new connections can be created on any hostgroup no matter the number of servers in that hostgroup. The default is very high (1000000) thus not changing default behaviour. Tuning this variable allows to control and throttle connections spikes to the backend servers. Added also new status variable `Server_Connections_delayed`. This is a counter of how many times Hostgroup Manager didn't return a connection because the limit was reached. It is worth to note that a single client request could make multiple requests, therefore this variable counts the number of time a new connection wasn't created and not how many requests were delayed. --- include/MySQL_HostGroups_Manager.h | 3 +++ include/MySQL_Thread.h | 1 + include/proxysql_structs.h | 2 ++ lib/MySQL_HostGroups_Manager.cpp | 31 +++++++++++++++++++++++------- lib/MySQL_Thread.cpp | 24 +++++++++++++++++++++++ 5 files changed, 54 insertions(+), 7 deletions(-) diff --git a/include/MySQL_HostGroups_Manager.h b/include/MySQL_HostGroups_Manager.h index cef659bf5..08ed86497 100644 --- a/include/MySQL_HostGroups_Manager.h +++ b/include/MySQL_HostGroups_Manager.h @@ -122,6 +122,8 @@ class MySrvList { // MySQL Server List class MyHGC { // MySQL Host Group Container public: unsigned int hid; + unsigned long long current_time_now; + uint32_t new_connections_now; MySrvList *mysrvs; MyHGC(int); ~MyHGC(); @@ -188,6 +190,7 @@ class MySQL_HostGroups_Manager { int client_connections; unsigned long server_connections_aborted; unsigned long server_connections_created; + unsigned long server_connections_delayed; unsigned long server_connections_connected; unsigned long myconnpoll_get; unsigned long myconnpoll_get_ok; diff --git a/include/MySQL_Thread.h b/include/MySQL_Thread.h index c1183ad45..f702d3edc 100644 --- a/include/MySQL_Thread.h +++ b/include/MySQL_Thread.h @@ -365,6 +365,7 @@ class MySQL_Threads_Handler bool enforce_autocommit_on_reads; bool autocommit_false_not_reusable; int max_allowed_packet; + int throttle_connections_per_sec_to_hostgroup; int max_transaction_time; int threshold_query_length; int threshold_resultset_size; diff --git a/include/proxysql_structs.h b/include/proxysql_structs.h index 24cad7d1b..8dab9dc1b 100644 --- a/include/proxysql_structs.h +++ b/include/proxysql_structs.h @@ -589,6 +589,7 @@ __thread char *mysql_thread___init_connect; __thread char *mysql_thread___default_sql_mode; __thread char *mysql_thread___default_time_zone; __thread int mysql_thread___max_allowed_packet; +__thread int mysql_thread___throttle_connections_per_sec_to_hostgroup; __thread int mysql_thread___max_transaction_time; __thread int mysql_thread___threshold_query_length; __thread int mysql_thread___threshold_resultset_size; @@ -688,6 +689,7 @@ extern __thread char *mysql_thread___init_connect; extern __thread char *mysql_thread___default_sql_mode; extern __thread char *mysql_thread___default_time_zone; extern __thread int mysql_thread___max_allowed_packet; +extern __thread int mysql_thread___throttle_connections_per_sec_to_hostgroup; extern __thread int mysql_thread___max_transaction_time; extern __thread int mysql_thread___threshold_query_length; extern __thread int mysql_thread___threshold_resultset_size; diff --git a/lib/MySQL_HostGroups_Manager.cpp b/lib/MySQL_HostGroups_Manager.cpp index 6ce477578..857860478 100644 --- a/lib/MySQL_HostGroups_Manager.cpp +++ b/lib/MySQL_HostGroups_Manager.cpp @@ -320,6 +320,8 @@ MySrvList::~MySrvList() { MyHGC::MyHGC(int _hid) { hid=_hid; mysrvs=new MySrvList(this); + current_time_now = 0; + new_connections_now = 0; } @@ -1387,11 +1389,24 @@ MySQL_Connection * MySrvConnList::get_random_MyConn(bool ff) { 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 { - conn = new MySQL_Connection(); - conn->parent=mysrvc; - __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; + 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++; + if (_myhgc->new_connections_now > (unsigned int) mysql_thread___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; + __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 } @@ -1404,8 +1419,10 @@ MySQL_Connection * MySQL_HostGroups_Manager::get_MyConn_from_pool(unsigned int _ MySrvC *mysrvc=myhgc->get_random_MySrvC(); if (mysrvc) { // a MySrvC exists. If not, we return NULL = no targets conn=mysrvc->ConnectionsFree->get_random_MyConn(ff); - mysrvc->ConnectionsUsed->add(conn); - status.myconnpoll_get_ok++; + if (conn) { + mysrvc->ConnectionsUsed->add(conn); + status.myconnpoll_get_ok++; + } } wrunlock(); proxy_debug(PROXY_DEBUG_MYSQL_CONNPOOL, 7, "Returning MySQL Connection %p, server %s:%d\n", conn, (conn ? conn->parent->address : "") , (conn ? conn->parent->port : 0 )); diff --git a/lib/MySQL_Thread.cpp b/lib/MySQL_Thread.cpp index 352fcefa4..9b66946b1 100644 --- a/lib/MySQL_Thread.cpp +++ b/lib/MySQL_Thread.cpp @@ -232,6 +232,7 @@ static char * mysql_thread_variables_names[]= { (char *)"monitor_wait_timeout", (char *)"monitor_writer_is_also_reader", (char *)"max_allowed_packet", + (char *)"throttle_connections_per_sec_to_hostgroup", (char *)"max_transaction_time", (char *)"multiplexing", (char *)"forward_autocommit", @@ -342,6 +343,7 @@ MySQL_Threads_Handler::MySQL_Threads_Handler() { variables.monitor_wait_timeout=true; variables.monitor_writer_is_also_reader=true; variables.max_allowed_packet=4*1024*1024; + variables.throttle_connections_per_sec_to_hostgroup=1000000; variables.max_transaction_time=4*3600*1000; variables.hostgroup_manager_verbose=1; variables.threshold_query_length=512*1024; @@ -596,6 +598,7 @@ int MySQL_Threads_Handler::get_variable_int(char *name) { if (!strcasecmp(name,"connect_retries_delay")) return (int)variables.connect_retries_delay; if (!strcasecmp(name,"eventslog_filesize")) return (int)variables.eventslog_filesize; if (!strcasecmp(name,"max_allowed_packet")) return (int)variables.max_allowed_packet; + if (!strcasecmp(name,"throttle_connections_per_sec_to_hostgroup")) return (int)variables.throttle_connections_per_sec_to_hostgroup; if (!strcasecmp(name,"max_transaction_time")) return (int)variables.max_transaction_time; if (!strcasecmp(name,"hostgroup_manager_verbose")) return (int)variables.hostgroup_manager_verbose; if (!strcasecmp(name,"threshold_query_length")) return (int)variables.threshold_query_length; @@ -848,6 +851,10 @@ char * MySQL_Threads_Handler::get_variable(char *name) { // this is the public f sprintf(intbuf,"%d",variables.max_allowed_packet); return strdup(intbuf); } + if (!strcasecmp(name,"throttle_connections_per_sec_to_hostgroup")) { + sprintf(intbuf,"%d",variables.throttle_connections_per_sec_to_hostgroup); + return strdup(intbuf); + } if (!strcasecmp(name,"max_transaction_time")) { sprintf(intbuf,"%d",variables.max_transaction_time); return strdup(intbuf); @@ -1220,6 +1227,15 @@ bool MySQL_Threads_Handler::set_variable(char *name, char *value) { // this is t return false; } } + if (!strcasecmp(name,"throttle_connections_per_sec_to_hostgroup")) { + int intv=atoi(value); + if (intv >= 1 && intv <= 100*1000*1000) { + variables.throttle_connections_per_sec_to_hostgroup=intv; + return true; + } else { + return false; + } + } if (!strcasecmp(name,"hostgroup_manager_verbose")) { int intv=atoi(value); if (intv >= 0 && intv <= 1) { @@ -3058,6 +3074,7 @@ 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___throttle_connections_per_sec_to_hostgroup=GloMTH->get_variable_int((char *)"throttle_connections_per_sec_to_hostgroup"); 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"); @@ -3378,6 +3395,13 @@ SQLite3_result * MySQL_Threads_Handler::SQL3_GlobalStatus() { pta[1]=buf; result->add_row(pta); } + { + // Connections delayed + pta[0]=(char *)"Server_Connections_delayed"; + sprintf(buf,"%lu",MyHGM->status.server_connections_delayed); + pta[1]=buf; + result->add_row(pta); + } #ifdef IDLE_THREADS { // Connections non idle pta[0]=(char *)"Client_Connections_non_idle";