【问题标题】:Non-simultaneous commits in a distributed transaction involving EntityFramework/SQL Server and NServiceBus/MSMQ涉及 EntityFramework/SQL Server 和 NServiceBus/MSMQ 的分布式事务中的非同时提交
【发布时间】:2018-04-12 12:57:13
【问题描述】:

有一个 .NET 4.7 WebAPI 应用程序使用实体框架与 SQL Server 一起工作,并通过 MSMQ 传输托管 NServiceBus 端点。

可以通过控制器操作来描述简化的工作流程:

[HttpPost]
public async Task<IHttpActionResult> SendDebugCommand()
{
    var sample = new Sample
                 {
                     State = SampleState.Initial,
                 };
    _dataContext.Set<Sample>().Add(sample);
    await _dataContext.SaveChangesAsync();

    sample.State = SampleState.Queueing;

    var options = new TransactionOptions
                  {
                      IsolationLevel = IsolationLevel.ReadCommitted,
                  };
    using (var scope = new TransactionScope(TransactionScopeOption.Required, options, TransactionScopeAsyncFlowOption.Enabled))
    {
        await _dataContext.SaveChangesAsync();    
        await _messageSession.Send(new DebugCommand {SampleId = sample.Id});
        scope.Complete();
    }

    _logger.OnCreated(sample);

    return Ok();
}

还有DebugCommand 处理程序,即发送到同一个 NServiceBus 端点:

public async Task Handle(DebugCommand message, IMessageHandlerContext context)
{
    var sample = await _dataContext.Set<Sample>().FindAsync(message.SampleId);

    if (sample == null)
    {
        _logger.OnNotFound(message.SampleId);
        return;
    }

    if (sample.State != SampleState.Queueing)
    {
        _logger.OnUnexpectedState(sample, SampleState.Queueing);
        return;
    }

    // Some work being done

    sample.State = SampleState.Processed;
    await _dataContext.SaveChangesAsync();

    _logger.OnHandled(sample);
}

有时,消息处理程序从数据库中检索Sample,其状态仍为Initial,而不是预期的Queueing。这意味着在控制器操作中启动的分布式事务尚未完全完成。日志文件中的时间戳也证实了这一点。

“有时”很少发生,在较重的负载和网络延迟可能会受到影响。本地数据库无法重现问题,但远程数据库可以轻松重现。

我检查了 DTC 配置。我确实检查了分布式事务的升级。此外,如果未调用 scope.Complete(),则不会发生 DB 更新,也不会发生消息发送。

当事务范围完成并处置后,直觉上我希望 DB 和 MSMQ 在执行一条进一步的指令之前都已解决。

我找不到问题的明确答案:

  • 这是 DTC 的工作方式吗?交易双方都进行提交,而完成没有报告给协调者,这是正常的吗?
  • 如果是,是否意味着我应该通过改变程序逻辑来克服此类事件?
  • 我是否以某种方式滥用事务?什么是正确的方法?

【问题讨论】:

标签: c# sql-server nservicebus msmq msdtc


【解决方案1】:

除了 Evk 在Distributed transaction with MSMQ and SQL Server but sometimes getting dirty reads 中提到的 cmets,这里还有一段来自particular documentation page about transactions 的摘录:

排队系统和持久存储之间的分布式事务保证原子提交,但只保证最终一致性。

两个附加说明:

  • NServiceBus 默认使用IsolationLevel.ReadCommitted 来处理用于消费消息的事务。 This can be configured 虽然我不确定将其设置为对消费者进行序列化是否真的可以解决这里的问题。
  • 一般而言,不建议在服务之间使用共享数据库,因为这会极大地增加耦合并为您在此处遇到的问题打开大门。尝试将相关数据作为消息的一部分传递,并将数据库作为一项服务的内部存储。特别是在使用 Web 服务器时,一种常见的模式是将所有相关数据添加到消息中并在向用户确认成功的同时触发它(因为消息不会丢失),而接收端点可以将数据存储到它的数据库中,如果必要的。要给出更具体的建议,这需要更多关于您的领域和用例的知识。我可以推荐particular discussion community 来讨论这样的设计/架构问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-10
    • 2017-04-06
    • 2015-07-27
    • 2015-10-04
    • 1970-01-01
    • 1970-01-01
    • 2012-08-14
    相关资源
    最近更新 更多