使用SQL Server 2005上的System.Transactions的TransactionInDoubtException

Mar*_*ark 5 c# transactions sql-server-2005 transactionscope system.transactions

这篇文章的基本问题是"为什么非升级的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()

有任何想法吗?为什么我会怀疑,当我得到它时我该怎么办?

编辑以获取更多信息

我实际上仍然没有这个答案.我所意识到的是交易实际上是部分提交的.一个表获取插入但另一个表未获得更新.代码是HEAVILY跟踪,没有太多空间让我错过一些东西.

有没有办法可以轻松找出交易是否已被提升.我们可以从堆栈跟踪中判断它是否存在?单一阶段提交(在strack trace中)似乎表明没有向我推广,但也许我错过了一些东西.如果它没有得到提升那么它怎么会有疑问.

这个难题的另一个有趣的部分是我创建了当前事务的克隆.我这样做是为了解决这个问题. 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();
}
Run Code Online (Sandbox Code Playgroud)

谢谢

Bat*_*ech 12

目前接受的答案是,非升级的LTM(非MSDTC)交易永远不会有疑问.经过对类似问题的大量研究后,我发现这是不正确的.

由于单阶段提交协议的实现方式,在事务管理器将SinglePhaseCommit请求发送给其下级之后,以及在下级回复之前,事务处于"怀疑"状态的时间很短.中止/或准备(需要推广/升级到MSDTC)消息.如果在此期间连接丢失,则事务处于"不确定"状态,b/c当TransactionManager要求下属执行SinglePhaseCommit时,它从未收到响应.

MSDN单阶段提交,还可以在此答案的底部看到"单阶段提交流"图像:

这种优化有一个可能的缺点:如果事务管理器在发送单阶段提交请求之后但在接收结果通知之前失去与下级参与者的联系,则它没有可靠的机制来恢复事务的实际结果.因此,事务管理器向等待信息结果通知的任何应用程序或选民发送In Inouboub结果

这里还有我发现的一些实际例子,它们会导致System.Transaction升级/升级到MSDTC事务(这与OP没有直接关系,但我发现它非常有用.在VS 2013中测试过,SQL Server 2008 R2 ,.NET 4.5除非另有说明):

  1. (这个特定于SQL Server 2005或兼容级别<100) - 在TransactionScope中的任何位置多次调用Connection.Open().这还包括在SAME连接实例上调用.Open(),. Close(),. Open().
  2. 在TransactionScope中打开嵌套连接
  3. 使用不使用连接池的多个连接,即使它们未嵌套并连接到同一数据库.
  4. 涉及链接服务器的查询
  5. 使用TransactionScope的SQL CLR过程.请参阅:http://technet.microsoft.com/en-us/library/ms131084.aspx "只有在访问本地和远程数据源或外部资源管理器时才应使用TransactionScope.这是因为TransactionScope [在CLR内]始终导致事务促进,即使它仅在上下文连接中使用"
  6. 似乎如果使用连接池,并且Connection1中使用的相同的物理连接由于某种原因在Connections"2到N"中不可用,则整个事务将被提升(b/c这些被视为2个单独持久的资源,项目#2是下面的MS官方列表).我没有测试/确认这个特例,但是我对它是如何工作的理解.它在后台有意义b/c这类似于使用嵌套连接或不使用连接池b/c使用多个物理连接. 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. 调用将事务"编组"到不同应用程序域或不同进程的请求.例如,跨应用程序域边界的事务对象的序列化.事务对象是按值封送的,这意味着任何尝试将其传递到应用程序域边界(即使在同一进程中)都会导致事务对象的序列化.您可以通过对以事务作为参数的远程方法进行调用来传递事务对象,也可以尝试访问远程事务服务组件.这会序列化事务对象并导致升级,就像跨应用程序域序列化事务一样.它正在分发,本地事务管理器已不再适用.

单阶段提交流程


Mar*_*ark 3

答案是不能。显然正在发生的事情是促销正在进行中。(我们无意中发现了这一点)我仍然不知道如何检测是否正在发生促销尝试。这对于检测这一点非常有用。

  • 您本可以处理 [DistributedTransactionStarted](http://msdn.microsoft.com/en-us/library/system.transactions.transactionmanager.distributedtransactionstarted.aspx) 事件。 (3认同)