Обработване на Perl критични DBD::ODBC грешки грациозно

Имам прост Perl скрипт, който слуша мрежов порт, приема данни и записва неща в база данни, използвайки DBD::ODBC. Изглежда донякъде така:

#!/usr/bin/perl
my $dbh = DBI->connect('dbi:ODBC:SqlProd',"yay","ooo",{AutoCommit => 0}) || die "Couldn't connect to db";
my $sth = $dbh->prepare("insert into table some stuff");

$sock = IO::Socket::INET->new(LocalPort => '1234', Proto => 'udp')||die("Socket: $@");
do {
    $sock->recv($buf, $MAXLEN);

    /*parse some data here*/
    /*bind some parameters to $sth here*/

    my $rv = $sth->execute() || warn logit('warning', "Error inserting to db: $! $msg");
    $dbh->commit() || warn logit('err',"Error committing db transaction: $! $msg");

}while(1);

Това, въпреки че е малко грубо, работи доста добре. Въпреки това, по неизвестни причини, базата данни пада и скриптът се срива, казвайки неща като:

DBD::ODBC::st execute failed: [unixODBC][FreeTDS][SQL Server]Write to the server failed     (SQL-08S01) at /usr/local/bin/haproxy-syslog line 117.
0 at /usr/local/bin/haproxy-syslog line 117.
DBD::ODBC::db commit failed: [unixODBC][FreeTDS][SQL Server]Could not perform COMMIT or     ROLLBACK (SQL-HY000) at /usr/local/bin/haproxy-syslog line 118.
Error committing db transaction: Connection reset by peer 
DBD::ODBC::db DESTROY failed: [unixODBC][FreeTDS][SQL Server]Could not perform COMMIT or ROLLBACK (SQL-HY000) during global destruction.

Ако исках скриптът да е устойчив на този вид повреда, или просто като изчакам определен период от време и видя дали базата данни е будна отново, или изпусна получените данни и изтегля още данни от сокета, какво би било най-добрият начин да го направите? Горната грешка означава ли, че $dbh в крайна сметка е боклук и трябва да бъде повторно инициализирано?


person growse    schedule 28.12.2011    source източник


Отговори (1)


Проблемът е причинен, защото въпреки че execute() е неуспешно, вие все още извиквате commit(). Тъй като няма връзка, не можете да изпратите на DB повикването за извършване. Освен това изглежда, че сте включили AutoCommit, така че DB се опитва да затвори всички отворени транзакции, когато изхвърля връзката в повикването DESTROY.

Можете да опитате нещо подобно (простете ми ръждивия perl):

do {
    $sock->recv($buf, $MAXLEN);

    # parse some data here
    # bind some parameters to $sth here

    my $rv = $sth->execute() || warn logit('warning', "Error inserting to db: $! $msg");
    if ($msg ~= /connection reset/i)
    {
        warn logit('warning', "Connection disconnected.");
        $sth->disconnect();
        break; # exit while
    }
    $dbh->commit() || warn logit('err',"Error committing db transaction: $! $msg");

}while(1);
person saarp    schedule 29.12.2011
comment
Ако исках да не излизам от цикъла, а просто да изчакам известно време DB да се върне, преди да продължа, мога просто да извикам sleep за малко? Също така, $msg попълва ли се автоматично от командата execute? - person growse; 29.12.2011
comment
Ако искате да не излезете, ще трябва да създадете нова връзка. Заменете break с ново повикване до DBI->connect(). Ще трябва да преструктурирате цикъла си, за да поддържате повторни опити, така че да не изпускате мрежови съобщения. - person saarp; 29.12.2011
comment
Всъщност не съм толкова притеснен за намаляването на мрежовия трафик, въпреки че оценявам мисълта. Ще пробвам и ще видя какво мога да намеря. - person growse; 29.12.2011