Если при выполнении распределенной транзакции с Microsoft SQL Server на сервере произошла ошибка I/O или произошло отключение сервера / базы данных, тогда транзакция в координаторе (DTC) и на других базах будет откачена (ROLLBACK), а в пострадавшей базе данных на SQL Server может не откатиться. Это потому, что в результате отключения / сбоя, СУБД технически не могла её откатить.

Зависшая распределенная транзакция будет проявляться, в том числе, тем, что в базе не будет обрезаться журнал (TRANSACTION LOG) даже в режиме восстановления SIMPLE.

Если транзакция провела много изменений в базе, все они так и не принятые будут лежать в журнале и увеличивать размер самого журнала и размер резервных копий. Если транзакция заблокировала ресурсы, по логике, они так же должны оказаться заблокированы до её отката (хотя я это не проверял).

Как диагностировать зависшую распределенную транзакцию?

Нужно выполнить:

DBCC OPENTRAN

Эта команда может выдать приблизительно следующее:

Transaction information for database '.....'.

Oldest active transaction:
    SPID (server process ID): 53
    UID (user ID) : -1
    Name          : DTCXact
    LSN           : (1517932:234765:1)
    Start time    : Jul  3 2013  3:41:04:643AM
    SID           : 0x010600000000000550000000cc62defa0799cb7d28fedbc8b4ce1b1ace9fa59b
DBCC execution completed. If DBCC printed error messages, contact your system administrator.

Обратите внимание на имя DTCXact - это означает, что транзакцию инициировал Distributed Transaction Coordinator.

Ок. Узнав SPID, мы можем узнать что за процесс работал в транзакции:

EXEC sp_who 53

Мы можем его убить (KILL ...). Но не надо этого делать.

Зависшую распределенную транзакцию прекращение клиентского процесса не остановит — она существует теперь только в пострадавшей базе данных, сам процесс и координатор про неё уже забыли.

Как завершить зависшую распределенную транзакцию?

Интернет на вопрос "ROLLBACK DTCXact" и "DTCXact" выдает дурацкие статьи на общие темы или обсуждения без ответа на вопрос.

Недавно, я использовал такой способ — перевести базу в однопользовательский режим с откатом всех транзакций:

ALTER DATABASE [...] SET SINGLE_USER WITH ROLLBACK IMMEDIATE

Убедился, что транзакция откатилась, и попытался перевести в многопользовательский:

ALTER DATABASE [...] SET MULTI_USER WITH NOWAIT

Правда после этого, как я подозреваю, из-за логического повреждения (которое было не следствием, а причиной зависшей распределенной транзакции) база вошла в режим SUSPECT. Пришлось её выключить (Take Off) и включить (Take On). Потребовался, видимо, не только возврат страниц данных "назад" путем отката, но и восстановление части страниц "вперед" по журналу с какого-то момента.

В результате база заработала, распределенная транзакция откатилась, журнал усёкся.

Читайте также "Как удалить зависшую распределенную транзакцию в PostgreSQL".