FAQ
Hi,

I've been trying to implement a timeout wrapper for my database
functions to handle a database connection that is remote and therefore
could either become very slow of the link could disappear completely.

I'm using something like this:

use DBI;
use Sys::SigAction;

my($dbh);
eval {
my $h = set_sig_handler('ALRM',sub {die "timeout";}, { trap => [
'ALRM', 'INT' ], safe=>0 });
alarm(30);
$dbh = DBI->connect_cached(...);
alarm(0);
};
alarm(0);
if($@) {
...
}

This works fine if the connection to the database is not already
established and times out after 30 seconds correctly; however if the
database connection was established previously (and cached) it takes >
200 seconds to return instead of the expected 30 seconds.

An strace shows the following:

rt_sigaction(SIGALRM, NULL, {SIG_DFL}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [ALRM], ~[KILL STOP RTMIN RT_1], 8) = 0
rt_sigaction(SIGALRM, {0xd30835, [], SA_RESTORER, 0xc29a98}, {SIG_DFL},
8) = 0
rt_sigprocmask(SIG_SETMASK, ~[KILL STOP RTMIN RT_1], NULL, 8) = 0
rt_sigaction(SIGALRM, {0xd3419d, [ALRM], SA_RESTORER, 0xc29a98}, NULL,
8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
alarm(30) = 0
rt_sigaction(SIGPIPE, {SIG_IGN}, {SIG_DFL}, 8) = 0
send(6, "Q\0\0\0\37SELECT \'DBD::Pg ping test\'\0", 32, 0) = 32
rt_sigaction(SIGPIPE, {SIG_DFL}, {SIG_IGN}, 8) = 0
poll(
[{fd=6, events=POLLIN|POLLERR}], 1, -1) = -1 EINTR (Interrupted system call)
--- SIGALRM (Alarm clock) @ 0 (0) ---
rt_sigprocmask(SIG_UNBLOCK, [ALRM], NULL, 8) = 0
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 9
setsockopt(9, SOL_TCP, TCP_NODELAY, [1], 4) = 0
fcntl64(9, F_SETFL, O_RDONLY|O_NONBLOCK) = 0
fcntl64(9, F_SETFD, FD_CLOEXEC) = 0
connect(9, {sa_family=AF_INET, sin_port=htons(5432),
sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in
progress)
poll(
*** hangs here for ~280 seconds ***

Does anyone have any idea as to what I'm doing wrong or how to
workaround this??

Kind regards,
Steve.

Search Discussions

  • Lincoln A. Baxter at Feb 28, 2009 at 3:31 am
    Hey Steve,

    I started to write you a message asking for a different strace, when I
    noticed that you are passing attributes for set_sig_handler that
    Sys::SigAction has no knowledge of:

    my $h = set_sig_handler('ALRM',sub {die "timeout";}, { trap => ['ALRM', 'INT' ], safe=>0 });

    The "trap" key in the hashref: { trap => ['ALRM', 'INT' ], safe=>0 } is
    not used by Sys::SigAction.

    This reference is passed from set_sig_handler() (line 91) to
    mk_sig_action() (defined on line 94) where the 'flags' and the 'mask'
    keys are referenced (lines 98 and 99).

    Sys::SigAction does not know anything about the key 'trap'. And i don't
    know how masking ALRM or INT can help you, and flags is used for setting
    sigaction flags usually in the form of SA_flag that are passed to
    sigaction() by perl.

    Could you explain or fix this code, and then try again?

    Lincoln
    Author of Sys::SigAction
    On Tue, 2009-02-24 at 15:37 +0000, Steve Freegard wrote:
    Hi,

    I've been trying to implement a timeout wrapper for my database
    functions to handle a database connection that is remote and therefore
    could either become very slow of the link could disappear completely.

    I'm using something like this:

    use DBI;
    use Sys::SigAction;

    my($dbh);
    eval {
    my $h = set_sig_handler('ALRM',sub {die "timeout";}, { trap => [
    'ALRM', 'INT' ], safe=>0 });
    alarm(30);
    $dbh = DBI->connect_cached(...);
    alarm(0);
    };
    alarm(0);
    if($@) {
    ...
    }

    This works fine if the connection to the database is not already
    established and times out after 30 seconds correctly; however if the
    database connection was established previously (and cached) it takes >
    200 seconds to return instead of the expected 30 seconds.

    An strace shows the following:

    rt_sigaction(SIGALRM, NULL, {SIG_DFL}, 8) = 0
    rt_sigprocmask(SIG_BLOCK, [ALRM], ~[KILL STOP RTMIN RT_1], 8) = 0
    rt_sigaction(SIGALRM, {0xd30835, [], SA_RESTORER, 0xc29a98}, {SIG_DFL},
    8) = 0
    rt_sigprocmask(SIG_SETMASK, ~[KILL STOP RTMIN RT_1], NULL, 8) = 0
    rt_sigaction(SIGALRM, {0xd3419d, [ALRM], SA_RESTORER, 0xc29a98}, NULL,
    8) = 0
    rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
    alarm(30) = 0
    rt_sigaction(SIGPIPE, {SIG_IGN}, {SIG_DFL}, 8) = 0
    send(6, "Q\0\0\0\37SELECT \'DBD::Pg ping test\'\0", 32, 0) = 32
    rt_sigaction(SIGPIPE, {SIG_DFL}, {SIG_IGN}, 8) = 0
    poll(
    [{fd=6, events=POLLIN|POLLERR}], 1, -1) = -1 EINTR (Interrupted system call)
    --- SIGALRM (Alarm clock) @ 0 (0) ---
    rt_sigprocmask(SIG_UNBLOCK, [ALRM], NULL, 8) = 0
    socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 9
    setsockopt(9, SOL_TCP, TCP_NODELAY, [1], 4) = 0
    fcntl64(9, F_SETFL, O_RDONLY|O_NONBLOCK) = 0
    fcntl64(9, F_SETFD, FD_CLOEXEC) = 0
    connect(9, {sa_family=AF_INET, sin_port=htons(5432),
    sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in
    progress)
    poll(
    *** hangs here for ~280 seconds ***

    Does anyone have any idea as to what I'm doing wrong or how to
    workaround this??

    Kind regards,
    Steve.
  • Henri Asseily at Feb 28, 2009 at 4:41 am
    From DBIx::HA (fudged a bit):

    sub _connect_with_timeout {
    my ($dsn, $username, $auth, $attrs) = @_;
    my $res;
    my $dbh;
    my $timeout = 0;
    eval {
    no strict;
    my $h = set_sig_handler(
    'ALRM',
    sub { $timeout = 1; die 'TIMEOUT'; },
    { mask=>['ALRM'], safe=>1 }
    );
    alarm(30);
    $dbh = DBI->connect_cached($dsn, $username, $auth,
    $attrs);
    alarm(0);
    };
    alarm(0);
    if ($@ or $timeout) { # there's a problem above
    if ($timeout) { # it's a timeout
    warn "$prefix *** CONNECT TIMED OUT in $dsn";
    eval { $dbh->disconnect };
    $dbh = undef;
    } else { # problem in the connection
    warn "$prefix Error in DBI::connect: $@\n" if
    $@;
    }
    }
    $dbh->{private_dbixha_dsn} = $dsn if $dbh;
    return $dbh;
    }

    On Feb 27, 2009, at 9:31 PM, Lincoln A. Baxter wrote:

    Hey Steve,

    I started to write you a message asking for a different strace, when I
    noticed that you are passing attributes for set_sig_handler that
    Sys::SigAction has no knowledge of:

    my $h = set_sig_handler('ALRM',sub {die "timeout";}, { trap =>
    ['ALRM', 'INT' ], safe=>0 });

    The "trap" key in the hashref: { trap => ['ALRM', 'INT' ], safe=>0 }
    is
    not used by Sys::SigAction.

    This reference is passed from set_sig_handler() (line 91) to
    mk_sig_action() (defined on line 94) where the 'flags' and the 'mask'
    keys are referenced (lines 98 and 99).

    Sys::SigAction does not know anything about the key 'trap'. And i
    don't
    know how masking ALRM or INT can help you, and flags is used for
    setting
    sigaction flags usually in the form of SA_flag that are passed to
    sigaction() by perl.

    Could you explain or fix this code, and then try again?

    Lincoln
    Author of Sys::SigAction
    On Tue, 2009-02-24 at 15:37 +0000, Steve Freegard wrote:
    Hi,

    I've been trying to implement a timeout wrapper for my database
    functions to handle a database connection that is remote and
    therefore
    could either become very slow of the link could disappear completely.

    I'm using something like this:

    use DBI;
    use Sys::SigAction;

    my($dbh);
    eval {
    my $h = set_sig_handler('ALRM',sub {die "timeout";}, { trap => [
    'ALRM', 'INT' ], safe=>0 });
    alarm(30);
    $dbh = DBI->connect_cached(...);
    alarm(0);
    };
    alarm(0);
    if($@) {
    ...
    }

    This works fine if the connection to the database is not already
    established and times out after 30 seconds correctly; however if the
    database connection was established previously (and cached) it
    takes >
    200 seconds to return instead of the expected 30 seconds.

    An strace shows the following:

    rt_sigaction(SIGALRM, NULL, {SIG_DFL}, 8) = 0
    rt_sigprocmask(SIG_BLOCK, [ALRM], ~[KILL STOP RTMIN RT_1], 8) = 0
    rt_sigaction(SIGALRM, {0xd30835, [], SA_RESTORER, 0xc29a98},
    {SIG_DFL},
    8) = 0
    rt_sigprocmask(SIG_SETMASK, ~[KILL STOP RTMIN RT_1], NULL, 8) = 0
    rt_sigaction(SIGALRM, {0xd3419d, [ALRM], SA_RESTORER, 0xc29a98},
    NULL,
    8) = 0
    rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
    alarm(30) = 0
    rt_sigaction(SIGPIPE, {SIG_IGN}, {SIG_DFL}, 8) = 0
    send(6, "Q\0\0\0\37SELECT \'DBD::Pg ping test\'\0", 32, 0) = 32
    rt_sigaction(SIGPIPE, {SIG_DFL}, {SIG_IGN}, 8) = 0
    poll(
    [{fd=6, events=POLLIN|POLLERR}], 1, -1) = -1 EINTR (Interrupted
    system call)
    --- SIGALRM (Alarm clock) @ 0 (0) ---
    rt_sigprocmask(SIG_UNBLOCK, [ALRM], NULL, 8) = 0
    socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 9
    setsockopt(9, SOL_TCP, TCP_NODELAY, [1], 4) = 0
    fcntl64(9, F_SETFL, O_RDONLY|O_NONBLOCK) = 0
    fcntl64(9, F_SETFD, FD_CLOEXEC) = 0
    connect(9, {sa_family=AF_INET, sin_port=htons(5432),
    sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation
    now in
    progress)
    poll(
    *** hangs here for ~280 seconds ***

    Does anyone have any idea as to what I'm doing wrong or how to
    workaround this??

    Kind regards,
    Steve.
  • Steve Freegard at Feb 28, 2009 at 11:31 am
    Hi Henri,

    Henri Asseily wrote:
    From DBIx::HA (fudged a bit):
    Thanks for the info; I changed my function to loosely match, but it
    still fails to actually die and immediately return from the eval{} block.

    Here's the function now:

    # Create database connection
    my($dbh);
    my($alarm) = 30;
    eval {
    no strict;
    my $h = set_sig_handler('ALRM', sub {die "timeout";}, {mask=>['ALRM'],
    safe=>1});
    alarm($alarm);
    $dbh = MailWatchCommon::db_connect();
    alarm(0);
    };
    alarm(0);
    if($@) {
    MailScanner::Log::WarnLog("MailWatch Error: Unable to initialise
    database connection: %s", $@);
    DisableMailWatchLogging();
    return undef;
    }

    MailWatchCommon::db_connect does this:

    # Database
    use DBI;
    use DBD::Pg qw(:pg_types);

    sub db_connect {
    # Use first argument as cache key
    my($cachekey) = (@_) ? shift : 'DEFAULT';
    my($dsn) =
    "DBI:Pg:dbname=".$cfg{'database.name'}.";host=".$cfg{'database.host'};
    my($dbh);
    $dbh = DBI->connect_cached($dsn, $cfg{'database.user'},
    $cfg{'database.password'}, { RaiseError => 1, InactiveDestroy => 1,
    AutoCommit => 1, ShowErrorStatement => 1, private_mailwatch_cachekey =>
    $cachekey });
    return $dbh;
    }

    Here's the strace:

    rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1], [], 8) = 0
    rt_sigaction(SIGALRM, NULL, {SIG_DFL}, 8) = 0
    rt_sigprocmask(SIG_BLOCK, [ALRM], ~[KILL STOP RTMIN RT_1], 8) = 0
    rt_sigaction(SIGALRM, {0xd30835, [], SA_RESTORER, 0xc29a98}, {SIG_DFL},
    8) = 0
    rt_sigprocmask(SIG_SETMASK, ~[KILL STOP RTMIN RT_1], NULL, 8) = 0
    rt_sigaction(SIGALRM, {0xd30835, [ALRM], SA_RESTORER, 0xc29a98}, NULL,
    8) = 0
    rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
    alarm(30) = 0
    rt_sigaction(SIGPIPE, {SIG_IGN}, {SIG_DFL}, 8) = 0
    send(9, "Q\0\0\0\37SELECT \'DBD::Pg ping test\'\0", 32, 0) = 32
    rt_sigaction(SIGPIPE, {SIG_DFL}, {SIG_IGN}, 8) = 0
    poll([{fd=9, events=POLLIN|POLLERR}], 1, -1) = -1 EINTR (Interrupted
    system call)
    --- SIGALRM (Alarm clock) @ 0 (0) ---
    sigreturn() = ? (mask now [])
    poll(

    This is running on Linux 2.6.9, Perl 5.8.8, DBI 1.607 and DBD::Pg 2.10.7

    Kind regards,
    Steve.
  • Steve Freegard at Feb 28, 2009 at 11:46 am
    Hi Lincoln,

    Thanks for the reply.

    Lincoln A. Baxter wrote:
    Hey Steve,

    I started to write you a message asking for a different strace, when I
    noticed that you are passing attributes for set_sig_handler that
    Sys::SigAction has no knowledge of:

    my $h = set_sig_handler('ALRM',sub {die "timeout";}, { trap => ['ALRM', 'INT' ], safe=>0 });

    The "trap" key in the hashref: { trap => ['ALRM', 'INT' ], safe=>0 } is
    not used by Sys::SigAction.

    This reference is passed from set_sig_handler() (line 91) to
    mk_sig_action() (defined on line 94) where the 'flags' and the 'mask'
    keys are referenced (lines 98 and 99).

    Sys::SigAction does not know anything about the key 'trap'. And i don't
    know how masking ALRM or INT can help you, and flags is used for setting
    sigaction flags usually in the form of SA_flag that are passed to
    sigaction() by perl.

    Could you explain or fix this code, and then try again?
    I've fixed the code, the 'trap' key was a typo - I've changed it to
    'mask' as per the documentation:

    # Create database connection
    my($dbh);
    my($timeout) = 0;
    eval {
    no strict;
    my $h = set_sig_handler('ALRM', sub {$timeout=1; die "timeout";},
    {mask=>['ALRM','INT'], safe=>0});
    alarm($alarm);
    $dbh = MailWatchCommon::db_connect();
    alarm(0);
    };
    alarm(0);
    if($@) {
    MailScanner::Log::WarnLog("MailWatch Error: Unable to initialise
    database connection: %s", $@);
    DisableMailWatchLogging();
    return undef;
    }

    Still no joy - the eval{} doesn't return on SIGALRM; here's the strace:

    rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1], [], 8) = 0
    rt_sigaction(SIGALRM, NULL, {SIG_DFL}, 8) = 0
    rt_sigprocmask(SIG_BLOCK, [ALRM], ~[KILL STOP RTMIN RT_1], 8) = 0
    rt_sigaction(SIGALRM, {0xd30835, [], SA_RESTORER, 0xc29a98}, {SIG_DFL},
    8) = 0
    rt_sigprocmask(SIG_SETMASK, ~[KILL STOP RTMIN RT_1], NULL, 8) = 0
    rt_sigaction(SIGALRM, {0xd3419d, [INT ALRM], SA_RESTORER, 0xc29a98},
    NULL, 8) = 0
    rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
    alarm(30) = 0
    rt_sigaction(SIGPIPE, {SIG_IGN}, {SIG_DFL}, 8) = 0
    send(6, "Q\0\0\0\37SELECT \'DBD::Pg ping test\'\0", 32, 0) = 32
    rt_sigaction(SIGPIPE, {SIG_DFL}, {SIG_IGN}, 8) = 0
    poll([{fd=6, events=POLLIN|POLLERR}], 1, -1) = -1 EINTR (Interrupted
    system call)
    --- SIGALRM (Alarm clock) @ 0 (0) ---
    rt_sigprocmask(SIG_UNBLOCK, [ALRM], NULL, 8) = 0
    socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 9
    setsockopt(9, SOL_TCP, TCP_NODELAY, [1], 4) = 0
    fcntl64(9, F_SETFL, O_RDONLY|O_NONBLOCK) = 0
    fcntl64(9, F_SETFD, FD_CLOEXEC) = 0
    connect(9, {sa_family=AF_INET, sin_port=htons(5432),
    sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in
    progress)
    poll(

    This is on Linux 2.6.9, Perl 5.8.8, DBI 1.607 and DBD::Pg 2.10.7.

    Is appears that the poll() interrupted by the alarm is simply restarted
    again after by DBI or DBD::Pg.

    Kind regards,
    Steve.

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupdbi-users @
categoriesperl
postedFeb 24, '09 at 3:40p
activeFeb 28, '09 at 11:46a
posts5
users3
websitedbi.perl.org

People

Translate

site design / logo © 2022 Grokbase