在 SQL Server 2005 上使用 System.Transactions 时出现 TransactionInDoubtException
这篇文章的根本问题是“为什么非促销的 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();
}
谢谢
The underlying question to this post is "Why would a non-promoted LTM Transaction ever be in doubt?"
I'm getting System.Transactions.TransactionInDoubtException and i can't explain why. Unfortunately i cannot reproduce this issue but according to trace files it does happen. I am using SQL 2005, connecting to one database and using one SQLConnection so i don't expect promotion to take place. The error message indicates a timeout. However, sometimes I get a timeout message but the exception is that the transaction has aborted as opposed to in doubt, which is much easier to handle.
Here is the full stack trace:
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()
Any ideas? Why am i getting in doubpt and what should i do when i get it?
EDIT for more information
I actually still don't have the answer for this. What I did realize is that the transaction actually partially commits. One table gets the insert but the other does not get the update. The code is HEAVILY traced and there is not much room for me to be missing something.
Is there a way I can easily find out if the transaction has been promoted. Can we tell from the stack trace if it is? SIngle Phase commit (which is in the strack trace) seems to indicate no promotion to me, but maybe i'm missing something. If its not getting promoted then how can it be in doubt.
Another interesting piece to the puzzle is that i create a clone of the current transaction. I do that as a workarround to this issue.
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=914869&SiteID=1
Unfortunately, i don't know if this issue has been resolved. Maybe creating the clone is causing a problem. Here is the relevant code
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();
}
Thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
目前公认的答案是,非促销 LTM(非 MSDTC)交易永远不会有疑问。 经过对类似问题的大量研究,我发现这是不正确的。
由于单阶段提交协议的实现方式,在事务管理器向其下属发送 SinglePhaseCommit 请求之后,以及下属回复已提交/提交之前,有一小段时间事务处于“不确定”状态。已中止/或已准备(需要升级/升级到 MSDTC)消息。 如果在此期间连接丢失,则该事务“有疑问”,因为 TransactionManager 在要求下级执行 SinglePhaseCommit 时从未收到响应。
来自 MSDN 单阶段提交 ,另请参阅此答案底部的“单阶段提交流程”图像:
另外,这里还有一些我发现的导致 System.Transaction 升级/升级为 MSDTC 事务的实际示例(这与 OP 没有直接关系,但我发现非常有用。在 VS 2013 中进行了测试, SQL Server 2008 R2、.NET 4.5(除非另有说明):(
以下是导致升级的 MS 官方列表:http://msdn.microsoft.com/en-us/library/ms229978(v=vs.85).aspx
The current accepted answer is that a non-promoted LTM (non-MSDTC) Transaction can never be in doubt. After much research into a similar issue, I have found that this is incorrect.
Due to the way the single phase commit protocol is implemented, there is a small period of time where the transaction is "in doubt", after the Transaction Manager sends the SinglePhaseCommit request to its subordinate, and before the subordinate replies with either a committed/aborted/or prepared (needs to promote/escalate to MSDTC) message. If the connection is lost during this time, then the transaction is "in doubt", b/c the TransactionManager never received a response when it asked the subordinate to perform a SinglePhaseCommit.
From MSDN Single-Phase Commit, also see "Single phase commit flow" image at the bottom of this answer:
Also here are some practical examples of things I've found that cause System.Transaction promotion/escalation to a MSDTC transaction (this is not directly related to the OP, but I have found very useful. Tested in VS 2013, SQL Server 2008 R2, .NET 4.5 except where noted):
And here is the MS official list of what causes escalation: http://msdn.microsoft.com/en-us/library/ms229978(v=vs.85).aspx
答案是不能。 显然正在发生的事情是促销正在进行中。 (我们无意中发现了这一点)我仍然不知道如何检测是否正在发生促销尝试。 这对于检测这一点非常有用。
The answer is that it can't. What apparently was happening was that promotion was taking place. (We accidentally discovered this) I still don't know how to detect if a promotion attempt is happening. That would have been extreamly useful in detecting this.
如果不查看你的代码,很难给出任何建议,但我的第一个建议是,当你有 1 个 SQL 服务器和 1 个连接时,TransactionScope() 是一种开销。
为什么不使用 System.Data.SqlClient.SqlTransaction() 来代替?
文档称“如果在数据库事务中打开到远程服务器的连接,则到远程服务器的连接将被纳入分布式事务,并且本地事务将自动提升为分布式事务。” 然而,如果你真的只使用一个连接,那就是一个非常奇怪的错误。 您确定您没有调用任何可以创建与 MS SQL、MS MQ 或其他需要创建分布式事务的连接的第三方组件吗?
此外,如果您在 SQL Server CLR 过程中使用 TransactionScope(),无论如何它都会促进事务。
另外,如果您调用从链接的 SQL Server 访问表的存储过程,我想这也需要升级。
这个问题很老了,也许你已经知道答案并可以将其发布在这里供其他人使用。 谢谢!
Hard to advice anything without looking into your code, but my first suggestion is that TransactionScope() is an overhead when you have 1 SQL server with 1 connection.
Why not to use System.Data.SqlClient.SqlTransaction() instead?
Documentation sais that "If a connection to a remote server is opened within a database transaction, the connection to the remote server is enlisted into the distributed transaction and the local transaction is automatically promoted to a distributed transaction." However if you use really only one connection is a very strange error. Are you sure that you are not calling any 3rd party components that can create connections to MS SQL, MS MQ or something else that will require a distibuted transaction to be created?
Also if you use TransactionScope() in SQL Server CLR procedure, it will promote transaction in any case.
Also if you call a store procedure that access a table from linked SQL server, I suppose this will also require promotion.
The question is quite old, perhaps you already know the answer and could post it here for others. Thanks!
打败了我。
我习惯于手动对“BEGIN TRANSACTION”和“COMMIT”或“ROLLBACK”执行 ExecuteNonQuery。
很偶然的是,当某些代码需要无论是否在事务中都以相同的方式工作时,这种方法效果非常好。
Beats the heck out of me.
I'm in the habit of doing ExecuteNonQuery on "BEGIN TRANSACTION" and "COMMIT" or "ROLLBACK" by hand.
Quite by accident this worked out really well when some code needed to work just the same whether it was in a transaction or not.
我实际上遇到了同样的问题,它似乎与数据库服务器的规格有关。 当您执行此代码时,我会让您的 dba 查看一下机器的 CPU 利用率。 在我们的环境中会发生这种情况,因为我们正在事务中尝试对数据库中的大量行进行更新操作。 这种情况发生在我们最常用的表之一的 OLTP 数据库上,这将导致锁争用。 我发现这个问题令人着迷的是我在堆栈跟踪中看到的超时方面。 无论您设置什么超时值(无论是在命令上还是作为 TransactionScope 构造函数的参数),它似乎都无法解决问题。 我解决这个问题的方法是对提交进行分块。 希望这可以帮助
I am actually having the same problem and it seems to be related to the specs of the db server. I would have your dba have a look at the CPU utilization of the box while you are executing this code. This happens in our environment because we are attempting an update operation on a large number of rows in our database within a transaction. This is happening on our OLTP database on one of our most used tables which will create lock contention. What I find fascinating about the problem is the time out aspect which I see in your stack trace. No matter what time out values you set whether it be on the command or as an argument to the constructor of the TransactionScope it does not seem to adress the issue. The way I am going to address the issue is to chunk the commits. Hope this helps