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
--------------
getArg("password"),
getArg("group"),
mustDo("innodb-read-committed"),
- getArgAsNum("timeout")));
+ getArgAsNum("timeout"),
+ mustDo("thread-cleanup")));
}
class gMySQLFactory : public BackendFactory
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");
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;
int retry=1;
Lock l(&s_myinitlock);
+ if (d_threadCleanup) {
+ threadcloser.enable();
+ }
+
if (!mysql_init(&d_db))
throw sPerrorException("Unable to initialize mysql driver");
}
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();
}
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();
unsigned int d_timeout;
uint16_t d_port;
bool d_setIsolation;
+ bool d_threadCleanup;
};
#endif /* SSMYSQL_HH */