2007-11-18

Russian edition for "2K ill | NOT 2K ill (on the essence of KeepAlive)"

Убивать - не убивать (мёртвые сеансы и KeepAlive)

У меня завалялась неопубликованная статья (которую я готовил как MetaLink Note году так в 2001-2002. Были там этакие правила для центров тех.поддержки). Несмотря на преклонный возраст, статья бывает периодически востребована для решения проблем наших (ЦТП {Центр Технической Поддержки} ЗАО "РДТЕХ") заказчиков.

Обычно эти проблемы связаны с неудовлетворительным качеством сети (удаленный доступ с клиентских рабочих мест по "шумящим каналам")

Либо с непродуманной (как минимум) организацией промежуточного (middle tier/multi-tier, серверы приложений) программного обеспечения. Особенно когда промежуточное ПО не использует опции типа connection pooling, а вместо этого реализует stateless connection (т.е. статус соединения не отслеживается). Типично для "кривых" реализаций WEB-интерфейсов к БД. На каждый "клик" в броузере октрывается соединение к БД, выполняется пара-тройка команд... И о соединении забывают (т.е. не закрывают его корректно). Решения тут понятны и прозрачны... Однако... что делать DBA, столкнувшемуся с данной проблемой, пока разработчики системы "прочухаются"?

Один из способов: использование свойства протокола TCP/IP - KeepAlive

Ниже - перевод той самой давней статьи на тему.

---------
Целевая аудитория

Статья может быть полезна опытным DBA, когда клиенты подсоединены к основной БД по сети неудовлетворительного качества, когда соединения с клиента с сервером могут неожиданно прерываться (по различным, вне сферы ответственности DBA причинам)

Введение

В подавляющем большинстве случаев серверный процесс/диспетчер вовремя обнаруживает факт утраты соединения с клиентом. Обнаружив сей прискорбный факт, серверный процесс аварийно завершается, позволяя фоновому процессу PMON выполнить свою работу. Обсуждаемая проблема касается тех случаев, когда до PMON'а дело не доходит. Т.е. серверный процесс жив, но клиент о нём забыл.

Типичная ситуация:

a) по какой либо причине соединение на стороне клиента аварийной прервано (пользователь нажал кнопку "RESET" или "POWER OFF" на своей персоналке, или случился кратковременный перебой в обеспечении WAN-соединения, или вмешался firewall/router/switch со своими тайм-аутами и т.п.). В этом случае серверный процесс может "выжить", в то время как клиентская программа уже забыла о его существовании или вообще аварийно завершилась.

b) пользователь переустановил соединение (создал новый сеанс со своим серверным процессом)

c) "старый" сеанс был в неактивном состоянии ('INACTIVE') в момент аварийного завершения соединения на стороне пользователя (в случае 'ACTIVE' рано или поздно серверный процесс попытался обменяться сообщениями с клиентом, обнаружил бы сбой соединения и благополучно завершился)

d) этот "старый" сеанс никогда не завершится (или завершится через весьма продолжительный период времени)

e) DBA хочет завершить данный сеанс, поскольку PMON никаких действий не предпринимает, а сеанс мешает другим пользователям (например, в процессе выполнения транзакции удерживает блокировки)

f) удаление сеанса средствами Oracle не помогает, status='KILLED', process='PSEUDO'
ALTER SYSTEM KILL SESSION '<sid>,<serial#>',;
ALTER SYSTEM DISCONNECT SESSION '<sid>,<serial#>' IMMEDIATE;


Надо отметить, что вторая команда (DISCONNECT) срабатывает чаще

g) поиск процесса для удаления его средствами ОС
SELECT p.spid FROM v$session s, v$process p
WHERE s.paddr=p.addr
AND s.sid=<sid>;


не возвращает строк. Конечно, процесс можно найти методом исключения (в V$PROCESS он всё еще присутствует).

Еще комментарий - если для unix-подобных систем команда ОС kill -KILL решает проблему почти всегда (главное не ошибиться с выбором удаляемого процесса :-)), то в MS Windows в некоторых версиях, например, не освобождается PGA. Если версия, к тому же, 32-битная, то следует ожидать TNS-12560... Гхмм... А кто знает, как удалить сеанс на zOS/MVS?

Oracle Net (NET8, SQL*NET v2) предоставляет механизм DCD (Dead Connection Detection) для разрешения подобных ситуаций. Чтобы задействовать его, следует в sqlnet.ora на сервере установить параметр SQLNET.EXPIRE_TIME (значение в минутах). Достоинство этого метода - в независимости от сетевого протокола (хотя нынче выжил единственный протокол - TCP/IP). К сожалению, реализация его до версии 10g оставляла желать лучшего. В частности, при задании параметра на стороне клиента некоторые операции по экспорту или, скажем, вызову долго выполняющихся ( > SQLNET.EXPIRE_TIME ) процедур PL/SQL|Java могли неожиданно быть прерваны задействованным механизмом. Вы можете спросить - но ведь sqlnet.ora надо править на сервере? Да. Но вспомним о dblink... Один из серверов становится клиентом другого(-их).


Свойство "Keep Alive" протокола TCP/IP

Протокол TCP/IP является фактически единственным стандартным протоколом, используемым в современных сетях в Oracle Net.

Сетевой протокол Oracle (Oracle Net/NET8/SQL*Net) использует возможности стандартных протоколов (в частности TCP/IP).

Адаптер сетевого протокола Oracle может использовать общие возможности нижележащего протокола, в частности, обсуждаемый в этой статье механизм "Keep Alive".

ВАЖНОЕ ЗАМЕЧАНИЕ: в случае, когда сообщение "keep alive" не получает ответа, серверный процесс благополучно завершается (так как не содержит соответствующего обработчика исключительной ситуации), позволяя процессу PMON выполнить "зачистку".

Механизм Keep Alive

достаточно прост - две стороны соединения (клиент и сервер) периодически (по умолчанию интервал равен двум часам) посылают друг другу "пустые" или "пробные" пакеты TCP/IP, которые предназначены исключительно для проверки факта, что партнер в состоянии ответить. Если партнер не ответил на "пробник", посылавшая "пробник" сторона посылает еще несколько пакетов (но уже с другой частотой, отличной от 2-х часов по умолчанию). В конце концов, если ни на один "пробник" ответа не получено, соединение помечается как сбойное (драйвером TCP/IP) и процессу посылается TRAP от драйвера на уровне ОС.

Поскольку обработчика прерывания в серверном процессе Oracle не предусмотрено (или же он не является последним в цепочке обработчиков), то процесс (серверный) аварийно завершается. Аварийное завершение серверного процесса обнаруживается PMON, что и требовалось.

Механизм DCD (Dead Connection Detection)

несколько отличается, хотя общие черты, естественно, имеются. PMON (если не ошибаюсь) периодически пересылает IPC (Inter-Process Communication) сообщение серверным процессам средствами Oracle (постановка сообщения в очередь сообщений процессу и, в зависимости от платформы, взведение семафора, задействование post-wait драйвера и т.п.). И ждет ответа. Предполагается, что каждый серверный процесс периодически проверяет свою очередь входных сообщений*), посылает "пробник" партнеру, и, в случае успеха, ответчает на них PMON'у. Вопрос в том, как часто происходит такая проверка. Что, если исполняется "долгоиграющая" процедура? Или долго выполняющаяся команда? И предусмотрен ли механизм асинхронной проверки очередей сообщений (т.е. вне стандартного потока действий по обработке команды)? По тестам видно, в Oracle10g проверки проводятся асинхронно. Поэтому начиная с версии 10g можно полагаться на DCD. Но не в предыдущих версиях.

Как задействовать Keep Alive на разных платформах

Предполагается, нашей целью является
a) включение этой возможности
b) регулирование частоты проверок (два часа могут быть слегка завышенной цифрой)

- для включения механизма Keep Alive следует воспользоваться настройками на стороне клиента. В определение сетевого имени службы (оно же alias, описатель соединения... кратко говоря - то, что содержится в tnsnames.ora) следует включить "кляузу" (ENABLE=BROKEN). Пример:

xxx.domain.com = (DESCRIPTION=(ENABLE=BROKEN)(ADDRESS= ....)... )

подробности в MetaLink Note:39357.1

- ниже приведен (неполный) перечень действий по изменению интервала передачи "пробных пакетов" Keep Alive на разных платформах.

a) Windows: Изменить установки в реестре. Подробности на сайте поддержки Microsoft 120642

HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Tcpip/Parameters
Edit - Add value : KeepAliveTime (REG_DWORD)
Decimal -> 180000 (3 minutes, for example)

b) Solaris/HP-UX

/usr/sbin/ndd –set /dev/tcp tcp_keepalive_interval 180000
(интервал 3 минуты)

c) Linux

/sbin/sysctl -w net.ipv4.tcp_keepalive_time=1800 ?
(интервал 3 минуты)

d) Tru64

/usr/sbin/netconfig tcp_keepidle 360
(интервал 3 минуты)
ЗАМЕЧАНИЕ: время указывается в единицах 1/2 секунды

e) AIX

/usr/sbin/no -o tcp_keepidle=360
(интервал 3 минуты)

ЗАМЕЧАНИЕ: время указывается в единицах 1/2 секунды

10 comments:

Anonymous said...

Может попутно подскажете, у меня из базы идет ображение к ftp-серверу для обмена файлами, канал к ftp крайне неустойчив, соответственно, регулярно возникают описанные выше проблемы. Здесь можно что-либо сделать? БД 10.2 Linux x86_64, ftp - win32.

Anonymous said...

Маленькое замечаньице:
Введение - последнее предложение
Один из сервером становится

Andrey Kriushin said...

сервером - исправил. Спасибо за замечание.

Anonymous said...

SQLNET.EXPIRE_TIME
Use to specify a specify the time interval, in !!!SECONDS!!!,
to send a probe to verify that client/server
connections are active.

А в статье написано, что в минутах...

Andrey Kriushin said...

Что касается ftp...

1. Этот протокол использует два "сеанса" - командный (по TCP/IP) и передачи данных (UDP/IP). KeepAlive действует только на TCP/IP.

Для ftp есть другие средства... Например, для протокола FTP предусмотрен режим докачки данных (некоторые сервера поддерживают этот режим). Этим свойством пользуются(-вались) многие утилиты типа reget, net vampire и иже с ними...

2. Статья посвящена конкретной теме обнаружения "мёртвых", "безхозных" сеансов сервера Oracle и их "безболезненном" для сервера удалении. Ни о какой починке речи тут не идёт.

HTH

Andrey Kriushin said...

SQLNET.EXPIRE_TIME

Лично я проверил официальную документацию. Время указывается в минутах. Если у вас есть опровержение, welcome.

HTH

Alex Fatkulin said...

Андрей!

Ну наконец-то и ты в блогосфере :)

Anonymous said...

Статья оказалась полезной на практике.
Спасибо!
Пишите еще!

Anonymous said...

Такой вопрос - без изменения на клиентских местах tnsnames (добавление ENABLE=BROKEN) будет работать или обязательно на клиенте обязательно менять нужно ?

Andrey Kriushin said...

Добавление (ENABLE=BROKEN) следует делать на клиенте. Эта опция включает KeepAlive для конкретного соединения, и, насколько мне известно, только в "толстом" клиенте, т.е. в случае использования OCI.

Я не знаю, как ведет себя thin JDBC (то, что он не читает tnsnames.ora - понятно). И есть ли другие методы включения KeepAlive.

С другой стороны (см. про DCD в статье), начиная с 10g потребность в использовании KeepAlive существенно сократилась.