1

I would like to use unique natural key instead of build in Guid CorrelationId in Masstransit Sagas. However it seems not really work. If I send twice Initial event with same key value, two Sagas created in repository - expected is single instance. Same scenario with using CorrelationId produces one Saga instance in repository.

Any ideas, why second Saga instance created with same key, ignoring correlation statement?

Here is short example with unit test:

[TestFixture]
public class SagaTests
{
    [Test]
    public void TestSagaInitialization()
    {
        var testSaga = new TestSaga();
        var testSagaRepository = new InMemorySagaRepository<TestSagaState>();
        var busControl = Bus.Factory.CreateUsingInMemory(
            cfg =>
            {
                cfg.ReceiveEndpoint("test_queue",
                    e =>
                    {
                        e.StateMachineSaga(testSaga, testSagaRepository);
                    });
            });
        busControl.Start();

        busControl.Publish(new TestSagaInitEvent() { UniqueNaturalKey = 1 }).Wait();
        busControl.Publish(new TestSagaInitEvent() { UniqueNaturalKey = 2 }).Wait();
        // Message with same natural key published again
        busControl.Publish(new TestSagaInitEvent() { UniqueNaturalKey = 1 }).Wait();

        // Wait till all messges consumed
        var till = DateTime.Now.AddSeconds(1);
        while (DateTime.Now < till) { System.Threading.Thread.Sleep(50); }

        busControl.Stop();

        var sagaInstances = testSagaRepository.Where(x => x.UniqueNaturalKey == 1).Result.ToList();
        Assert.AreEqual(1, sagaInstances.Count, "Dublicate initial events with same Natural Key value must not create new Saga instance");
    }
}

public class TestSagaState : Automatonymous.SagaStateMachineInstance
{
    public Guid CorrelationId { get; set; }

    public int CurrentState { get; set; }

    public long UniqueNaturalKey { get; set; }
}

public class TestSaga : MassTransitStateMachine<TestSagaState>
{
    public TestSaga()
    {
        InstanceState(x => x.CurrentState);

        Event(() => SagaInitiated, x => x.CorrelateById(state => state.UniqueNaturalKey, context => context.Message.UniqueNaturalKey)
            .SelectId(context => Guid.NewGuid()));

        Initially(
            When(SagaInitiated)
                .Then(context =>
                {
                    context.Instance.UniqueNaturalKey = context.Data.UniqueNaturalKey;
                })
                .TransitionTo(Initiated)
            );
    }

    public State Initiated { get; private set; }
    public Event<TestSagaInitEvent> SagaInitiated { get; private set; }
}

public class TestSagaInitEvent
{
    public long UniqueNaturalKey { get; set; }
}
Andris
  • 1,262
  • 1
  • 15
  • 24
  • You wait for messages to be published but not consumed. This looks like race condition to me. Use proper persistence. – Alexey Zimarev Jul 12 '17 at 17:21
  • As a result of test execution, repository has 3 saga instances, where 2 saga instances with UniqueNaturalKey = 1. This proves, that all 3 messages consumed by Saga. – Andris Jul 12 '17 at 17:59

0 Answers0