使用TransactionScope和Oracle时的问题

4
我们编写了一个C# 3.5客户端,使用ODP.NET连接Oracle数据库(11g)。该应用程序具有批处理过程,在TransactionScope内执行长时间运行的任务,进行多个对数据库的调用。
在我们的开发环境中,一切都很顺利,但是在我们的一个客户的UAT环境中(他们有大量数据),会出现两个交替的错误(有时是一个,有时是另一个...):
1. 无法加入分布式事务 2. 事务已终止。(内部异常:事务超时)
我们目前为事务使用了一天的超时时间(仅用于测试目的)。
在UAT环境上运行该进程后,大约10分钟后就会因为以上错误之一而停止,远远没有达到超时值。
以下是第二个错误的堆栈跟踪片段:
at System.Transactions.TransactionStatePromotedAborted.CreateAbortingClone(InternalTransaction tx)
   at System.Transactions.DependentTransaction..ctor(IsolationLevel isoLevel, InternalTransaction internalTransaction, Boolean blocking)
   at System.Transactions.Transaction.DependentClone(DependentCloneOption cloneOption)
   at System.Transactions.TransactionScope.SetCurrent(Transaction newCurrent)
   at System.Transactions.TransactionScope.PushScope()
   at System.Transactions.TransactionScope..ctor(TransactionScopeOption scopeOption)
   at System.Transactions.TransactionScope..ctor()
   at Application.Domain.DataAccess.Oracle.EntityDaoBase`2.SaveItem(TEntity item, EntityReference`1 user)

该过程试图在事务范围内保存项目到数据库中,但堆栈跟踪显示TransactionScope类的构造函数被调用,这意味着它创建了一个新的TransactionScope。
到目前为止,我的理解是正确的吗?
因为我不太了解TransactionScope的内部工作原理,但似乎当你在范围内调用方法时,它会创建一个新的事务(可能从环境事务继承)。
如果我是对的,是否可能这个新的事务没有继承正确的超时时间(而是默认值),所以嵌套的事务会导致超时异常?
如果不是,你有什么可能性是什么?顺便说一句,在环境事务内调用的方法中没有定义嵌套事务。
非常感谢您的任何帮助!
编辑1:
该功能的简化代码片段:
public void SomeLengthyBatchProcess()
{
   using (var transaction = new TransactionScope(TransactionScopeOption.Required, new TimeSpan(1, 0, 0, 0)))
   {
       foreach (var item in Items)
       {
          SaveItemToDB(item);
       }

       transaction.Complete();
   }
}

public void SaveItemToDB(object item)
{
   using (var transaction = new TransactionScope(TransactionScopeOption.Required, new TimeSpan(1, 0, 0, 0)))
   {
       // Performing data persistency here

       transaction.Complete();
   }
}

编辑2:

好吧,事实证明,在“SaveItemToDB”方法中确实有一个嵌套事务。通过阅读同事编写的代码,我发现它定义了自己的TransactionScope,但没有设置选项和超时时间。

在修改此方法以使其具有相同的超时参数后,我在客户端服务器上再次运行代码,但仍然没有运气(再次出现事务中止错误和超时)。

所以我的问题现在如下:

  1. 在嵌套事务中是否需要定义超时值,或者它们是从环境事务继承的?
  2. 当所有事务范围的超时设置(除了我不知道的内部工作之外)都相同时,超时异常如何发生,并且已定义1天的超时值,在大约10分钟后发生异常?
  3. 是否可以防止Oracle为连接字符串相同的事务创建分布式事务?
  4. 是否可能由于分布式事务的额外开销导致像事务中止这样的异常?

我更新了代码片段,使其更好地反映了情况。

(顺便说一句:第二个嵌套事务很必要,因为DAL还单独持久化了一些子项,如果有的话,整个项当然应该在持久化子项时出现任何问题时回滚)

希望通过这个补充,能更容易地解决这个问题!

6个回答

6
由于我们无法找到解决方案,我们已决定停止使用TransactionScope并自己安排回滚。
我发现TransactionScope和Oracle不太兼容,可能SQL Server处理得更好,但这对我们来说不是一个选择。
谢谢阅读。

1

机器配置文件中的默认事务超时时间为10分钟...这可能是您超时的原因。


0

首先解决主要问题:

  1. 当超时设置被定义为1天,而异常在大约10分钟后发生时,如何可能出现超时异常(除了我不知道的内部工作)?

有一个名为TransactionManager.MaximumTimeout的属性,它是您尝试通过作用域设置的上限。在您的系统上,它设置为10分钟,但根据文档

此值可以在配置文件的MachineSettingsSection中设置。

至于其他问题:

  1. 是否需要为嵌套事务定义超时值,或者它们从环境事务继承超时值?

启动事务的范围(即任何RequiresNew范围、任何最外层的Required范围以及任何具有嵌套堆栈上一级的Suppress范围的Required范围)将建立一个事务超时,就我阅读来源的理解,这个超时时间不受嵌套范围的影响

然而,每个参与现有事务的嵌套范围(即具有堆栈上一级的RequiredRequiresNew范围的Required范围)都将建立自己的范围超时,并且会在上述事务超时之外运行。

事务超时范围超时在内部实现上有所不同,但如果其中任何一个超时,尚未Complete()的事务将被回滚。

顺便提一下,前面提到的TransactionManager.MaximumTimeout仅适用于事务超时作用域超时没有上限。不过这并不重要,因为最短的超时时间才是最重要的。

  1. 是否有可能防止Oracle在连接字符串相同的情况下创建分布式事务?

只要您在任何单个时间点上只打开了一个“物理”数据库连接,作用域就不会升级到DTC。如果我没记错的话,尽管(this)似乎声称相反,但这适用于Oracle ODP.Net(也许当时的版本无法正常工作?)。

您可能无法防止并发连接即使使用嵌套作用域,并且针对不同的数据库(只要它们在同一台服务器上)。


0

虽然这是一个老问题,但我希望这个答案能够有所帮助...

这种情况通常发生在长时间运行的事务中,因为底层IDbConnection并没有保持开启状态,而是为部分事务范围创建了新的连接(连接池) 。出现这种情况的原因也正是如此,即如果使用相同的开放连接返回并使用,则长事务可能会成功,否则会失败。唯一的解决方法是控制连接创建并确保在整个过程中仅使用一个连接。


0

我知道这是一个老问题,但我想补充一下,因为我经常看到这种情况。

你是否使用了RAC?是否与DBA合作,以查看是否遇到了锁定/阻塞的情况。我多年来一直在使用System.Transactions和Oracle,唯一遇到类似问题的情况是当我们使用RAC时,需要进行其他配置。

以下是发生的情况:您启动事务并在事务期间打开连接(这很好)。然而,Oracle服务未配置为分布式事务处理(这是服务上的简单复选框选项)。因此,额外的连接开始跨越RAC集群中的多个实例,并且相关的事务彼此不知道,导致.NET进程自身被阻塞。

这是一个简单的解决方法。您正在使用的Oracle服务只需要启用DTP即可。


0

请问您能否提供一小段代码示例吗?根据您提到的,我所找到的唯一相关内容是与System.Transactions有关的。讨论在这里。当然,他们的“解决方案”是确保您至少使用ODP.NET 11.1.0.6.20或更高版本。


谢谢您的回复。我已经看过那篇文章,但没有帮助。我将在原问题中更新一段代码片段。 - Mace

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