Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Timeout Failing

by beckmanel (Novice)
on Oct 23, 2017 at 14:07 UTC ( [id://1201893]=perlquestion: print w/replies, xml ) Need Help??

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

Gentlepersons - I surrounded db change code with a timeout, hoping that if there was a problem with the DB I/O, and it hung, the timeout would free up the program and allow it to continue. However, it does not seem to be working, and the program is hanging up. This is on a Windows 10 server. Did I miswrite the timeout, or is there an issue with Windows, or something else ?

my $sth; my $res; eval{ local $SIG{ALRM} = sub { die "db_timeout" }; alarm 12; $sth = $dbh->prepare($sql); if (! defined $sth) { print LOG "ERROR: insert prepare failed\n"; } else { $res = $sth->execute; } alarm 0; }; if ($@) { if ($@ !~ /db_timeout/) { print LOG "ERROR: Alarm during insert execute othe +r than db_timeout: $@\n"; } else { print LOG "ERROR: db_timeout: $@\n"; } }

Replies are listed 'Best First'.
Re: Timeout Failing
by huck (Prior) on Oct 23, 2017 at 15:07 UTC
Re: Timeout Failing
by thanos1983 (Parson) on Oct 23, 2017 at 15:12 UTC

    Hello beckmanel,

    From the documentation DBI/Signal Handling and Canceling Operations:

    my $failed; eval { local $SIG{ALRM} = sub { die "TIMEOUT\n" }; # N.B. \n required eval { alarm($seconds); ... code to execute with timeout here (which may die) ... 1; } or $failed = 1; # outer eval catches alarm that might fire JUST before this alarm( +0) alarm(0); # cancel alarm (if code ran fast) die "$@" if $failed; 1; } or $failed = 1; if ( $failed ) { if ( defined $@ and $@ eq "TIMEOUT\n" ) { ... } else { ... } # some other error }

    I am not able to replicate your code so I can identify the problem but the code bellow works for me:

    my $failed; my $seconds = 12; eval{ Config::Simple->import_from("".$path."", \%config) or die Config::Simple->error(); my $dbh = DBI->connect("dbi:mysql::".$config{'MySQL.host'}.":".$co +nfig{'MySQL.port'}."", "".$config{'MySQL.user'}."", "".$config{'MySQL.pass'}."", { 'PrintError' => 1, 'RaiseError' => 1 , 'AutoInactiveD +estroy' => 1 } ) or die "Could not connect to ". $config{'MySQL.host'} .": ". $DB +I::errstr ."\n"; local $SIG{ALRM} = sub { die "db_timeout" }; eval { alarm($seconds); $dbh->do("USE Thanos1983"); # or die "Error: " .dbh->errstr. "\n"; 1; } or $failed = 1; # outer eval catches alarm that might fire JUST before this alarm( +0) alarm(0); # cancel alarm (if code ran fast) die "$@" if $failed; 1; } or $failed = 1; if ( $failed ) { if ( defined $@ and $@ eq "TIMEOUT\n" ) { ... } else { print "Error\n" } # some other error } __END__ $ perl test.pl DBD::mysql::db do failed: Unknown database 'Thanos1983' at test.pl lin +e 30. Error

    Also usually when I use prepare() I use it like this:

    my $sth = $dbh->prepare("SELECT `ID`, `Y-Values` FROM `".$config{'MySQ +L.table'}."` WHERE 1"); if (!$sth->execute()) { die "Error: ". $sth->errstr ."\n"; }

    Update: For login purposes I prefer to use Log::Log4perl.

    Hope this helps, BR.

    Seeking for Perl wisdom...on the process of learning...not there...yet!
Re: Timeout Failing
by vr (Curate) on Oct 25, 2017 at 19:05 UTC

    Code like below used to help me to cope with timeouts for blocking IO on Windows. Of course introducing threads may be undesirable or totally unusable for your purposes, i.e. not sure if it's worth anything.

    use strict; use warnings; use threads; use threads::shared; my $err :shared = ''; sub timed_action (&$;@) { my ( $code, $timeout, @args ) = @_; local $SIG{ TERM } = sub { threads-> exit }; lock( $err = '' ); my $t = async { { lock $err } my $result = eval { $code-> ( @args )}; lock( $err = $@ ); cond_signal $err; return $result }; if ( !cond_timedwait $err, time + $timeout ) { $err = 'Timeout!!!'; $t-> kill( 'TERM' ); } if ( $err ) { print "$err\n"; $t-> detach; return undef } return $t-> join; } # let's test it use LWP::Simple; timed_action { get 'http://10.255.255.1' or die } 3;
Re: Timeout Failing
by holli (Abbot) on Oct 23, 2017 at 14:29 UTC
    Remove the eval.
    C:\>perl -e "eval { local $SIG{ALRM} = sub { die 'db_timeout' }; alarm + 3; sleep 4; }; print 1" 1 C:\>perl -e "local $SIG{ALRM} = sub { die "db_timeout" }; alarm 3; sle +ep 4;" db_timeout at -e line 1. C:\>
    Edit: No coffee yet.


    holli

    You can lead your users to water, but alas, you cannot drown them.
Re: Timeout Failing
by Anonymous Monk on Oct 25, 2017 at 04:11 UTC
    And, generically speaking, "a timeout, in such a situation, can never be your friend," because you have absolutely no way to know what it is that the timer might be interrupting, at the precise moment that it occurs." Although the results will not be catastrophic to the database itself, since it's being managed by an external server, it could very easily leave the database in an unknown state. If something isn't finishing on time, there are no easy shortcuts: you have to find, and fix, the underlying bug in your program.

      Never? Never is a very big word.

      If you're in a transaction, it doesn't matter what the timer interrupts and it will all be left in a predictable state. Tools like DBIx::Class with DBIx::Class::Storage::TxnScopeGuard and perhaps Sys::SigAction make things like what the OP requested semitrivial.

      Don't give generic, half-right advice; fixing the issue is indeed the best thing to do here. Give 100% on-point advice with working code or don't participate.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1201893]
Approved by Corion
Front-paged by haukex
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (3)
As of 2024-04-24 19:40 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found