From b4a7c77023708a3b1fe5dde75ef2193d24d35be7 Mon Sep 17 00:00:00 2001 From: Peter van Dijk Date: Fri, 1 Mar 2019 13:03:02 +0100 Subject: [PATCH] plug mysql_thread_init memory leak --- modules/gmysqlbackend/gmysqlbackend.cc | 4 ++- modules/gmysqlbackend/smysql.cc | 34 ++++++++++++++++++++++++-- modules/gmysqlbackend/smysql.hh | 4 ++- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/modules/gmysqlbackend/gmysqlbackend.cc b/modules/gmysqlbackend/gmysqlbackend.cc index c5b85b7d2e..440a528967 100644 --- a/modules/gmysqlbackend/gmysqlbackend.cc +++ b/modules/gmysqlbackend/gmysqlbackend.cc @@ -59,7 +59,8 @@ void gMySQLBackend::reconnect() getArg("password"), getArg("group"), mustDo("innodb-read-committed"), - getArgAsNum("timeout"))); + getArgAsNum("timeout"), + mustDo("thread-cleanup"))); } class gMySQLFactory : public BackendFactory @@ -78,6 +79,7 @@ public: declare(suffix,"group", "Database backend MySQL 'group' to connect as", "client"); declare(suffix,"innodb-read-committed","Use InnoDB READ-COMMITTED transaction isolation level","yes"); declare(suffix,"timeout", "The timeout in seconds for each attempt to read/write to the server", "10"); + declare(suffix,"thread-cleanup","Explicitly call mysql_thread_end() when threads end","no"); declare(suffix,"dnssec","Enable DNSSEC processing","no"); diff --git a/modules/gmysqlbackend/smysql.cc b/modules/gmysqlbackend/smysql.cc index 20c2d290da..cd4f8a522d 100644 --- a/modules/gmysqlbackend/smysql.cc +++ b/modules/gmysqlbackend/smysql.cc @@ -38,6 +38,32 @@ typedef bool my_bool; #endif +/* + * Older versions of the MySQL and MariaDB client leak memory + * because they expect the application to call mysql_thread_end() + * when a thread ends. This thread_local static object provides + * that closure, but only when the user has asked for it + * by setting gmysql-thread-cleanup. + * For more discussion, see https://github.com/PowerDNS/pdns/issues/6231 + */ +class MySQLThreadCloser +{ +public: + ~MySQLThreadCloser() { + if(d_enabled) { + mysql_thread_end(); + } + } + void enable() { + d_enabled = true; + } + +private: + bool d_enabled = false; +}; + +static thread_local MySQLThreadCloser threadcloser; + bool SMySQL::s_dolog; pthread_mutex_t SMySQL::s_myinitlock = PTHREAD_MUTEX_INITIALIZER; @@ -411,6 +437,10 @@ void SMySQL::connect() int retry=1; Lock l(&s_myinitlock); + if (d_threadCleanup) { + threadcloser.enable(); + } + if (!mysql_init(&d_db)) throw sPerrorException("Unable to initialize mysql driver"); @@ -459,8 +489,8 @@ void SMySQL::connect() } SMySQL::SMySQL(const string &database, const string &host, uint16_t port, const string &msocket, const string &user, - const string &password, const string &group, bool setIsolation, unsigned int timeout): - d_database(database), d_host(host), d_msocket(msocket), d_user(user), d_password(password), d_group(group), d_timeout(timeout), d_port(port), d_setIsolation(setIsolation) + const string &password, const string &group, bool setIsolation, unsigned int timeout, bool threadCleanup): + d_database(database), d_host(host), d_msocket(msocket), d_user(user), d_password(password), d_group(group), d_timeout(timeout), d_port(port), d_setIsolation(setIsolation), d_threadCleanup(threadCleanup) { connect(); } diff --git a/modules/gmysqlbackend/smysql.hh b/modules/gmysqlbackend/smysql.hh index 7e31d7b564..7a33e8c529 100644 --- a/modules/gmysqlbackend/smysql.hh +++ b/modules/gmysqlbackend/smysql.hh @@ -32,7 +32,8 @@ public: SMySQL(const string &database, const string &host="", uint16_t port=0, const string &msocket="",const string &user="", const string &password="", const string &group="", - bool setIsolation=false, unsigned int timeout=10); + bool setIsolation=false, unsigned int timeout=10, + bool threadCleanup=false); ~SMySQL(); @@ -61,6 +62,7 @@ private: unsigned int d_timeout; uint16_t d_port; bool d_setIsolation; + bool d_threadCleanup; }; #endif /* SSMYSQL_HH */ -- 2.47.2