The OpenNET Project / Index page

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




Версия для распечатки Пред. тема | След. тема
Новые ответы [ Отслеживать ]
DBI постоянное соединение, !*! krpsh, 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 на входящий коннект

  • DBI постоянное соединение, !*! test, 19:30 , 05-Июн-16 (1)
    Может, стоит просто делать проверку доступности удаленного хоста с базой перед тем как начинать писать в неё? В DBI ещё timeout есть, погуглите.
    • DBI постоянное соединение, !*! krpsh, 21:59 , 05-Июн-16 (2)
      > Может, стоит просто делать проверку доступности удаленного хоста с базой перед тем
      > как начинать писать в неё? В DBI ещё timeout есть, погуглите.

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

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

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

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

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

        • DBI постоянное соединение, !*! krpsh, 09:40 , 06-Июн-16 (5)
          > Замостите полную версию скрипта. С проверкой 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";
          }

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

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

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


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

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

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

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

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

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

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

    • DBI постоянное соединение, !*! krpsh, 12:52 , 06-Июн-16 (8)
      > наверное не "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.me/openforum/vsluhforumID9/6774.html)
      Я уже определился с модулем для этого https://metacpan.org/pod/Sys::SigAction, функция timeout_call.
      Но, честно говоря, это не очень удобно :-(

  • DBI постоянное соединение, !*! krpsh, 09:37 , 07-Июн-16 (9)
    В общем, поизучав сложившуюся ситуацию пришел в выводу, что использовать таймауты на каждый запрос - это плохо.
    В интернете по запросу "postgresql обрыв связи" много интересных споров на эту тему.
    Общий вывод из этих споров таков - нет однозначного ответа.
    Наткнулся на интересное решение - форкнуть процесс от скрипта, который устанавливает свой коннект к базе. Этот форкнутый процесс занимается проверкой связи и если связь разорвалась, то шлет сигнал основному скрипту.
    Такой вариант мне подходит :-)



Партнёры:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2024 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру