mtoconno has asked for the wisdom of the Perl Monks concerning the following question:

About five years ago there was a question about enforcing DBI timeouts. The advice then was to simply use alarm() to break out of the DBI call that is taking too long. However, when I do this using the MySQL driver (DBD::mysql) it appears that something is eating SIGALRM. When I strace the process I see that SIGALRM arrives in a timely fashion but my handler is not called for several minutes.

Has anyone else ever encountered this problem? I did not have much luck googling for it, but the problem seems pretty severe to me. The situation we are trying to avoid is one where the network becomes unavailable after the initial database connection is made (there's a DSN parameter to timeout the initial connection).

I have been using the following code to test. The code connects to the database and then goes to sleep. While it is sleeping I disable the network by pulling out the networking cable from my computer. The result I want, after it wakes back up, is for my alarm to be called and my handler invoked immediately. The result I see (with help from strace) is that the signal arrives and then about 10 minutes later my handler is called. Here's the code:

#!/usr/bin/perl use strict; use warnings; use DBI; $| = 1; my $db = "your database here"; my $host = 'your.server.here'; my $port = 3306; my $dsn = "dbi:mysql:database=$db;host=$host;port=$port"; my $user = 'your user name here'; my $pass = 'your password here'; my $dbh = DBI->connect($dsn, $user, $pass); print "Connection made. Sleeping so you can disable the network: "; sleep(3); print "done\n"; eval { local $SIG{ALRM} = sub {die "timeout\n"}; alarm(1); eval { print $dbh->ping() ? "ping succeeded\n" : "ping failed\n"; }; alarm(0); die "$@\n" if $@; }; if ($@) { die "$@\n" if $@ !~ /^timeout/; print "ping failed (timeout)\n"; }

Replies are listed 'Best First'.
Re: DBI Timeouts and MySQL
by samtregar (Abbot) on Jun 02, 2005 at 22:31 UTC
    What version of Perl are you using? In version 5.8.0 Perl switched from supporting real asynchronous signals to something called safe signals. Safe signals only trigger between op-codes, if I understand things correctly, which in this case probably means after the DBI XS code is finished thus defeating your attempt at timing out DBI.

    The nice thing about safe signals is that they no longer cause seg-faults, which is what your code would probably do if it really did interupt DBI halfway through a request!

    UPDATE: Did you read the section on this in the DBI docs? It sounds like POSIX::sigaction() or Sys::SigAction may be the correct, if still rather dangerous, work-around to the problem posed by safe signals.

    -sam