You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Our server hosts a somewhat popular website. Occasionally (sometimes every few days, sometimes months between) we would get apache processes that chew through 100% CPU and require kill -9 to end. Analysing the core dumps showed us that the processes are stuck in MySQL client library internals in a weird, seemingly inexplicable state. I believe I figured out the issue, however:
DBD::mysql will, even in the newest version, call mysql_library_init()every time it connects. This isn't principally a problem, because that call does nothing if the library is already initialized. However, suppose Apache spawns a new process with two (or more) workers which both simultaneously connect to a database in separate threads. In that case, mysql_library_init() may be called simultaneously and start initializing itself twice, which would lead to the problems we've observed. The MySQL documentation states:
In a nonmultithreaded environment, the call to mysql_library_init() may be omitted, because mysql_init() invokes it automatically as necessary. However, mysql_library_init() is not thread-safe in a multithreaded environment, and thus neither is mysql_init(), which calls mysql_library_init(). You must either call mysql_library_init() prior to spawning any threads, or else use a mutex to protect the call, whether you invoke mysql_library_init() or indirectly through mysql_init(). Do this prior to any other client library call.
It's nearly impossible to reliably reproduce this issue, of course. I've written a patch on my local (non-production) machine and it doesn't appear to break anything, but obviously I can't tell for sure if this would actually solve the problem. The patch uses pthread_once() which should be available on pretty much any platform out there.
diff --git a/dbdimp.c b/dbdimp.c
index 39db033..35ebaab 100644
--- a/dbdimp.c
+++ b/dbdimp.c
@@ -57,6 +57,21 @@ typedef struct sql_type_info_s
int is_num;
} sql_type_info_t;
+/*
+ Ensure we only call mysql_library_init once, since that is not threadsafe.
+ Not doing this before had lead to crashes in Apache MPM workers.
+*/
+pthread_once_t once_mysql_initialized = PTHREAD_ONCE_INIT;
+
+static void init_mysql_library(void)
+{
+ mysql_library_init(0, NULL, NULL);
+}
+
+static void ensure_mysql_initialized()
+{
+ pthread_once(&once_mysql_initialized, init_mysql_library);
+}
/*
@@ -1206,7 +1221,7 @@ MYSQL *mysql_dr_connect(
#else
client_flag = CLIENT_FOUND_ROWS;
#endif
- mysql_library_init(0, NULL, NULL);
+ ensure_mysql_initialized();
mysql_init(sock);
if (imp_dbh)
diff --git a/dbdimp.h b/dbdimp.h
index 3d8c76b..ec208da 100644
--- a/dbdimp.h
+++ b/dbdimp.h
@@ -22,7 +22,7 @@
#include <mysql.h> /* Comes with MySQL-devel */
#include <mysqld_error.h> /* Comes MySQL */
#include <errmsg.h> /* Comes with MySQL-devel */
-
+#include <pthread.h>
#define true 1
#define false 0
Other information
No response
The text was updated successfully, but these errors were encountered:
DBD::mysql version
4.050
MySQL client version
8.0.41
Server version
8.0.41-0ubuntu0.22.04.1
Operating system version
Ubuntu 22.04.5 LTS
What happened?
Our server hosts a somewhat popular website. Occasionally (sometimes every few days, sometimes months between) we would get apache processes that chew through 100% CPU and require
kill -9
to end. Analysing the core dumps showed us that the processes are stuck in MySQL client library internals in a weird, seemingly inexplicable state. I believe I figured out the issue, however:DBD::mysql will, even in the newest version, call
mysql_library_init()
every time it connects. This isn't principally a problem, because that call does nothing if the library is already initialized. However, suppose Apache spawns a new process with two (or more) workers which both simultaneously connect to a database in separate threads. In that case,mysql_library_init()
may be called simultaneously and start initializing itself twice, which would lead to the problems we've observed. The MySQL documentation states:It's nearly impossible to reliably reproduce this issue, of course. I've written a patch on my local (non-production) machine and it doesn't appear to break anything, but obviously I can't tell for sure if this would actually solve the problem. The patch uses
pthread_once()
which should be available on pretty much any platform out there.Other information
No response
The text was updated successfully, but these errors were encountered: