DeadLock
Пусть в базе данных имеется две таблицы: таблица А и таблица Б. Пусть, кроме того, два конкурирующих процесса пытаются получить доступ к этим таблицам, блокируя их при этом. DeadLock (или тупиковой ситуацией) называется ситуация, когда каждая из таблиц блокирована процессом, и при этом процессы пытаются получить доступ ко второй таблице.
В качестве примеров используйте проект
HB_Tutorial_DBLock. Проект состоит из двух примеров.
Форма HB_Tutorial_DBLock_Unconditional.
Запустив эту форму в двух приложениях, достаточно скоро вы получите DeadLock. Форма модифицирует таблицы HB_Tutorial_DBLock_TableA и HB_Tutorial_DBLock_TableB в следующем порядке:
- Начало транзакции (ttsbegin).
- Блокирование таблицы A (select for update from A).
- Блокирование таблицы Б (select for update from Б).
- Модифицирование таблицы Б (Б.update).
- Модифицирование таблицы А (А.update).
- Конец транзакции (ttscommit).
- Начало транзакции.
- Блокирование таблицы Б.
- Блокирование таблицы А.
- Модифицирование таблицы А.
- Модифицирование таблицы Б.
- Конец транзакции.
После действий, отмеченных курсивом, происходит задержка, чтобы увеличить вероятность возникновения DeadLock.
Пошаговое выполнение действий.
Используйте форму HB_Tutorial_DBLock_Client для запуска конкурирующих процессов, а форму HB_Tutorial_DBLock_Control для управления ими. Форма Client может работать в следующих режимах:<ul>[*]
Работа с одной таблицей.
Процесс будет модифицировать записи только в одной таблице.[*]
Работа с двумя таблицами.
В этом режиме можно выбрать, с какой таблицы начинать блокировку (для модификации).[/list]Для управления работой процессов используется форма HB_Tutorial_DBLock_Control. В этой форме в списке автоматически появляются имена процессов, начавших транзакции. С помощью кнопок «Шаг» и «Стоп» выбранному процессу можно отправить сигнал выполнить следующий шаг или остановить работу.
Для создания DeadLock запустите два работающих с двумя таблицами процесса: один из них сначала блокирует таблицу А, другой – таблицу Б.
Обработка DeadLock в Axapta.
Axapta отслеживает возникновение DeadLock. В случае создания подобной ситуации Axapta вызывает исключительную ситуацию в одном из процессов (нельзя с полной уверенностью сказать в каком), а второй продолжает работу. Вполне логично было бы предположить, что исключительная ситуация, которая возникает при этом, есть Exception::Deadlock, но проверить это не представляется возможным, ввиду не ясного мне механизма Axapta обработки исключительных ситуаций.
Способы борьбы с DeadLock.
Единственным способом борьбы с DeadLock является грамотное написание программы. В этой области помощь может быть предложена лишь в виде советов.
Планируйте обновление таблиц. Если вы будете четко представлять себе, в каком порядке будут изменяться таблицы, и какие таблицы будут задействованы в этом обновлении (таблица может быть заблокирована и при чтении данных из нее), вам будет проще найти места возможных ошибок. Все программы, которые в своих транзакциях обновляют несколько таблиц, по возможности должны всегда обновлять их в одном и том же порядке.
Не используйте транзакции без необходимости. Если таблица обновляется внутри некоторой транзакции, она будет заблокирована на протяжении всей транзакции. Очевидно, это увеличивает процесс блокировки таблицы. В случае отсутствия явного создания транзакции (с помощью операторов ttsbegin и ttscommit), каждый успешно выполненный оператор SQL будет выделен в отдельную транзакцию.
Старайтесь делать транзакции как можно более короткими. Такой подход уменьшит время блокировки таблиц, а значит и вероятность появления deadlock.
Используйте обработку исключительных ситуаций. В случае возникновения deadlock, Axapta выбирает один из процессов, попавших в эту ситуацию, в качестве «проигравшего». Ему отправляется ошибка Exception::DeadLock. Заключая операторы, которые могут привести к подобной ошибке в блок try – catch, вы сможете выполнить определенные действия, при возникновении этой ошибки (наиболее логичным является попытка повторить обновление с помощью оператора retry).
Максим Горбунов