NServiceBus Saga多次处理一个消息类型会导致并发异常

本文关键字:类型 消息 异常 并发 一个 Saga 处理 NServiceBus | 更新日期: 2023-09-27 18:29:24

我们有一个NServiceBus实现,可以处理多种消息类型:

public class StateCoordinator : Saga<MessageData>, 
                                IAmStartedByMessages<CreateMessage>, 
                                IAmStartedByMessages<ConfirmMessage>

MessageData是这样的:

public class FlowData : IContainSagaData
{
    [Unique]
    public Guid MappingId { get; set; }
    public Guid Id { get; set; }
    public string OriginalMessageId { get; set; }
    public string Originator { get; set; }
    public List<MessagePart> MessageParts { get; set; }
}
public MessagePart
{
    public int Id { get; set; }
    public string Status { get; set; }
}

CreateMessage具有一个MessagePart,该MessagePart已添加到其处理程序中的MessageParts中。ConfirmMessage更新特定MessagePart的状态。

这就是场景:

1) 接收到第一个CreateMessage。这将在Raven中创建Saga,并向MessagePart添加一个MessagePart(状态为"1")。

2) 接收到第一个ConfirmMessage。这会将Saga中第一个添加的MessagePart的状态更新为"12"。当转到RavenDB中的文档时,这在浏览器中是可见的。

3) 接收到第二个CreateMessage。这将向MessageParts添加第二个MessagePart。当查看数据时,第一个MessagePart的状态仍然是"1",而不是"12",这会引发并发异常(ActualETag不等于ExpectedETag):

A first chance exception of type 'Raven.Abstractions.Exceptions.ConcurrencyException' occurred in Raven.Client.Lightweight
Additional information: PUT attempted on document 'flow/79a7ee20-f090-4648-9b62-a3da00d87c93' using a non current etag

看起来Saga数据是按消息类型缓存的。是这样吗?有解决方案吗?

注:

我们使用多个IAmStartedByMessages,但如果ConfirmMessage在CreateMessage之前,则会将此消息添加到队列中,直到处理CreateMessage为止。

NServiceBus Saga多次处理一个消息类型会导致并发异常

经过几次测试,我可以说没有真正的问题。我认为您无法解决并发异常(此异常已记录),但当此异常发生时,消息只会发送回队列进行重试。

我担心Handle中的逻辑会被执行两次,但我想因为我启用了事务,所以情况并非如此。例如:其中一个操作是将消息发送到另一条总线以保持事件(潜望者队列)。此操作在异常发生之前调用(在Handle之后,数据保存在Saga中并抛出异常),但没有消息添加到持久队列中。在重试成功的消息之后(这会更新Data,这样就不会再有并发异常了),持久消息会被添加到持久队列中。

结论:不是真正的问题,只是日志文件中多了一行。