抑制 NServicebus 事务将错误写入数据库

发布于 2024-11-30 10:52:25 字数 603 浏览 1 评论 0原文

我正在使用 NServiceBus 来处理一些计算消息。我有一个新要求,通过将计算错误写入相同的数据库来处理它们。我使用 NHibernate 作为我的 DAL,它会自动加入 NServiceBus 事务并在出现异常时提供回滚,效果非常好。但是,如果我将这个特定错误写入数据库,它也会回滚,这是一个问题。

我知道这会是一个问题,但我想我可以使用 TransactionScopeOption = Suppress 将调用包装在新事务中。但错误数据仍被回滚。我相信这是因为它使用的现有会话已经在 NServiceBus 事务中登记。

接下来,我尝试从抑制事务范围内的现有 SessionFactory 打开一个新会话。但是,使用此新会话第一次调用数据库来检索或保存数据会阻塞,然后超时。

内部异常:System.Data.SqlClient.SqlException 消息=超时已过。 >操作完成之前超时时间已过,或者服务器没有响应。

最后,我尝试创建一个新的 SessionFactory,使用它在抑制事务范围内打开一个新会话。然而它再次阻塞并超时。

我觉得我在这里遗漏了一些明显的东西,并且非常感谢有关这个可能常见任务的任何建议。

I'm using NServiceBus to handle some calculation messages. I have a new requirement to handle calculation errors by writing them the same database. I'm using NHibernate as my DAL which auto enlists to the NServiceBus transaction and provides rollback in case of exceptions, which is working really well. However if I write this particular error to the database, it is also rolled back which is a problem.

I knew this would be a problem, but I thought I could just wrap the call in a new transaction with the TransactionScopeOption = Suppress. However the error data is still rolled back. I believe that's because it was using the existing session with has already enlisted in the NServiceBus transaction.

Next I tried opening a new session from the existing SessionFactory within the suppression transaction scope. However the first call to the database to retrieve or save data using this new session blocks and then times out.

InnerException: System.Data.SqlClient.SqlException
Message=Timeout expired. The timeout period elapsed prior to completion of the >operation or the server is not responding.

Finally I tried creating a new SessionFactory using it to open a new session within the suppression transaction scope. However again it blocks and times out.

I feel like I'm missing something obvious here, and would greatly appreciate any suggestions on this probably common task.

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

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

发布评论

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

评论(2

柠檬 2024-12-07 10:52:25

正如 Adam 在评论中建议的那样,在大多数情况下,最好让整个消息处理失败,让内置的重试机制有机会正确处理,并最终进入错误队列。然后另一个进程可以监视错误队列并执行任何所需的通知,包括记录到数据库。

然而,在某些用例中,整个消息并不是失败,即总的来说,它“成功”(无论业务相关的定义是什么),但有一些小部分出错了。例如,在财务计算中,处理“成功”,但数据的某些人为因素“错误”。在这种情况下,我建议捕获该异常并发送一条新消息,该消息在由另一个端点处理时会将信息记录到您的数据库中。

我可以看到另一种情况,您希望整个消息失败,但您希望以某种方式记录它已尝试的事实。这可能最接近您所描述的内容。在本例中,使用 TransactionScopeOption = Suppress 创建一个新的 TransactionScope,然后(再次)在该范围内发送一条新消息。无论您的完整消息事务是否回滚,该消息都会被发送。

您的事务正在回滚是正确的,因为 NHibernate 会话是在事务生效时打开的。尝试在被抑制的事务内打开新会话可能会导致锁定问题。这就是为什么在大多数情况下,异步发送新消息是这些情况下解决方案的一部分,但具体操作方式取决于您的特定业务需求。

As Adam suggests in the comments, in most cases it is preferred to let the entire message fail processing, giving the built-in Retry mechanism a chance to get it right, and eventually going to the error queue. Then another process can monitor the error queue and do any required notification, including logging to a database.

However, there are some use cases where the entire message is not a failure, i.e. on the whole, it "succeeds" (whatever the business-dependent definition of that is) but there is some small part that is in error. For example, a financial calculation in which the processing "succeeds" but some human element of the data is "in error". In this case I would suggest catching that exception and sending a new message which, when processed by another endpoint, will log the information to your database.

I could see another case where you want the entire message to fail, but you want the fact that it was attempted noted somehow. This may be closest to what you are describing. In this case, create a new TransactionScope with TransactionScopeOption = Suppress, and then (again) send a new message inside that scope. That message will be sent whether or not your full message transaction rolls back.

You are correct that your transaction is rolling back because the NHibernate session is opened while the transaction is in force. Trying to open a new session inside the suppressed transaction can cause a problem with locking. That's why, most of the time, sending a new message asynchronously is part of the solution in these cases, but how you do it is dependent upon your specific business requirements.

余厌 2024-12-07 10:52:25

我知道我迟到了,但作为替代建议,您可以简单地提出另一个单独的日志消息,NSB 独立处理该消息,例如:

public void Handle(DebitAccountMessage message)
{
    var account = this.dbcontext.GetById(message.Id);
    if (account.Balance <= 0)
    {
        // log request - new handler
        this.Bus.Send(new DebitAccountLogMessage
            {
                originalMessage = message,
                account = account,
                timeStamp = DateTime.UtcNow
            });

        // throw error - NSB will handle
        throw new DebitException("Not enough funds");
    }
}

public void Handle(DebitAccountLogMessage message)
{
    var messageString = message.originalMessage.Dump();
    var accountString = message.account.Dump(DumpOptions.SuppressSecurityTokens);
    this.Logger.Log(message.UniqueId, string.Format("{0}, {1}", messageString, accountString);
}

I know I'm late to the party, but as an alternative suggestion, you coudl simply raise another separate log message, which NSB handles independently, for example:

public void Handle(DebitAccountMessage message)
{
    var account = this.dbcontext.GetById(message.Id);
    if (account.Balance <= 0)
    {
        // log request - new handler
        this.Bus.Send(new DebitAccountLogMessage
            {
                originalMessage = message,
                account = account,
                timeStamp = DateTime.UtcNow
            });

        // throw error - NSB will handle
        throw new DebitException("Not enough funds");
    }
}

public void Handle(DebitAccountLogMessage message)
{
    var messageString = message.originalMessage.Dump();
    var accountString = message.account.Dump(DumpOptions.SuppressSecurityTokens);
    this.Logger.Log(message.UniqueId, string.Format("{0}, {1}", messageString, accountString);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文