DBD-mysql
DBD-mysql copied to clipboard
ping() on disconnected db handles
With the latest version of DBD::mysql one of my testcases is failing. The testcase is basically just doing this
$dbh->disconnect();
ok( ! $dbh->ping(), 'dbh is diconnected');
We tracked this behaviour down to the commit 998cd6a374a89057ee8d080e25c2612fbc7c3810, which is reopening a diconnected DBH.
The POD of DBI for ping says:
Attempts to determine, in a reasonably efficient way, if the database server is still running and the connection to it is still working
I am not perfectly sure but i would argue that ping should not reopen the connection.
Implicit reconnection is definitely a bad idea!
@dveeden there is a configuration already to enable this behavior mysql_auto_reconnect which is highly discouraged because it is bad to have a handle reconnect while you think you are in a transaction, or have table locks, or other session state that would be lost when reconnecting. This should not be done without this option set!
Hi. Is there any reason why a implicit reconnect was implemented in version 4.050? Can't find any change documentation / requirements.
Yes there was a reason to do that, but I don't recall the exact reason from the top of my head. I agree that a ping should be done to keep the connection alive, not to re-connect.
However with mysql_auto_reconnect and a lot of historic behaviour this is probably not how it is working today.
This causes a segfault with mariadb versions 10.3 and higher, if you call ping after disconnect.
use Test::More; use DBI;
say "please provide db name"; my $dbname = <STDIN>; chomp $dbname; say "please provide username"; my $user = <STDIN>; chomp $user; say "please provide password"; my $password = <STDIN>; chomp $password; my $dbh = DBI->connect("dbi:mysql:$dbname;host=localhost",$user, $password); $dbh->disconnect(); ok( ! $dbh->ping(), 'dbh is disconnected and did not segv');
done_testing();
I've been able to implement a workaround where disconnect is wrapped in a sub, and a flag set if the database version contains mariadb and version is 10.3 or higher, then checking that flag and forcing reconnect without calling ping if that flag is set (and unsetting the flag on successful connection)
Hello, just to note that DBI:MariaDB driver has fixed this problem and works fine with MariaDB 10.3.
Hello, just to note that
DBI:MariaDBdriver has fixed this problem and works fine with MariaDB 10.3.
Hi Pali, no DBD::MariaDB does not fix the ping following disconnect segfault. I've replicated it on debian buster and stretch with both DBD::MariaDB and DBD::mysql against MariaDB 10.3 and 10.4 - both needed the workaround
@hashbangperl if there is still segfault with DBD::MariaDB please report it to our project https://github.com/gooddata/DBD-MariaDB/issues and we look at it... but it is strange as I was testing specially this problem against 10.3 and there was no crash anymore.
@hashbangperl if there is still segfault with DBD::MariaDB please report it to our project https://github.com/gooddata/DBD-MariaDB/issues and we look at it... but it is strange as I was testing specially this problem against 10.3 and there was no crash anymore.
created issue. yeah, I got the problem between 1 in 2 and 1 in 4 times when running, and with the autoreconnect flag set.
I've managed to trigger a segfault inside gdb with mysql dbd against mariadb, using the test above. I was working the machine hard, as it seems to happen when mariadb is garbage collecting and under memory constraints.
gdb -ex r -ex bt -ex quit --args perl mariadb_disconnect_bug.t GNU gdb (Debian 8.2.1-2+b3) 8.2.1 Copyright (C) 2018 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: http://www.gnu.org/software/gdb/bugs/. Find the GDB manual and other documentation resources online at: http://www.gnu.org/software/gdb/documentation/.
For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from perl...(no debugging symbols found)...done. Starting program: /opt/perl5/bin/perl mariadb_disconnect_bug.t [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1"
Program received signal SIGSEGV, Segmentation fault.
0x0000555555dfe910 in ?? ()
#0 0x0000555555dfe910 in ?? ()
#1 0x00007ffff7897c7d in mariadb_reconnect () from /usr/lib/x86_64-linux-gnu/libmariadb.so.3
#2 0x00007ffff7898379 in ?? () from /usr/lib/x86_64-linux-gnu/libmariadb.so.3
#3 0x00007ffff7896f20 in mysql_ping () from /usr/lib/x86_64-linux-gnu/libmariadb.so.3
#4 0x00007ffff78d952c in XS_DBD__mysql__db_ping (cv=
Inferior 1 [process 7980] will be killed.
added PR with extra tests, which should fail if using mariadb 10.3 or newer server that's under load. https://github.com/perl5-dbi/DBD-mysql/pull/319
Hi, all,
I've had a chance to look at this briefly. Here is where the code is segfaulting in mariadb_lib.c:
/* check if connection handler is active */
if (IS_CONNHDLR_ACTIVE(mysql))
{
if (mysql->extension->conn_hdlr->plugin && mysql->extension->conn_hdlr->plugin->reconnect)
return(mysql->extension->conn_hdlr->plugin->reconnect(mysql)); // SEGFAULT
}
I believe the error is in mysql_close in mariadb_lib.c. Have a look at this code:
if (mysql->net.extension)
free(mysql->net.extension);
mysql->host_info=mysql->user=mysql->passwd=mysql->db=0;
/* Clear pointers for better safety */
memset((char*) &mysql->options, 0, sizeof(mysql->options));
if (mysql->extension)
free(mysql->extension);
mysql_close frees msyql->net.extension and mysql->extension, but does NOT set them to NULL. So they're pointing to freed memory and when they are reused... SEGV.
I will try and patch mariadb_lib.c to see if my theory is correct and if I can fix it.
Regards,
Dianne.
This patch to the MariaDB client library seems to fix the problem. patch.txt
I believe the patch that caused this needs to be reverted and implemented properly. Currently, the workaround to return the previous behavior is to use AutoCommit = 0. This is absolutely wrong, and the said patch should check mysql_auto_reconnect instead.