The OpenNET Project / Index page

[ новости/++ | форум | wiki | теги ]

форумы  помощь  поиск  регистрация  вход/выход  слежка  RSS
"DBI постоянное соединение"
Вариант для распечатки  
Пред. тема | След. тема 
Форум Программирование под UNIX (Perl)
Изначальное сообщение [ Отслеживать ]

"DBI постоянное соединение"  +/
Сообщение от krpsh on 05-Июн-16, 17:09 
Здравствуйте!

есть код из скрипта, который один раз в 5 секунд вставляет строку в базу
база находится на другом конце openvpn туннеля

my $dbh = DBI->connect("dbi:Pg:dbname=db_exp;host=192.168.170.1;port=5432",
                "exp", "111",
                {AutoCommit => 0, RaiseError => 0, PrintError => 0, pg_enable_utf8 => 1, ShowErrorStatement => 1, InactiveDestroy => 1});
my $query = qq/INSERT INTO proba_connector (id, name, is_del) VALUES (?,?,?)/;
my $sth = $dbh->prepare($query);
while (1) {
    my $rv = $sth->execute(22, 'test', 1);
    print "sleep 5\n";
    sleep 5;
}

при падении туннеля, скрипт как бы "замирает" на шаге "sleep 5"
и не переходит к вставке следующей строки, где можно было бы отловить падение канала
т.к. драйвер бы выдал ошибку, что мол нет коннекта
вопрос
можно ли как-то в скрипте отловить падение туннеля?

после восстановления туннеля, скрипт "оживает" и продолжает свою работу.
У DBI есть метод ping, который как раз и предназначен решать эту проблему
и он прекрасно работает если базу просто остановить, но вот он не работает если
рвется связь, т.к. скрипт "замирает"
ps: того же эффекта можно добиться при помощи iptables, если закрыть порт 5432 на входящий коннект

Ответить | Правка | Cообщить модератору

Оглавление

Сообщения по теме [Сортировка по времени | RSS]


1. "DBI постоянное соединение"  +/
Сообщение от test (??) on 05-Июн-16, 19:30 
Может, стоит просто делать проверку доступности удаленного хоста с базой перед тем как начинать писать в неё? В DBI ещё timeout есть, погуглите.
Ответить | Правка | ^ к родителю #0 | Наверх | Cообщить модератору

2. "DBI постоянное соединение"  +/
Сообщение от krpsh on 05-Июн-16, 21:59 
> Может, стоит просто делать проверку доступности удаленного хоста с базой перед тем
> как начинать писать в неё? В DBI ещё timeout есть, погуглите.

Это все понятно, код который я привел - тестовый.
Т.е. я поднимаю канал, запускаю скрипт, жду когда скрипт вставить пару строк,
затем рву канал и скрипт, бац и замер, и висит до поднятия канала (я ждал 10 мин),
как только канал поднялся снова, скрипт продолжает вставлять строки.
Я полагал, что когда я отрублю канал, скрипт при последующей итерации попытается вставить
строку в базу, но он даже не пытается - вот это и напрягает. Почему так происходит?
В реальном скрипте есть куча проверок на коннект перед выполнением запросов
и даже есть реконнект при не доступности базы (метод ping у DBI).
Но я не мгу проверить доступность базы если канал упал. Скрипт просто "замирает"
и не переходит к следующей итерации цикла while, чтобы я мог проверить коннект.

не ужели мне надо оборачивать все действия с базой в сигнал ALRM
судя по этому топику - придется http://www.opennet.ru/openforum/vsluhforumID9/6774.html
блин, а если я не угадаю с таймаутом (вдруг запрос будет выполняться дольше обычного, да и запросы разные, один - 1сек, другой 25 сек)

Ответить | Правка | ^ к родителю #1 | Наверх | Cообщить модератору

3. "DBI постоянное соединение"  +/
Сообщение от Аноним (??) on 06-Июн-16, 09:10 
>[оверквотинг удален]
> Я полагал, что когда я отрублю канал, скрипт при последующей итерации попытается
> вставить
> строку в базу, но он даже не пытается - вот это и
> напрягает. Почему так происходит?
> В реальном скрипте есть куча проверок на коннект перед выполнением запросов
> и даже есть реконнект при не доступности базы (метод ping у DBI).
> Но я не мгу проверить доступность базы если канал упал. Скрипт просто
> "замирает"
> и не переходит к следующей итерации цикла while, чтобы я мог проверить
> коннект.

Замостите полную версию скрипта. С проверкой ping.

> не ужели мне надо оборачивать все действия с базой в сигнал ALRM
> судя по этому топику - придется http://www.opennet.ru/openforum/vsluhforumID9/6774.html
> блин, а если я не угадаю с таймаутом (вдруг запрос будет выполняться
> дольше обычного, да и запросы разные, один - 1сек, другой 25
> сек)

Ответить | Правка | ^ к родителю #2 | Наверх | Cообщить модератору

5. "DBI постоянное соединение"  +/
Сообщение от krpsh on 06-Июн-16, 09:40 
> Замостите полную версию скрипта. С проверкой ping.

#!/usr/bin/perl

package Testov_test;

use strict;
use warnings;
use utf8;
use open qw(:std :utf8);
use FindBin;
use lib $FindBin::Bin;
use DBI;

my $con_errstr = undef;
my $while_is_enabled = 1;


sub connect_db {
    # connect db
    my $dbh = DBI->connect("dbi:Pg:dbname=db_exp;host=192.168.170.1;port=5432",
                "exp", "111",
                {AutoCommit => 0, RaiseError => 0, PrintError => 0, pg_enable_utf8 => 1, ShowErrorStatement => 1, InactiveDestroy => 1});
    
    unless ($dbh) {
        $con_errstr = "cannot connect on database ($DBI::errstr)";
        return undef;
    } else {
        return $dbh;
    }
}

sub check_connect_db {
    my $dbh = shift;
    
    print "=============check conn db============\n";
    my $conn_db = 0;
    #print "ping=".$dbh->ping."\n";
    unless ($dbh->ping) {
        $dbh = undef;
        $conn_db = 1;
    }
    while ($conn_db) {
        print "lost connect to db, attempt restore the connection (interval 10sec)\n";
        $dbh = connect_db;
        if ($dbh) {
            print "connection to db restored\n";
            last;
        }
        print "sleep 10\n";
        sleep 10;
    }
    print "======================================\n";
    
    return $dbh;
}

my $dbh = connect_db;
if ($con_errstr) {
    print "$con_errstr\n";
    exit;
}

my $query = qq/INSERT INTO proba_connector (id, name, is_del) VALUES (?,?,?)/;
my $sth = $dbh->prepare($query);

while ($while_is_enabled) {
    
    $dbh = check_connect_db($dbh);
    
    print "===============INSERT=================\n";
    
    my @values = (1, 'one', 1,
            2, 'two' ,1,
            3, 'tree', 1);
    
    for (my $a=0; $a<3; $a++) {
                
        my ($id, $name, $is_del) = splice @values, 0, 3;
                
        # RaiseError => 0
        my $rv = $sth->execute($id, $name, $is_del);
        print "state=".$dbh->state."\n";
        unless ($rv) {
            print "ROLLBACK INSERT: ($DBI::errstr) ($DBI::err)\n";
            $dbh->rollback;
            last;
        }
                
        #print "sleep 10 transaction\n";
        #sleep 10;
    }
    print "commit\n";
    $dbh->commit;
    print "======================================\n";
        
    print "sleep 5\n";
}

скрипт отлично работает если базу положить
а вот если упадет туннель, то не работает :-(

Ответить | Правка | ^ к родителю #3 | Наверх | Cообщить модератору

4. "DBI постоянное соединение"  +/
Сообщение от PavelR (??) on 06-Июн-16, 09:32 
1) как вы собираетесь обрабатывать падение канала - какие действия будете предпринимать? У вас же "бесконечная транзакция" которая по падению сеанса будет отменена на сервере.
Если у вас между вставками 5 секунд, то может и нет смысла в "бесконечной транзакции"?

2) еще есть метод ping у openvpn. Мне кажется, это будет предпочтительный для вас вариант, не отменяющий, конечно, п.1.


Ответить | Правка | ^ к родителю #0 | Наверх | Cообщить модератору

6. "DBI постоянное соединение"  +/
Сообщение от krpsh on 06-Июн-16, 10:04 
> 1) как вы собираетесь обрабатывать падение канала - какие действия будете предпринимать?
> У вас же "бесконечная транзакция" которая по падению сеанса будет отменена
> на сервере.
> Если у вас между вставками 5 секунд, то может и нет смысла
> в "бесконечной транзакции"?

Да согласен, "бесконечная транзакция" забыл при написании в посте сменить AutoCommit => 1
Полный скрипт выложил в 5 ответе.
Обрабатывать падение - нужны попытки реконнекта к базе, так долго, пока канал не поднимется.
А если смотреть дальше то, скрипт выполняет много последовательных действий с базой.
Приведенный скрипт лишь выполняет одно действие (вставляет строку, пусть и бесконечно, но в реальном скрипте есть условие выхода из цикла вставки).
И нужно сделать так - не переходить к другому действию, если канал упал, а скрипт просто "замирает" при падении канала.
Я понимаю, что при падении канала, когда скрипт замер, он не перейдет к другому действию, но нужно этот момент отловить, чтобы послать письмо админу, что мол у тебя канал упал и т.д. и т.п.

> 2) еще есть метод ping у openvpn. Мне кажется, это будет предпочтительный
> для вас вариант, не отменяющий, конечно, п.1.

проблема не в канале, а в том что скрипт не может деагностировать падение канала.

я продолжаю эксперименты и скорее всего придется использовать RaiseError => 1
и оборачивать все действия с базой в eval
т.к. при таком раскладе скрипт вроде не замирает, не доконца еще разобрался

Ответить | Правка | ^ к родителю #4 | Наверх | Cообщить модератору

7. "DBI постоянное соединение"  +/
Сообщение от Square1 on 06-Июн-16, 12:15 
наверное не "while (1) " а "while ($connect_db)"
потом, если все таки "while (1)" (что вообще говоря порочно в данном случае)
то первой строкой цикла должна быть проверка на $connect_db и выход из цикла если не успешна..

Ну и наконец... вы отошли от атомарных операций в своем скрипте.
Почему вы не хотите устанавливать соединение каждый раз при необходимости записи?
создали соединение - записали-вышли.
Через 5 секунд повторили итерацию.

Ответить | Правка | ^ к родителю #0 | Наверх | Cообщить модератору

8. "DBI постоянное соединение"  +/
Сообщение от krpsh on 06-Июн-16, 12:52 
> наверное не "while (1) " а "while ($connect_db)"
> потом, если все таки "while (1)" (что вообще говоря порочно в данном
> случае)
> то первой строкой цикла должна быть проверка на $connect_db и выход из
> цикла если не успешна..
> Ну и наконец... вы отошли от атомарных операций в своем скрипте.
> Почему вы не хотите устанавливать соединение каждый раз при необходимости записи?
> создали соединение - записали-вышли.
> Через 5 секунд повторили итерацию.

если Вы говорите про полный скрипт из пятого сообщения то:
1)там в цикле while ($while_is_enabled), а $while_is_enabled - это как раз и есть условие выхода из цикла, просто я не писал условия установки его в 0 (там много всего).
2)первой строкой цикла и есть $dbh = check_connect_db($dbh);
идет проверка доступности соединения, и если оно не доступно, то $connect_db, до тех пор
пока не подлючится
3)на самом деле установка соединения перед каждой итерацией не решит проблему глобально
ведь проблема в том, что $dbh->ping, не возвращает false при обрыве соединения, а просто скрипт "виснет" (и висит, висит ...) при вызове этого метода.
Если мы будем устанавливать соединенние при каждой итерации, то это решит проблему при вставке первой строки из стрех (там в одной транзакции вставляются три тсроки), а если связь пропадет между вставкой 1-й и 2-й строки, то что тогда.
По идее в таком случае, медод execute должен вернуть ошибку, но он этого не делает, а скрипт просто виснет, как и при ping.

В общем, подводя итог - проблема в том, что при обрыве связи, обращение к любому методу $dbh ведет к зависанию скрипта, а не к ошибке выполнения метода.
И выход только один, оборачивать каждое обращение к базе в таймаут.
(вот тут написано об этом же http://www.opennet.ru/openforum/vsluhforumID9/6774.html)
Я уже определился с модулем для этого https://metacpan.org/pod/Sys::SigAction, функция timeout_call.
Но, честно говоря, это не очень удобно :-(

Ответить | Правка | ^ к родителю #7 | Наверх | Cообщить модератору

9. "DBI постоянное соединение"  +/
Сообщение от krpsh on 07-Июн-16, 09:37 
В общем, поизучав сложившуюся ситуацию пришел в выводу, что использовать таймауты на каждый запрос - это плохо.
В интернете по запросу "postgresql обрыв связи" много интересных споров на эту тему.
Общий вывод из этих споров таков - нет однозначного ответа.
Наткнулся на интересное решение - форкнуть процесс от скрипта, который устанавливает свой коннект к базе. Этот форкнутый процесс занимается проверкой связи и если связь разорвалась, то шлет сигнал основному скрипту.
Такой вариант мне подходит :-)
Ответить | Правка | ^ к родителю #0 | Наверх | Cообщить модератору

Архив | Удалить

Рекомендовать для помещения в FAQ | Индекс форумов | Темы | Пред. тема | След. тема


  Закладки на сайте
  Проследить за страницей
Created 1996-2017 by Maxim Chirkov  
ДобавитьРекламаВебмастеруГИД  
Hosting by Ihor