Gary Mills <mills (AT) cc (DOT) umanitoba.ca> wrote:
Quote:
I have a daemon that's both an RPC server and a Mysql client. The
daemon is linked with libmysqlclient_r.so.12. The Mysql server runs
mysql-4.0.18. The daemon maintains a cache of connections to the
Mysql server. It spawns a thread in response to each incoming RPC
request, using one of the existing connections to make an SQL query. |
Connection pools are rarely necessary for MySQL and often do more harm
(allocating resources in the server) than benefits.
Quote:
The thread that initiates the connection calls mysql_init() and
mysql_real_connect(). Internally, the Mysql library also calls
mysql_thread_init() to allocate a block of memory. |
In fact this is a safety measure. Normally you are expected to call
mysql_thread_init() in each thread that will later call mysql_init().
If you use a connection pool, then there is probably exactly one
thread that is doing all the calls to mysql_init(). Then all memory
will be allocated from this threads local memory.
Quote:
This thread
eventually terminates, leaving that memory behind. Another thread
uses the connection to make an SQL query, but this one does not
allocate thread-specific memory. It terminates without a memory leak.
Yet another thread closes the connection with mysql_close() and
terminates. This one also has no thread-specific memory. |
The point here is "this thread eventually terminates". If you called
mysql_thread_end() before terminating this thread, then the allocated
memory will be freed. If you don't do that (it seems like so) then the
allocated memory will not be freed unless you end the complete server
process. Calling mysql_close() on any of those connections will
deallocate the memory. But since the thread which allocated it, no
longer exists, this memory is no longer accessible. In other words:
here is the memory leak.
Quote:
The Mysql documentation recommends calling mysql_thread_end() to free
the thread-specific memory, but that method doesn't seem to fit this
threading model. |
Exactly. There are two clean approaches here:
1. allocate the memory in one thread - then this thread must not be
terminated through the lifetime of your demon. All memory will be
allocated from this threads local memory. mysql_close() will return
the memory to this thread, so it can be reused, i.e. for subsequent
calls to mysql_init().
-or-
2. have each worker thread run mysql_init() on its own and allocate
from its own local memory. Then mysql_thread_end() should be called
before terminating this thread. Memory will not leak.
Quote:
The easy fix is to call mysql_thread_end()
immediately after mysql_init() in the function that creates the
connection. That eliminates the memory leak. The daemon still works. |
Pure luck. It depends on how exactly the thread library handles this.
But the freed memory could be re-used any time and then you will get
all kinds of problems from silently corrupted data to segfaults.
Quote:
Is this a dangerous thing to do? |
Yes! You're relying on undefined behavior.
Quote:
Is there a better way to handle this situation? |
Do it correctly. Don't use a connection pool.
XL