处理 NHibernate 事务错误

发布于 2024-09-12 05:46:18 字数 427 浏览 8 评论 0原文

我们的应用程序(使用 NHibernate 和 ASP.NET MVC)在进行压力测试时会引发大量 NHibernate 事务错误。主要类型有:

  1. 事务未连接,或已断开连接
  2. 行被另一个事务更新或删除(或未保存值映射不正确)
  3. 事务(进程 ID 177)在锁资源上与另一个进程发生死锁,并被选为死锁受害者。重新运行事务。

有人可以帮我找出异常 1 的原因吗? 我知道我必须处理代码中的其他异常。有人可以向我指出可以帮助我有效处理这些错误的资源吗?

问:我们如何管理会话和交易?

答:我们正在使用 Autofac。对于每个服务器请求,我们创建一个新的请求容器,其中的会话位于容器生命周期范围内。激活会话后我们就开始交易。当请求完成时,我们提交事务。在某些情况下,交易可能会很大。为了简化,每个服务器请求都包含在一个事务中。

Our application (which uses NHibernate and ASP.NET MVC), when put under stress tests throws a lot of NHibernate transaction errors. The major types are:

  1. Transaction not connected, or was disconnected
  2. Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)
  3. Transaction (Process ID 177) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

Can someone help me in identifying the reason for Exception 1?
I know I have to handle the other exceptions in my code. Can someone point me to resources which can help me handle these errors in an efficient manner?

Q. How do we manage Sessions and Transactions?

A. We are using Autofac. For every server request, we create a new request container which has the session in the container lifetime scope. On activating the session we begin the transaction. When the request completes, we commit the transaction. In some cases, the transaction can be huge. To simplify, every server request is contained in a transaction.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

够钟 2024-09-19 05:46:18

看看这个线程:
http://n2cms.codeplex.com/Thread/View.aspx?ThreadId=85016 基本上它

所说的导致此异常的可能原因是:

2010-02-17 21:01:41,204 1 警告
NHibernate.Util.ADOExceptionReporter -
System.Data.SqlClient.SqlException:
数据库的事务日志
“数据库名称”已满。找出来
为什么日志中的空间不能重用,
请参阅 log_reuse_wait_desc 列
系统数据库


由于事务日志的大小与事务期间完成的工作量成正比,因此也许您应该考虑将事务边界跨命令处理程序“处理”事务写入部分的命令。然后,您可以使用会话 #X 加载您想要改变的状态,改变它并提交它,所有这些都作为 #X 中的一个工作单元。

至于读取方面,您可能会拥有另一个读取数据的 ISession#Y;这个 ISession 可用于在 RepeatableRead 或类似 Futures 功能的东西中批量读取,并且可以简单地从缓存中读取(尽管它确实是一个拐杖)。这样做可能会帮助您从并非如此的“错误”中恢复过来。活锁、死锁和受害者事务。

每个请求使用事务的问题是,您的 ISession 在您工作时获取大量簿记数据,所有这些数据都是事务的一部分。因此,数据库将数据(角色、列、表等)标记为参与事务,导致等待图跨越“实体”(在数据库意义上,而不是 DDD 意义上),而这些实体实际上并不是事务的一部分您的应用程序所采取的命令的事务边界。

根据记录(其他人在谷歌上搜索此内容),Fabio post 处理数据层的异常。引用他的一些代码;

public class MsSqlExceptionConverterExample : ISQLExceptionConverter
{
  public Exception Convert(AdoExceptionContextInfo exInfo)
  {
      var sqle = ADOExceptionHelper.ExtractDbException(exInfo.SqlException) as SqlException;
      if(sqle != null)
      {
          switch (sqle.Number)
          {
              case 547:
                  return new ConstraintViolationException(exInfo.Message,
                      sqle.InnerException, exInfo.Sql, null);
              case 208:
                  return new SQLGrammarException(exInfo.Message,
                      sqle.InnerException, exInfo.Sql);
              case 3960:
                  return new StaleObjectStateException(exInfo.EntityName, exInfo.EntityId);
          }
      }
      return SQLStateConverter.HandledNonSpecificException(exInfo.SqlException,
          exInfo.Message, exInfo.Sql);
  }
}
  • 547 是约束冲突的异常编号。
  • 208 是 SQL 中无效对象名称的异常编号。
  • 3960 是由于更新冲突而中止的快照隔离事务的异常编号。

因此,如果您遇到了像您所描述的那样的并发问题;请记住,它们将使您的 ISession 无效,并且您必须像上面那样处理它们。

您可能正在寻找的部分内容是 CQRS,其中您有单独的读取端和写入端。这可能会有所帮助:http://abdullin.com/cqrs/http://cqrsinfo.com

总结一下;您的问题可能与您处理交易的方式有关。另外,尝试运行 select log_wait_reuse_desc from sys.databases where name='MyDBName' 并查看它会提供什么。

Have a look at this thread:
http://n2cms.codeplex.com/Thread/View.aspx?ThreadId=85016

Basically what it says as a possible cause of this exception:

2010-02-17 21:01:41,204 1 WARN
NHibernate.Util.ADOExceptionReporter -
System.Data.SqlClient.SqlException:
The transaction log for database
'databasename' is full. To find out
why space in the log cannot be reused,
see the log_reuse_wait_desc column in
sys.databases

As the transaction log's size is proportional to the amount of work done during the transaction, perhaps you ought to look into putting your transactional boundaries across command handlers 'handling' of commands on the write-part of transactions. You would then, with a session#X, load the state you wish to mutate, mutate it and commit it, all as one unit of work in #X.

With regards to the read-side of things, you might then have another ISession#Y that reads data; this ISession could be used to batch reads within e.g. RepeatableRead or something similar with the Futures feature and could simply be reading from a cache (albiet it being a crutch indeed). Doing it this way might help you recover from "errors" that aren't; livelocks, deadlocks and victim transactions.

The problem with using a transaction per request is that your ISession acquires a lot of book keeping data while you are working, all of which is part of the transaction. Hence the database marks the datas (rols, cols, tables, etc) as partaking in the transaction, causing the wait-graph to span 'entities' (in the database-sense, not the DDD-sense), which are not actually part of the transactional boundary of the command your application took.

For the record (other people googling this), Fabio had a post dealing with dealing with exceptions from the data layer. Quoting some of his code;

public class MsSqlExceptionConverterExample : ISQLExceptionConverter
{
  public Exception Convert(AdoExceptionContextInfo exInfo)
  {
      var sqle = ADOExceptionHelper.ExtractDbException(exInfo.SqlException) as SqlException;
      if(sqle != null)
      {
          switch (sqle.Number)
          {
              case 547:
                  return new ConstraintViolationException(exInfo.Message,
                      sqle.InnerException, exInfo.Sql, null);
              case 208:
                  return new SQLGrammarException(exInfo.Message,
                      sqle.InnerException, exInfo.Sql);
              case 3960:
                  return new StaleObjectStateException(exInfo.EntityName, exInfo.EntityId);
          }
      }
      return SQLStateConverter.HandledNonSpecificException(exInfo.SqlException,
          exInfo.Message, exInfo.Sql);
  }
}
  • 547 is the exception number for constraint conflict.
  • 208 is the exception number for an invalid object name in the SQL.
  • 3960 is the exception number for Snapshot isolation transaction aborted due to update conflict.

So if you are running into concurrency issues like what you describe; remember that they will invalidate your ISession and that you'd have to handle them like the above.

Part of what you might be looking for is CQRS, where you have separate read and write-sides. This might help: http://abdullin.com/cqrs/, http://cqrsinfo.com.

So to summarize; your problems might be related to the way your handle your transactions. Also, try running select log_wait_reuse_desc from sys.databases where name='MyDBName' and see what it gives you.

翻了热茶 2024-09-19 05:46:18

这个线程有一个解释:
http://groups.google.com/group/nhusers/browse_thread/thread/7f5fb68a00829d13

简而言之,数据库可能会因为某些错误而自行回滚事务,这样当你稍后尝试回滚事务时,它已经回滚并处于僵尸状态。这往往会隐藏回滚的实际原因,因为您看到的只是 TransactionException,而不是首先实际触发回滚的异常。

我认为除了记录它并尝试找出导致潜在错误的原因之外,您对此无能为力。

This thread has an explanation:
http://groups.google.com/group/nhusers/browse_thread/thread/7f5fb68a00829d13

In short, the database probably rolls back the transaction by itself due to some error, so that when you try to rollback the transaction later it is already rolled back and in a zombie state. This tends to hide the actual reason for the rollback since all you see is a TransactionException instead of the exception that actually triggered the rollback in the first place.

I don't think there is much you can do about it beyond logging it and trying to figure out what is causing the underlying error.

薄荷梦 2024-09-19 05:46:18

我知道这篇文章是不久前发布的,假设您已修复它,但似乎您与 NHibernate ISession 存在线程共享问题,该问题不是线程安全的。基本上,一个线程正在启​​动一个事务,另一个线程正在尝试关闭它,从而导致各种混乱。

I know this post was a while back and assume you fixed it, but seems like you have thread sharing issues with the NHibernate ISession which is not threadsafe. Basically 1 thread is starting a transaction and another is attempting to close it causing all sorts of chaos.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文