TransactionScope和Oracle的问题

我们编写了一个C#3.5客户端,使用ODP.NET与Oracle数据库(11g)进行通信。

此应用程序具有批处理过程,其中执行长时间运行的任务,在TransactionScope中对数据库进行各种调用。

在我们的开发环境中一切顺利,但在我们的一个客户(拥有大量数据)的UAT环境中,出现两个交替(有时是一个,有时是另一个……)错误:

  1. 无法登记分布式事务
  2. 交易已中止。 (内部exception:事务超时)

我们目前使用一天的超时时间进行交易(用于测试目的)。

在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的内部工作原理,但是当你在范围内调用方法时,它会创建一个新的事务(假设inheritance自环境事务)。

可能是因为我是对的,这个新事务没有inheritance正确的超时(但是默认的超时),所以嵌套事务会导致这个超时exception?

如果没有,任何想法可能是什么? 另外,在环境事务中调用的方法中没有定义嵌套事务。

任何帮助将不胜感激!

编辑1:

该function的简化代码片段:

 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:

好的,事实certificate,方法’SaveItemToDB’中存在一个嵌套事务。 经过一些同事挖掘代码后,我看到它定义了自己的TransactionScope,但没有选项和超时。

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

所以我现在的问题如下:

  1. 是否有必要为嵌套事务定义超时值,还是从环境事务中inheritance它?
  2. 如果所有事务范围的超时设置(可能除了我不知道的内部工作之外)都相同,并且超时值定义为1天,超出值发生在1天之后,怎么可能发生超时exception?约。 10分钟?
  3. 是否可以阻止Oracle为连接字符串相同的事务创建分布式事务?
  4. 可能是分布式事务的额外开销导致事务中止的exception吗?

我更新了代码段,以便更好地反映情况。

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

希望通过这个添加,可以更容易地阐明这个问题!

由于我们找不到解决方案,因此我们决定停止将TransactionScope用于我们的目的,并自行安排回滚。

我发现TransactionScope和Oracle混合不好,也许SQL Server可以更好地处理它,但这对我们来说不是一个选择。

谢谢阅读。

machine.config中的默认事务超时是10分钟…这可能是你超时的原因。

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

我知道这是一个老问题,但我会加入它,因为我已经看到了这一点。

你在使用RAC吗? 您是否与DBA合作过以查看是否遇到锁定/阻塞问题。 我已经将System.Transactions与Oracle一起使用多年,而且我遇到类似问题的唯一时间是我们使用RAC并且需要完成其他配置。

这是发生的事情:你开始交易并在交易期间打开连接(这很好)。 但是,oracle服务未配置为分布式事务处理(它是服务上的一个简单复选框选项)。 因此,其他连接开始跨越RAC集群中的多个实例,并且相关事务彼此不知道导致.net进程阻塞自身。

这是一个简单的修复。 您正在使用的oracle服务只需要启用DTP。

虽然是一个老问题,我希望这个答案有所帮助……这对于长时间运行的事务尤其如此,因为底层IDbConnection不会保持打开更长的持续时间,并且会为部分事务管理器(连接池)创建新连接。 出于同样的原因,如果返回并使用相同的打开连接,则长事务可能成功,否则失败。 唯一的解决方案是控制连接创建并确保始终只使用一个连接。

首先解决主要问题:

  1. 如果所有事务范围的超时设置(可能除了我不知道的内部工作之外)都相同,并且超时值定义为1天,超出值发生在1天之后,怎么可能发生超时exception?约。 10分钟?

TransactionManager.MaximumTimeout属性,它是您尝试通过范围设置的任何内容。 在您的系统上,它设置为10 minutes ,但根据文档

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

至于其他问题:

  1. 是否有必要为嵌套事务定义超时值,还是从环境事务中inheritance它?

启动事务作用域 (即任何RequiresNew作用域,任何最外部的Required作用域以及任何具有Suppress作用域在嵌套堆栈上一级的Required作用域) 将建立transaction timeout ,并且就我对源的读取而言,超时不受嵌套作用域的影响

但是, 参与现有事务的每个嵌套作用域 (即任何具有RequiredRequiresNew作用域的一个级别的Required作用域)将建立自己的作用域超时 ,该超时除了上面提到的事务超时外还运行。

事务超时范围超时在内部以不同方式实现,但如果这些超时中的任何一个命中,则将回滚一个尚未成为Complete()的事务。

顺便说一句,前面提到的TransactionManager.MaximumTimeout仅适用于事务超时范围超时没有上限。 并不是真的很重要,因为最短的超时无论如何都是重要的。

  1. 是否可以阻止Oracle为连接字符串相同的事务创建分布式事务?

只要您在任何单个时间点只打开一个“物理”数据库连接,范围就不会升级到DTC。 如果我没记错的话,这可以与Oracle ODP.Net一起使用,尽管( 这个 )似乎声称相反(可能它当时不适用于该版本?)。

您可能也可能无法阻止并发连接, 即使对于嵌套范围,也可能阻止不同的数据库 (只要它们位于同一服务器上)。