Изящная обработка критических ошибок 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(). Поскольку соединения нет, вы не можете отправить БД вызов фиксации. Кроме того, похоже, что у вас включен AutoCommit, поэтому БД пытается закрыть любые открытые транзакции при удалении соединения в вызове 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
Если бы я хотел не выходить из цикла, а просто подождать некоторое время, пока БД вернется, прежде чем продолжить, я мог бы просто вызвать sleep на некоторое время? Кроме того, $msg заполняется автоматически из команды execute? - person growse; 29.12.2011
comment
Если вы не хотите выходить, вам придется создать новое соединение. Замените break новым вызовом DBI->connect(). Вам придется реструктурировать цикл для поддержки повторных попыток, чтобы не терять сетевые сообщения. - person saarp; 29.12.2011
comment
На самом деле я не очень беспокоюсь об отключении сетевого трафика, хотя я ценю эту мысль. Я пойду и посмотрю, что я могу найти. - person growse; 29.12.2011