Search code examples
c#nservicebusbussaganservicebus-sagas

NServiceBus Saga handle a message-type multiple times gives concurrency exception


We have a NServiceBus implementation that handles multiple message-types:

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

MessageData is something like this:

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; }
}

The CreateMessage has a MessagePart that is added to MessageParts in its handler. The ConfirmMessage updates the status of a particular MessagePart.

This is the scenario:

1) A first CreateMessage is received. This creates the Saga in Raven and adds a MessagePart (with status "1") to MessageParts.

2) A first ConfirmMessage is received. This updates the Status of the first added MessagePart in the Saga to "1 2". This is vissible in the browser when going to the document in RavenDB.

3) A second CreateMessage is received. This adds a second MessagePart to MessageParts. When looking to the Data, the status of the first MessagePart is still "1" and not "1 2" and this throws a concurrency-exception (the ActualETag does not equals the 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

It looks like the Saga-data is cached per message-type. Is this so? Is there a solution?

NOTE:

We are using multiple IAmStartedByMessages but when the ConfirmMessage is before the CreateMessage, then this message is added to a queue until the CreateMessage is handled.


Solution

  • After a few tests I can say that there is not a real problem. I think you can't solve the concurrency-exceptions (this exception is logged) but when this exception occurs, the message is just send back to the queue for a retry.

    I was afraid that the logic from the Handle would be executed twice but I guess because I've transactions enabled, this is not the case. For example: one of the actions is to send the message to the another bus to persist the event (perister-queue). This action is called before the exceptions occurs (after the Handle, Data is saved in the Saga and the exceptions is thrown) but there is no message added to the persister-queue. After the retry of the message that succeeds (this updates Data so there isn't a concurrency-exception anymore), the persister-message is added to the persister-queue.

    Conclusion: not a real problem, just an extra line in the log-file.