четверг, 4 октября 2012 г.

Timeout на соединение для Oracle

В ходе работы наткнулся на траблу:

Допустим имеется некий скрипт на perl'e\python'e или php ;). Скрипт подключается к дб Oracle и поддерживает соединение. Если с базой происходит неведомая хрень и подключится мы не можем - т.е порт открыт, процесс висит, но ответ не прилетает, в таких случаях Oracle будет ругаться ошибками вида:

"ORA-12170: TNS:Connect timeout occurred error message to the sqlnet.log file. 
The client receives either an ORA-12547: TNS:lost contact or an ORA-12637: Packet receive failed error message."

Скрипт на попытке подключения начинает висеть долго и безуспешно пытаясь подключиться. Очевидно что требуется ограничить его во времени на соединение, но в документации к библиотеке ничего нет про параметр connect_timeout или expire_timeout. Если начать искать решение в гугле то быстро натыкаешься на трэш типа через функцию alarm вызывать самого себя по прошествии столько то секунд или задать таймер:

Для python:
 
t = threading.Timer(timeout,conn.cancel)
t.start()
cursor = conn.cursor()
cursor.execute(query)
res =  cursor.fetchall()
t.cancel()
или
import signal
class TimeoutExc(Exception):
    """this exception is raised when there's a timeout"""
    def __init__(self): Exception.__init__(self)
def alarmhandler(signame,frame):
    "sigalarm handler.  raises a Timeout exception"""
    raise TimeoutExc()
 
nsecs=5
signal.signal(signal.SIGALRM, alarmhandler)  # set the signal handler function
signal.alarm(nsecs)                          # in 5s, the process receives a SIGALRM
try:
    cx_Oracle.connect(blah blah)             # do your thing, connect, query, etc
    signal.alarm(0)                          # if successful, turn of alarm
except TimeoutExc:
    print "timed out!"                       # timed out!!

Равно значно как для перла:
 
eval {
   local $SIG{ALRM} = sub { die "timeout" };
   alarm 10;
   $dbh = DBI->connect ( "dbi:Oracle:host=$db_host;sid=$db_base;port=$db_port",
               $db_user, $db_password,
               {PrintError=>1} );
};
if ( $@ ) {
   die "Exit due to timeout";
}
Так делать не нужно, во-первых по завершению функции скорее всего вы будете продолжать ждать ответ от драйвера oracl'a, во-вторых это костыль что уже по определению не правильно ;)
В документации к драйверу было найдено следующее решение:
На линукс машине в папке /etc/oracle/ если нет - создаёте файл sqlnet.ora. В нём в формате обычного ini файла пишете:

sqlnet.outbound_connect_timeout= 3

Больше меньше кому как надо и сохраняете. Всё, теперь при подключении драйвер будет только 3 секунды долбится на базу. Что интересно дефолтным значением для этого параметра является none.)
Есть ещё пара интересных опций если кому то пригодиться tcp.connect_timeout и sqlnet.expire_time. Ссылка на документацию: http://docs.oracle.com/.



0 коммент.:

Отправить комментарий