Если наша транзакция выбрана для разрешения взаимной блокировки, обработчик исключения в приложении должен выполнить откат транзакции, чтобы позволить другой транзакции возобновить и завершить свою работу. Альтернатива - подтверждение транзакции в обработчике исключения - не рекомендуется, поскольку наша транзакция станет неатомарной, а другая транзакция даст сбой по конфликту блокировки.
В редких случаях более двух транзакций может быть включено во взаимную блокировку при борьбе за перекрывающиеся наборы. Иногда это называется тупиковой ситуацией (программистский жаргон - deadly embrace, смертельные объятия). Сканирование блокировок выберет одну транзакцию (нашу транзакцию), ситуация будет обработана в клиентском обработчике исключений, как в предыдущем примере. При этом даже если клиент выполнит откат нашей транзакции, те другие транзакции все еще останутся заблокированными.
Клиент может запустить новую транзакцию и повторить попытку, однако другие соперники все еще находятся в состоянии взаимной блокировки, ожидая следующего сканирования блокировок для освобождения следующего соперника с выдачей исключения блокировки. Если приложение повторяет попытку с транзакцией WAIT, он просто ждет бесконечное время, когда будет разрешена тупиковая ситуация с другими транзакциями. Про транзакцию с такими тщетными попытками повтора говорят, что она находится в активном тупике.
Короче говоря, важно в первую очередь исключить контексты транзакций, которые могут привести к тупиковой ситуации. В качестве дополнительной защиты обработчики исключений должны быть способны быстро обрабатывать блокировки и обеспечивать, чтобы проблемные транзакции завершались чисто и без задержек.
Далее мы рассмотрим транзакции с точки зрения разработчика клиентских приложений. Темы в этой главе являются нейтральными к включающим языкам. Тем не менее все современные средства разработки приложений и драйверы для Firebird используют один и тот же API в той или иной форме, что хорошо для одного, хорошо и для другого.
ГЛАВА 27. Программирование с транзакциями.
Транзакция является начальной точкой для всех взаимодействий клиентского приложения с сервером. В этой главе мы с точки зрения различных интерфейсов клиента рассмотрим запуск, управление и завершение транзакций.
Многие языки и средства разработки имеют интерфейс с Firebird. Подробное описание управления транзакциями Firebird в каждом из них находится за пределами данной книги.
Язык для транзакций
Важно обратиться к средствам реализации транзакций в Firebird. До сих пор некоторые связанные с транзакциями особенности вовсе не реализованы в динамическом SQL, а только через API. Среди небольшого количества связанных с транзакциями операторов SQL и доступных в подмножестве DSQL только COMMIT и ROLLBACK доступны в каждом интерфейсе. В книге осознанно выбран нейтральный подход к языкам средств разработки. Основной акцент делается на динамический SQL, используемый в большинстве существующих средств разработки клиентских приложений. В главе представлены некоторые проблемы, одинаково актуальные и для автора, и для читателя.
Хотя эта глава не описывает ESQL[98] или API[99], следующие разделы будут рассказывать о них в более или менее общем виде для получения некоторого понимания того, что передается через интерфейс, когда клиенты "беседуют" с серверами о транзакциях.
ESQL
Надмножество операторов SQL и подобных SQL операторов, используемых в прежние времена, даже до публикации API и подмножества DSQL, представляет синтаксис стандарта SET TRANSACTION для конфигурирования и старта транзакций. В некоторых формах он доступен в DSQL и может быть использован в утилите isql. Это удобное средство общей проверки того, как API передает эквивалентную информацию[100].
API
API предоставляет гибкий интерфейс множества сложных функций программистам С и C++ для создания клиентских приложений с наиболее тонким уровнем связи и соединения. Группа функций API реализует эквивалентные операторы SQL, относящиеся к транзакциям, например isc_start_transaction() для оператора START TRANSACTION и isc_commit_transaction() для оператора COMMIT.
Заголовочный файл API, ibase.h, объявляет прототипы функций, определения типов для каждой структуры, определения параметров и макросы, которые используются в функциях. Он поставляется в каталоге Firebird /include.
Для некоторых объектно-ориентированных сред разработки, таких как Object Pascal, Borland C++ Builder, Java, PHP, Python и DBI::Perl классы и компоненты полностью инкапсулируют вызовы API Firebird, относящиеся к транзакциям. Пользовательские драйверы для интерфейсов соединения со стандартными базами данных SQL - в особенности ODBC, JDBC и .NET- похожим образом представляют API[101].
Запуск транзакции
SQL
Оператор SQL для запуска транзакции имеет следующий синтаксис:
SET TRANSACTION [NAME <имя-транзакции>]
[READ WRITE | READ ONLY] /* режим доступа */