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.dev/openforum/vsluhforumID9/6774.html блин, а если я не угадаю с таймаутом (вдруг запрос будет выполняться дольше обычного, да и запросы разные, один - 1сек, другой 25 сек)
- DBI постоянное соединение,
Аноним, 09:10 , 06-Июн-16 (3)>[оверквотинг удален] > Я полагал, что когда я отрублю канал, скрипт при последующей итерации попытается > вставить > строку в базу, но он даже не пытается - вот это и > напрягает. Почему так происходит? > В реальном скрипте есть куча проверок на коннект перед выполнением запросов > и даже есть реконнект при не доступности базы (метод ping у DBI). > Но я не мгу проверить доступность базы если канал упал. Скрипт просто > "замирает" > и не переходит к следующей итерации цикла while, чтобы я мог проверить > коннект.Замостите полную версию скрипта. С проверкой ping. > не ужели мне надо оборачивать все действия с базой в сигнал ALRM > судя по этому топику - придется http://www.opennet.dev/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.dev/openforum/vsluhforumID9/6774.html) Я уже определился с модулем для этого https://metacpan.org/pod/Sys::SigAction, функция timeout_call. Но, честно говоря, это не очень удобно :-(
- DBI постоянное соединение,
krpsh, 09:37 , 07-Июн-16 (9)В общем, поизучав сложившуюся ситуацию пришел в выводу, что использовать таймауты на каждый запрос - это плохо. В интернете по запросу "postgresql обрыв связи" много интересных споров на эту тему. Общий вывод из этих споров таков - нет однозначного ответа. Наткнулся на интересное решение - форкнуть процесс от скрипта, который устанавливает свой коннект к базе. Этот форкнутый процесс занимается проверкой связи и если связь разорвалась, то шлет сигнал основному скрипту. Такой вариант мне подходит :-)
|