为什么SqlConnection在事务进行中会被关闭?

3
在简化后的代码中,类似以下代码 conn2.Open(),在事务开始不到1秒钟的时间内执行(即没有超时问题),有时会抛出异常,声称(环境)事务已被中止 - 换句话说,conn2不是问题所在。
using (var ts1 = new TransactionScope(...))
{
    using (SqlConnection conn1 = new SqlConnection(connStr1))
    {
        conn1.Open();
        var cmd1 = conn1.CreateCommand();
        // use cmd1 .. 
    }

    using (SqlConnection conn2 = new SqlConnection(connStr2))
    {
        conn2.Open(); // THIS SOMETIMES THROWS
        // ...
    }

    ts1.Complete();
}

到目前为止,每次出现此异常时,日志都表明在失败的事务和其之前的最后一个事务之间至少有4.5分钟没有任何事务,因此看起来可能是TCP连接超时了。
但是,如果例如conn1已超时,则会在conn1.Open()上抛出异常。相反,它会在conn2.Open()上抛出异常,表明conn1已被关闭。
那么,conn1发生了什么?为什么只在调用conn2.Open()时才指示它?
当尝试使用上面的代码重现问题时,通过在调用conn1.Dispose()后手动终止支持conn1的TCP连接,我几乎可以在conn2.Open()上重现完全相同的堆栈跟踪。只有InvalidOperationException变成了System.Data.SqlClient.SqlException,其他所有内容都是100%相同的。但是,在conn1的活动和成功的conn1.Dispose()和conn2.Open()之间几乎没有时间经过,因此它不能是超时。
这是在:
WindowsServer 2008R2 .Net 3.5 SQLServer 2008R2
异常和堆栈跟踪:
System.Transactions.TransactionAbortedException: The transaction has aborted.
 ---> System.Transactions.TransactionPromotionException: Failure while attempting to promote transaction.
 ---> System.InvalidOperationException: The requested operation cannot be completed because the connection has been broken.
   at System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransaction(TransactionRequest transactionRequest, String name, IsolationLevel iso, SqlInternalTransaction internalTransaction, Boolean isDelegateControlRequest)
   at System.Data.SqlClient.SqlDelegatedTransaction.Promote()
   --- End of inner exception stack trace ---
   at System.Data.SqlClient.SqlDelegatedTransaction.Promote()
   at System.Transactions.TransactionStatePSPEOperation.PSPEPromote(InternalTransaction tx)
   at System.Transactions.TransactionStateDelegatedBase.EnterState(InternalTransaction tx)
   --- End of inner exception stack trace ---
   at System.Transactions.TransactionStateAborted.CheckForFinishedTransaction(InternalTransaction tx)
   at System.Transactions.Transaction.Promote()
   at System.Transactions.TransactionInterop.ConvertToOletxTransaction(Transaction transaction)
   at System.Transactions.TransactionInterop.GetExportCookie(Transaction transaction, Byte[] whereabouts)
   at System.Data.SqlClient.SqlInternalConnection.GetTransactionCookie(Transaction transaction, Byte[] whereAbouts)
   at System.Data.SqlClient.SqlInternalConnection.EnlistNonNull(Transaction tx)
   at System.Data.ProviderBase.DbConnectionInternal.ActivateConnection(Transaction transaction)
   at System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject)
   at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection)
   at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
   at System.Data.SqlClient.SqlConnection.Open()
   at My.Code...

编辑(针对提出的答案)

这是否可能是超时引起的呢?正如我所提到的,错误发生在交易不到1秒钟的时间内,因此(除非存在一些奇怪的 bug),它不可能仅仅是事务/事务范围/DTC 超时(这些超时都设置在30秒以上)。

Fwiw,在conn1.Dispose()之后、conn2.Open之前发生 DTC 超时,会看起来像这样(在日语操作系统上)。

System.Transactions.TransactionException: トランザクションの状態に対して操作が有効ではありません。
 ---> System.TimeoutException: トランザクションがタイムアウトしました。
   --- 内部例外スタック トレースの終わり ---
   場所 System.Transactions.TransactionState.EnlistPromotableSinglePhase(InternalTransaction tx, IPromotableSinglePhaseNotification promotableSinglePhaseNotification, Transaction atomicTransaction)
   場所 System.Transactions.Transaction.EnlistPromotableSinglePhase(IPromotableSinglePhaseNotification promotableSinglePhaseNotification)
   場所 System.Data.SqlClient.SqlInternalConnection.EnlistNonNull(Transaction tx)
   場所 System.Data.ProviderBase.DbConnectionInternal.ActivateConnection(Transaction transaction)
   場所 System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject)
   場所 System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection)
   場所 System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
   場所 System.Data.SqlClient.SqlConnection.Open()

应用服务器和SQL服务器之间有任何防火墙吗? - João Silva
如果您在事务范围内打开多个与数据库的连接,则该事务将升级为分布式事务。也许服务器上的DTC存在一些问题? - Magnus
@JoãoSilva 没有防火墙,只有一两个交换机。 - Evgeniy Berezovsky
@EugeneBeresovsky,你找到解决方案了吗? - wal
很遗憾,我不知道。虽然目前情况可能已经改变,但是那个项目的大部分数据库相关工作已经转移到了Oracle,这可能是为什么我再也没有听说过它的原因... - Evgeniy Berezovsky
1个回答

1
似乎是您的DTC超时出现了问题,这种情况发生在您声明第二个连接并将其提升为dtc事务的同时超时了。您可以在机器设置中更改超时时间。更改所有DTC事务的超时时间可能会对性能产生影响,因此当将其更改为大值时,请注意性能。
machine.config中的10分钟超时:
<configuration>
 <system.transactions>
   <machineSettings maxTimeout="00:10:00" /> 
 </system.transactions>
</configuration> 

你认为这是DTC超时的原因吗?最内层的堆栈帧似乎是SqlInternalConnectionTds.ExecuteTransaction,这表明原始问题更接近于SqlConnection方面。此外,我在我的问题中添加了说明,并重申了为什么我不认为它可能只是一个简单的超时问题。对于特定的DTC超时情况(很容易复现),我粘贴了堆栈跟踪,它与我在生产环境中得到的堆栈跟踪非常不同。 - Evgeniy Berezovsky
我曾在 DTC 超时时看到过完全相同的堆栈跟踪。检查所有配置并使用更长的超时时间进行测试。还要检查您是否具有正确的 64/32 位配置。 - Peter
“确切的堆栈跟踪”是指仅包括从TransactionStateAborted.CheckForFinishedTransaction开始的外部异常,还是确切相同的两个内部异常,包括SqlInternalConnectionTds.ExecuteTransaction - Evgeniy Berezovsky
超时设置没问题。但是它可能是一个空闲超时(我提到了在这个错误之前总是有一个空闲期),断开了与 MSDTC 的 TCP (?) 连接,该连接在某个连接池中闲置时间过长,当升级程序尝试重新使用它时,有时会失败? - Evgeniy Berezovsky
同时,我在相关机器的组件服务中检查了聚合的dtc统计数据,结果发现每个提交的事务就有4个被中止的dtc事务!(已提交:77k,已中止:369k)这里显然出了问题。我仍然不确定是DTC中止了事务,但我知道该将调查重点放在哪里。 - Evgeniy Berezovsky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接