我们已经编写了一个使用 ODP.NET 与 Oracle 数据库 (11g) 通信的 C# 3.5 客户端。
该应用程序有一个批处理过程,其中执行长时间运行的任务,在 TransactionScope 内对数据库进行各种调用。
在我们的开发环境中,一切顺利,但在我们的一个客户(拥有大量数据)的 UAT 环境中,会出现两个交替的(有时是一个,有时是另一个......)错误:
- 无法加入分布式
- 事务交易已中止。 (内部例外:交易超时)
我们目前对交易使用一天的超时(出于测试目的)。
在 UAT 环境上运行所述进程会导致大约 1 秒后停止。 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 天时,怎么可能发生超时异常,其中异常发生在约。 10分钟?
- 是否可以阻止Oracle为连接字符串相同的事务创建分布式事务?
- 分布式事务增加的开销是否会导致事务中止等异常?
我更新了代码片段,以便它更好地反映情况。
(顺便说一句:第二个嵌套事务是必要的,因为 DAL 还单独保留一些子项(如果存在),并且如果在保留子项时出现任何问题,当然应该回滚整个项)
希望通过这一补充,可以更容易地阐明这个问题!
we have written a C# 3.5 client talking to an Oracle database (11g) using the ODP.NET.
This application has a batch process where a long running task is performed making various calls to the database within a TransactionScope.
On our development environment all goes well, but at the UAT environment of one of our clients (who has loads of data) two alternating (sometimes the one, sometimes the other...) errors occur:
- Unable to enlist in a distributed transaction
- The transaction has aborted. (inner exception: Transaction Timeout)
We currently use a time-out of one day for the transaction (for testing purposes).
Running said process on the UAT environment causes to halt after approx. 10 mins with one of above exceptions, so no way near the timeout value.
Here's a snippet of the stacktrace for the second error:
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)
The process tries to save an item to the DB within the transaction scope, but the stacktrace shows that the constructor is hit for the TransactionScope class, meaning it creates a new TransactionScope.
Am I right so far?
Because I don't know much of the inner workings of the TransactionScope, but it seems like when you call a method within the scope, it will create a new transaction (assumingly inheriting from the ambient transaction).
Could it be that if I am right, that this new transaction does not inherit the correct timeout (but the default one), so that a nested transaction will cause this timeout exception?
If not, any thoughts on what it possibly can be? On a side note, there are no nested transactions defined within the methods called from within the ambient transaction.
Any help would be greatly appreciated!
Edit 1:
Simplified code snippet of the 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();
}
}
Edit 2:
Okay, so as it turns out, there is a nested transaction going on in the method 'SaveItemToDB'. After some digging through the code a colleague made, I saw that it has its own TransactionScope defined, but without options and timeout.
After modifying this method so that it has the same parameters regarding timeout, I ran the code again on the customer's server and still no luck (again the transaction aborted error with the time out).
So my questions are now as follows:
- Is it necessary to define timeout values for nested transactions or do they inherit this from the ambient transaction?
- How is it possible that a timeout exception can occur when the timeout setting is (presumably, aside from inner workings that I do not know about) the same for all transaction scopes and has a timeout value defined of 1 day, where the exception occurs after approx. 10 minutes?
- Is it possible to prevent Oracle from creating a distributed transaction for transactions where the connectionstring is the same?
- Can it be that the added overhead of a distributed transaction causes exceptions like the transaction aborted one?
I updated the code snippet so it better reflects the situation.
(btw: the second, nested transaction, is necessary because the DAL also seperately persists some child items, if present, and the whole item should of course, be rolled back if anything goes wrong while persisting the child items)
Hopefully with this addition it will be easier to shed some light on this issue!
发布评论
评论(6)
由于找不到解决方案,我们决定停止使用 TransactionScope 来实现我们的目的并自行安排回滚。
我发现 TransactionScope 和 Oracle 不能很好地混合,也许 SQL Server 处理得更好,但这不是我们的选择。
感谢您的阅读。
Because we couldn't find a solution, we have decided to stop using the TransactionScope for our purposes and arrange the rollback ourselves.
I find that TransactionScope and Oracle do not mix well, perhaps SQL Server handles it better, but that is not an option for us.
Thanks for reading.
machine.config 中的默认事务超时是 10 分钟...这可能就是您超时的原因。
the default transaction timeout in machine.config is 10 minutes...that is probably why you are timing out.
您可以展示一段代码吗?从你提到的我能找到的唯一的东西是与 System.Transactions 有关的。讨论位于此处。当然,他们的“解决方案”是确保您至少使用 ODP.NET 11.1.0.6.20 或更高版本。
Is it possible for you to please show a snippet of code? From what you mentioned The only thing I could find was related with System.Transactions. The discussion is here. Of course their "solution" is to make sure you are using at least ODP.NET 11.1.0.6.20 or higher.
我知道这是一个老问题,但我会补充它,因为我已经看过很多了。
您使用的是 RAC 吗?您是否曾与 DBA 一起检查过是否遇到锁定/阻塞问题。我已经在 Oracle 中使用 System.Transactions 多年,唯一一次遇到类似问题是当我们使用 RAC 且需要完成其他配置时。
发生的情况如下:您启动一个事务并在事务期间打开连接(这很好)。但是,oracle 服务未配置为分布式事务处理(它是服务上的一个简单的复选框选项)。因此,额外的连接开始跨越 RAC 集群中的多个实例,并且相关事务彼此不知情,从而导致 .net 进程自行阻塞。
这是一个简单的修复。您正在使用的 oracle 服务只需要启用 DTP。
I know this is an old question but I'll add to it since I've seen this quite a bit.
Are you using RAC? Have you worked with a DBA to see if you're experiencing locking/blocking. I've used System.Transactions with Oracle for years and the only time I've had similar issues is when we were using RAC and additional configuration needed to be done.
Here's what happens: You start a transaction and are opening connections during the transaction (which is fine). However, the oracle service is not configured for distributed transaction processing (it's a simple checkbox option on the service). So additional connections start spanning more than one instance in the RAC cluster, and the related transactions are unaware of each other causing the .net process to block itself.
It's a simple fix. The oracle service you are using just needs DTP enabled.
虽然是一个老问题,但我希望这个答案有帮助......
对于长时间运行的事务尤其会发生这种情况,因为底层 IDbConnection 不会长时间保持打开状态,并且会为部分事务范围(连接池)创建新连接。出于同样的原因,如果返回并使用相同的打开连接,长事务可能会成功,否则会失败。解决此问题的唯一解决方案是控制连接创建并确保自始至终只使用一个连接。
although an old question, am hoping this answer helps...
this especially happens for a long running transactions because the underlying IDbConnection does not remain open for longer duration and new connection is created for parts of transactionscope (connection pooling). it is for the same reason, the long transaction could succeed if the same open connection is returned and used else it fails. Only solution for this is to control connection creation and ensure that only one connection is used throughout.
首先解决主要问题:
有
TransactionManager.MaximumTimeout
属性,它是 您试图通过范围设置的任何内容的上限。在您的系统上,它设置为10分钟
,但根据文档至于其他问题:
启动事务的范围(即任何
RequiresNew
范围、任何最外层Required
范围以及具有以下功能的任何Required
范围:抑制
作用域位于嵌套堆栈的上一层)将建立事务超时
,并且据我阅读来源消失,此超时不受嵌套范围的影响。但是,参与现有事务的每个嵌套作用域(即任何具有
Required
或RequiresNew
作用域的Required
作用域堆栈上一级)将建立自己的范围超时,除了上面提到的事务超时之外运行。事务超时和范围超时在内部实现方式不同,但如果其中任何一个超时,尚未完成的事务将被
Complete()
d被回滚。顺便说一句,前面提到的
TransactionManager.MaximumTimeout
仅适用于事务超时。 范围超时没有上限。这并不重要,因为无论如何最短的超时才是最重要的。只要在任一时间点仅打开一个“物理”数据库连接,范围就不会升级为 DTC。如果我没记错的话,这适用于 Oracle ODP.Net,尽管 (this) 似乎声称相反(也许它没有使用当时的版本?)。
您可能无法阻止并发连接即使具有嵌套范围,并且对于不同的数据库(只要它们是在同一台服务器上)。
Addressing the main issue first:
There is the
TransactionManager.MaximumTimeout
property which is the upper bound of whatever you are trying to set via your scope. On your system, it is set to10 minutes
, but according to the documentationAs to the other questions:
The scope initiating a transaction (i.e. any
RequiresNew
scope, any outermostRequired
scope, and anyRequired
scope that has aSuppress
scope one level up the nesting stack) will establish atransaction timeout
, and as far as my reading of the sources goes, this timeout is not affected by nested scopes.However, every nested scope participating in an existing transaction (i.e. any
Required
scope that has aRequired
orRequiresNew
scope one level up the stack) will establish its own scope timeout that runs in addition to the transaction timeout mentioned above.Transaction timeouts and scope timeouts are implemented differently internally, but if any one of these timeouts hits, a transaction yet to be
Complete()
d would be rolled back.Btw, aforementioned
TransactionManager.MaximumTimeout
only applies to transaction timeouts. Scope timeouts do not have an upper bound. Not that it really matters, as the shortest timeout is what counts anyway.As long as you have only one "physical" DB connection open at any single point in time, the scope will not escalate to DTC. If I recall correctly, this works with Oracle ODP.Net, despite (this) seemingly claiming the opposite (maybe it did not work with the version at the time?).
You may or may not be able to prevent concurrent connections even with nested scopes, and for different databases (as long as they are on the same server).