]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
plug mysql_thread_init memory leak, fixes #6231
authorPeter van Dijk <peter.van.dijk@powerdns.com>
Thu, 21 Feb 2019 20:38:01 +0000 (20:38 +0000)
committerPeter van Dijk <peter.van.dijk@powerdns.com>
Fri, 1 Mar 2019 11:23:36 +0000 (12:23 +0100)
docs/backends/generic-mysql.rst
modules/gmysqlbackend/gmysqlbackend.cc
modules/gmysqlbackend/smysql.cc
modules/gmysqlbackend/smysql.hh

index c04070d58c30588f8af551215c9d2cdcc82def75..dc69801ffc51e16bbfbd70e612a8dbdc52444a92 100644 (file)
@@ -123,6 +123,15 @@ Use the InnoDB READ-COMMITTED transaction isolation level. Default: yes.
 The timeout in seconds for each attempt to read from, or write to the
 server. A value of 0 will disable the timeout. Default: 10
 
+.. _setting-gmysql-thread-closer:
+
+``gmysql-thread-closer``
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+Older versions (such as those shipped on RHEL 7) of the MySQL/MariaDB client libraries leak memory unless applications explicitly report the end of each thread to the library. Enabling ``gmysql-thread-closer`` tells PowerDNS to call ``mysql_thread_end()`` whenever a thread ends.
+
+Only enable this if you are certain you need to. For more discussion, see https://github.com/PowerDNS/pdns/issues/6231.
+
 Default Schema
 --------------
 
index e3033abe3b1a7a2a68b5ca93b01b17cf4b45451c..48707d3a95f250ea6780d2c21484fb2a05a02968 100644 (file)
@@ -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");
 
index 1d851daa91e3094a8fc637a35f6ee1220963a2f0..0b062574776f7bfe9ccc0232c9849d5113df8364 100644 (file)
 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;
 
@@ -419,6 +445,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");
 
@@ -467,8 +497,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();
 }
index 7e31d7b5644f1da8d405318253abe22e08f6d305..7a33e8c529d12357430a4cf3418320a6a3860eca 100644 (file)
@@ -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 */