TransactionInDoubtException с использованием System.Transactions на SQL Server 2005

Основной вопрос к этому посту: "Почему не продвигаемая транзакция LTM когда-либо будет подвергаться сомнению?"

Я получаю System.Transactions.TransactionInDoubtException, и я не могу объяснить, почему. К сожалению, я не могу воспроизвести эту проблему, но, согласно файлам трассировки, это действительно происходит. Я использую SQL 2005, подключаюсь к одной базе данных и использую один SQLConnection, поэтому я не ожидаю, что продвижение будет иметь место. Сообщение об ошибке указывает на тайм-аут. Однако иногда я получаю сообщение об истечении времени ожидания, но исключение состоит в том, что транзакция была прервана, а не вызвана сомнением, что намного проще в обработке.

Вот полная трассировка стека:

System.Transactions.TransactionInDoubtException: транзакция находится в сомнении. ---> System.Data.SqlClient.SqlException: истекло время ожидания. Время ожидания истекло до завершения операции или сервер не отвечает. в System.Data.SqlClient.SqlInternalConnection.OnError(исключение SqlException, логическое breakConnection) в System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj) в System.Data.SqlOjectStoreReaderStateState_Sject.Data.SqlClient.TdsParserStateObject.ReadSni(DbAsyncResult AsyncResult, TdsParserStateObject stateObj) в System.Data.SqlClient.TdsParserStateObject.ReadNetworkPacket() в System.Data.SqlClient.TdsParserStateObject.ReadBuffer() в System.Data.SqlClient.TdsParserStateObject.ReadByte() при System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader DATASTREAM, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) в System.Data.SqlClient.TdsParser.TdsExecuteTransactionManagerRequest(байт [] буфера, запрос TransactionManagerRequestType, струнного transactionName, TransactionManagerIsolationLevel isoLevel, время ожидания Int32, SqlInternalTransaction транзакции, TdsParserStateObject stateObj, булева isDelegateControlRequest) при System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransactionYukon(TransactionRequest transactionRequest, струнного transactionName, IsolationLevel ISO, SqlInternalTransaction internalTransaction, булева isDelegateControlRequest) в System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransaction(TransactionRequest transactionRequest, String имя, IsolationLevel iso, SqlInternalTransaction internalTransaction, логическое isDelegateControlRequest) в System.Data.SqlClient.SqlDelegatedTransaction.SinglePhaseCommit(SinglePhaseEnlistment enlistment)
   --- Конец внутреннего исключения из стека System.Transactions.CommittableTransaction.Commit() в System.Transactions.TransactionScope.InternalDispose() в System.Transactions.TransactionScope.Dispose()

Есть идеи? Почему я в сомнении и что я должен делать, когда я получаю это?

РЕДАКТИРОВАТЬ для получения дополнительной информации

У меня на самом деле до сих пор нет ответа на это. Я понял, что транзакция частично совершается. Одна таблица получает вставку, а другая не получает обновление. Код тяжело прослеживается, и у меня не так много места, чтобы что-то упустить.

Есть ли способ, которым я могу легко узнать, была ли сделка продвинута. Можем ли мы узнать из трассировки стека, если это так? Похоже, что одиночная фазовая фиксация (которая находится в следовом потоке) указывает на отсутствие продвижения по службе, но, возможно, я что-то упустил Если его не продвигают, то как это может быть под вопросом.

Еще одна интересная часть головоломки состоит в том, что я создаю клон текущей транзакции. Я делаю это как обходной путь к этой проблеме. http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=914869&SiteID=1

К сожалению, я не знаю, была ли решена эта проблема. Возможно, создание клона вызывает проблемы. Вот соответствующий код

using (TransactionScope ts = new TransactionScope())
{
   transactionCreated = true;
   //part of the workarround for microsoft defect mentioned in the beginning of this class
   Transaction txClone = Transaction.Current.Clone();
   transactions[txClone] = txClone;
   Transaction.Current.TransactionCompleted += new TransactionCompletedEventHandler(TransactionCompleted);
   MyTrace.WriteLine("Transaction clone stored and attached to event");

   m_dataProvider.PersistPackage(ControllerID, package);
   MyTrace.WriteLine("Package persisted");
   m_dataProvider.PersistTransmissionControllerStatus(this);
   MyTrace.WriteLine("Transmission controlled updated");
   ts.Complete();
}

Спасибо

5 ответов

Решение

Ответ в том, что не может. Очевидно, что происходило то, что продвижение происходило. (Мы случайно обнаружили это) Я до сих пор не знаю, как определить, происходит ли попытка продвижения по службе. Это было бы чрезвычайно полезно для обнаружения этого.

Текущий принятый ответ заключается в том, что не продвигаемая Транзакция LTM (не MSDTC) никогда не может подвергаться сомнению. После долгих исследований подобной проблемы я обнаружил, что это неправильно.

Благодаря тому, как реализован протокол однофазной фиксации, существует небольшой период времени, когда транзакция "находится под сомнением", после того как Менеджер транзакций отправляет запрос SinglePhaseCommit своему подчиненному, и до того, как подчиненный отвечает либо зафиксированным / прервано / или подготовлено (необходимо повысить / перейти к MSDTC) сообщение. Если в течение этого времени соединение теряется, то транзакция "находится под сомнением", так как Transaction Manager никогда не получал ответ, когда просил подчиненного выполнить SinglePhaseCommit.

Из однофазной фиксации MSDN см. Также изображение "Поток фиксации однофазной сети" в нижней части этого ответа:

У этой оптимизации есть возможный недостаток: если менеджер транзакций теряет связь с подчиненным участником после отправки запроса однофазной фиксации, но до получения уведомления о результате, у него нет надежного механизма для восстановления фактического результата транзакции. Следовательно, менеджер транзакций отправляет результат In Doubt любым заявкам или избирателям, ожидающим информационного уведомления о результатах.

Также вот некоторые практические примеры вещей, которые я обнаружил, которые вызывают повышение / повышение транзакции System.Transaction для транзакции MSDTC (это не имеет прямого отношения к OP, но я нашел очень полезным. Протестировано в VS 2013, SQL Server 2008 R2, .NET 4.5 кроме случаев, где это указано):

  1. (это относится к SQL Server 2005 или если уровень совместимости < 100)- Вызов Connection.Open() более одного раза в любой точке TransactionScope. Это также включает вызовы.Open(), .Close(), .Open() для того же самого соединения.
  2. Открытие вложенных соединений в TransactionScope
  3. Использование нескольких соединений, которые не используют пул соединений, даже если они не являются вложенными, и подключение к одной и той же базе данных.
  4. Запросы, которые связаны со связанными серверами
  5. Процедуры SQL CLR, использующие TransactionScope. См.: http://technet.microsoft.com/en-us/library/ms131084.aspx "TransactionScope следует использовать только при обращении к локальным и удаленным источникам данных или менеджерам внешних ресурсов. Это потому, что TransactionScope [в CLR] всегда заставляет транзакции продвигаться, даже если они используются только в контексте соединения
  6. Похоже, что если используется пул соединений и то же самое точное физическое соединение, которое использовалось в Соединении 1, по каким-то причинам недоступно в Соединениях "2 - N", тогда вся транзакция будет продвинута (б / с они рассматриваются как 2 отдельных длительных ресурсы, пункт № 2 - официальный список MS ниже). Я не проверял / не подтверждал этот конкретный случай, но я понимаю, как он работает. Имеет смысл скрытый доступ за кадром, это похоже на использование вложенных соединений или не использование пула соединений, так как используется несколько физических соединений. http://msdn.microsoft.com/en-us/library/8xx3tyca(v=vs.110).aspx "Когда соединение закрывается и возвращается в пул с зачисленной транзакцией System.Transactions, оно откладывается в таким образом, что следующий запрос для этого пула соединений с той же транзакцией System.Transactions будет возвращать то же соединение, если оно доступно. Если такой запрос выполнен, и нет доступных соединений в пуле, соединение извлекается из -отделена часть бассейна и зачислена

И вот официальный список MS, что вызывает эскалацию: http://msdn.microsoft.com/en-us/library/ms229978(v=vs.85).aspx

  1. По крайней мере один долговременный ресурс, который не поддерживает однофазные уведомления, зачислен в транзакцию.
  2. По крайней мере два долговременных ресурса, которые поддерживают однофазные уведомления, зачисляются в транзакцию. Например, использование одного соединения с SQL Server 2005 не приводит к продвижению транзакции. Однако всякий раз, когда вы открываете второе соединение с базой данных SQL Server 2005, в результате чего база данных подключается, инфраструктура System.Transactions обнаруживает, что это второй долговременный ресурс в транзакции, и преобразует его в транзакцию MSDTC.
  3. Вызывается запрос на "маршализацию" транзакции в другой домен приложения или в другой процесс. Например, сериализация объекта транзакции через границу домена приложения. Объект транзакции является маршалированным по значению, что означает, что любая попытка передать его через границу домена приложения (даже в том же процессе) приводит к сериализации объекта транзакции. Вы можете передать объекты транзакции, вызвав удаленный метод, который принимает транзакцию в качестве параметра, или вы можете попытаться получить доступ к удаленному компоненту, обслуживаемому транзакцией. Это сериализует объект транзакции и приводит к эскалации, как при сериализации транзакции в домене приложения. Он распространяется, и локальный менеджер транзакций больше не подходит.

Поток фиксации одной фазы

Черт побери из меня.

У меня есть привычка делать ExecuteNonQuery для "BEGIN TRANSACTION" и "COMMIT" или "ROLLBACK" вручную.

Совершенно случайно это сработало очень хорошо, когда некоторый код должен был работать точно так же, независимо от того, был он в транзакции или нет.

Трудно что-либо советовать, не заглядывая в ваш код, но мое первое предложение заключается в том, что TransactionScope() является непроизводительной нагрузкой, когда у вас 1 сервер SQL с 1 соединением.

Почему бы не использовать System.Data.SqlClient.SqlTransaction() вместо этого?

Документация гласит: "Если в транзакции базы данных открыто соединение с удаленным сервером, соединение с удаленным сервером включается в распределенную транзакцию, а локальная транзакция автоматически преобразуется в распределенную транзакцию". Однако, если вы используете только одно соединение, это очень странная ошибка. Вы уверены, что не вызываете какие-либо сторонние компоненты, которые могут создавать соединения с MS SQL, MS MQ или чем-то еще, что потребует создания распределенной транзакции?

Также, если вы используете TransactionScope() в процедуре CLR SQL Server, это будет способствовать транзакции в любом случае.

Также, если вы вызываете процедуру хранилища, которая обращается к таблице со связанного сервера SQL, я предполагаю, что это также потребует продвижения.

Вопрос довольно старый, возможно, вы уже знаете ответ и могли бы опубликовать его здесь для других. Спасибо!

У меня на самом деле та же проблема, и это, кажется, связано со спецификациями сервера БД. Я хотел бы, чтобы ваш dba посмотрел на загрузку ЦП блока, пока вы выполняете этот код. Это происходит в нашей среде, потому что мы пытаемся выполнить операцию обновления большого количества строк в нашей базе данных в рамках транзакции. Это происходит в нашей базе данных OLTP в одной из наших наиболее часто используемых таблиц, что приведет к конфликту блокировок. Что меня привлекает в этой проблеме, так это аспект времени ожидания, который я вижу в вашей трассировке стека. Независимо от того, какие значения тайм-аута вы устанавливаете, будь то в команде или в качестве аргумента для конструктора TransactionScope, похоже, проблема не решается. Способ, которым я собираюсь решить проблему, состоит в том, чтобы разделить коммиты. Надеюсь это поможет

Другие вопросы по тегам