TransactionInDoubtException с использованием System.Transactions на SQL Server 2005 - PullRequest
5 голосов
/ 11 июня 2009

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

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

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

System.Transactions.TransactionInDoubtException: The transaction is in doubt. ---> System.Data.SqlClient.SqlException: Timeout expired.  The timeout period elapsed prior to completion of the operation or the server is not responding.
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
   at System.Data.SqlClient.TdsParserStateObject.ReadSniError(TdsParserStateObject stateObj, UInt32 error)
   at System.Data.SqlClient.TdsParserStateObject.ReadSni(DbAsyncResult asyncResult, TdsParserStateObject stateObj)
   at System.Data.SqlClient.TdsParserStateObject.ReadNetworkPacket()
   at System.Data.SqlClient.TdsParserStateObject.ReadBuffer()
   at System.Data.SqlClient.TdsParserStateObject.ReadByte()
   at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   at System.Data.SqlClient.TdsParser.TdsExecuteTransactionManagerRequest(Byte[] buffer, TransactionManagerRequestType request, String transactionName, TransactionManagerIsolationLevel isoLevel, Int32 timeout, SqlInternalTransaction transaction, TdsParserStateObject stateObj, Boolean isDelegateControlRequest)
   at System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransactionYukon(TransactionRequest transactionRequest, String transactionName, IsolationLevel iso, SqlInternalTransaction internalTransaction, Boolean isDelegateControlRequest)
   at System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransaction(TransactionRequest transactionRequest, String name, IsolationLevel iso, SqlInternalTransaction internalTransaction, Boolean isDelegateControlRequest)
   at System.Data.SqlClient.SqlDelegatedTransaction.SinglePhaseCommit(SinglePhaseEnlistment enlistment)
   --- End of inner exception stack trace ---
   at System.Transactions.TransactionStateInDoubt.EndCommit(InternalTransaction tx)
   at System.Transactions.CommittableTransaction.Commit()
   at System.Transactions.TransactionScope.InternalDispose()
   at 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 ]

12 голосов
/ 07 мая 2014

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

В связи с тем, как реализован протокол однофазной фиксации, существует небольшой период времени, когда транзакция «находится под сомнением», после того, как менеджер транзакций отправляет запрос SinglePhaseCommit своему подчиненному, и до того, как подчиненный отвечает либо зафиксированное / прерванное / или подготовленное (необходимо повысить / повысить до MSDTC) сообщение. Если в течение этого времени соединение теряется, то транзакция «находится под сомнением», так как TransactionManager никогда не получал ответ, когда просил подчиненного выполнить 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 () для одного и того же экземпляра соединения. </li>
  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. Вызывается запрос на «преобразование» транзакции в другой домен приложения или в другой процесс. Например, сериализация объекта транзакции через границу домена приложения. Объект транзакции является маршалированным по значению, что означает, что любая попытка передать его через границу домена приложения (даже в том же процессе) приводит к сериализации объекта транзакции. Вы можете передать объекты транзакции, вызвав удаленный метод, который принимает транзакцию в качестве параметра, или вы можете попытаться получить доступ к удаленному компоненту, обслуживаемому транзакцией. Это сериализует объект транзакции и приводит к эскалации, как при сериализации транзакции в домене приложения. Он распространяется, и локальный менеджер транзакций больше не подходит.

Single phase commit flow

2 голосов
/ 27 августа 2010

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

0 голосов
/ 17 сентября 2009

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

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

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

0 голосов
/ 29 января 2010

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

0 голосов
/ 13 июля 2009

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

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

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

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

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

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

...