1

My problem that Composite Event isn't not triggered when I got all the required events (in my case LinkContract, LinkCustomer). And there is a strange thing - when I send LinkContract event, value of ProcessingStatus became 1 and then when I got the second event LinkCustomer it changes to 0, but I thought it must be 3. Can someone explain why this happens and my composite event isn't trigger?

I've registered composite event in my MassTransitStateMachine:

using MassTransit;
using Sandbox.WebApi.Models.StateEvents;
using Serilog;

namespace Sandbox.WebApi.StateMachines;

public class BillingStateMachine : MassTransitStateMachine<BillingState>
{
    public BillingStateMachine()
    {
        InstanceState(x => x.CurrentState);
        
        Event(() => LinkCustomer, e => e
            .CorrelateById(x => x.Message.CustomerId));
        Event(() => LinkContract, e => e
            .CorrelateById(x => x.Message.CustomerId));

        Initially(
            When(LinkCustomer)
                .Then(HandleLinkCustomer)
                .TransitionTo(Processing),
            When(LinkContract)
                .Then(HandleLinkContract)
                .TransitionTo(Processing));
        
        During(Processing,
            When(LinkContract)
                .Then(HandleLinkContract),
            When(LinkCustomer)
                .Then(HandleLinkCustomer));

        CompositeEvent(() => PaymentSucceeded, 
            x => x.ProcessingStatus,
            CompositeEventOptions.IncludeInitial,
            LinkContract, LinkCustomer);
        
        During(Processing,
            When(PaymentSucceeded)
                .Then(HandlePaymentSucceeded)
                .TransitionTo(Paid));
    }
    
    public State Processing { get; }
    public State Paid { get; }
    
    public Event PaymentSucceeded { get; }
    public Event<ContractCreated> LinkContract { get; }
    public Event<CustomerCreated> LinkCustomer { get; }

    private static void HandleLinkCustomer(BehaviorContext<BillingState, CustomerCreated> context)
    {
        context.Saga.CustomerId = context.Message.CustomerId;
        Log.Information("Customer was linked: {CustomerId}", context.Saga.CustomerId);
    }

    private static void HandleLinkContract(BehaviorContext<BillingState, ContractCreated> context)
    {
        context.Saga.ContractId = context.Message.ContractId;
        context.Saga.CustomerId = context.Message.CustomerId;
        Log.Information("Contract was linked: {ContractId}", context.Saga.ContractId);
    }

    private static void HandlePaymentSucceeded(BehaviorContext<BillingState> context)
    {
        Log.Information("Billing was paid: {ContractId} & {CustomerId}",
            context.Saga.ContractId, context.Saga.CustomerId);
    }
}

and the following state:

using MassTransit;

namespace Sandbox.WebApi.StateMachines;

public class BillingState : SagaStateMachineInstance, ISagaVersion
{
    public Guid CorrelationId { get; set; }
    public string CurrentState { get; set; }
    public CompositeEventStatus ProcessingStatus { get; set; }
    public int Version { get; set; }
    
    public Guid CustomerId { get; set; }
    public Guid ContractId { get; set; }
}

There is DI configuration:

services.AddMassTransit(opts =>
{
    opts.SetEndpointNameFormatter(KebabCaseEndpointNameFormatter.Instance);

    opts.AddConsumers(assembly);
    opts.AddSagaStateMachine<BillingStateMachine, BillingState>()
        .RedisRepository(x => x
            .DatabaseConfiguration("<connection_string>"));

    opts.UsingRabbitMq((ctx, cfg) =>
    {
        cfg.ConfigureEndpoints(ctx);
        cfg.AutoStart = true;
        cfg.Host("localhost", 5672, "/", r =>
        {
            r.Username("guest");
            r.Password("guest");
        });
    });
});

Also if this will help, there are logs:

[13:04:29 INF] Bus started: rabbitmq://localhost/
[13:05:16 DBG] Declare exchange: name: Sandbox.WebApi.Models.StateEvents:ContractCreated, type: fanout, durable
[13:05:16 DBG] SEND rabbitmq://localhost/Sandbox.WebApi.Models.StateEvents:ContractCreated eb480000-25c5-9e68-3b62-08da536d8a2f Sandbox.WebApi.Models.StateEvents.ContractCreated
[13:05:16 INF] HTTP POST /api/User/contract responded 204 in 322.4377 ms
[13:05:16 DBG] SAGA:Sandbox.WebApi.StateMachines.BillingState:00000000-507f-1f77-bcf8-6cd799439011 Created Sandbox.WebApi.Models.StateEvents.ContractCreated
[13:05:16 DBG] SAGA:Sandbox.WebApi.StateMachines.BillingState:00000000-507f-1f77-bcf8-6cd799439011 Added Sandbox.WebApi.Models.StateEvents.ContractCreated
[13:05:16 INF] Contract was linked: 00000000-507f-1f77-bcf8-6cd799439011
[13:05:16 DBG] RECEIVE rabbitmq://localhost/billing-state eb480000-25c5-9e68-3b62-08da536d8a2f Sandbox.WebApi.Models.StateEvents.ContractCreated Sandbox.WebApi.StateMachines.BillingState(00:00:00.4622311)
[13:06:29 DBG] Declare exchange: name: Sandbox.WebApi.Models.StateEvents:CustomerCreated, type: fanout, durable
[13:06:29 DBG] SEND rabbitmq://localhost/Sandbox.WebApi.Models.StateEvents:CustomerCreated eb480000-25c5-9e68-751a-08da536db5cb Sandbox.WebApi.Models.StateEvents.CustomerCreated
[13:06:29 INF] HTTP POST /api/User/customer responded 204 in 360.3700 ms
[13:06:29 DBG] SAGA:Sandbox.WebApi.StateMachines.BillingState:00000000-507f-1f77-bcf8-6cd799439011 Used Sandbox.WebApi.Models.StateEvents.CustomerCreated
[13:06:29 INF] Customer was linked: 00000000-507f-1f77-bcf8-6cd799439011
[13:06:29 DBG] RECEIVE rabbitmq://localhost/billing-state eb480000-25c5-9e68-751a-08da536db5cb Sandbox.WebApi.Models.StateEvents.CustomerCreated Sandbox.WebApi.StateMachines.BillingState(00:00:00.3579677)

pilkha
  • 31
  • 4

1 Answers1

0

Just a guess, is it some strange serialization issue with Redis? Change the composite event status to an int:

public int ProcessingStatus { get; set; }

Also, make sure you have retry configured on the saga so that any concurrency issues are retried.

Chris Patterson
  • 28,659
  • 3
  • 47
  • 59
  • I've tried both `int` and `CompositeEventStatus` – pilkha Jun 21 '22 at 12:17
  • When ProcessingStatus is int, after received first event `LinkContract` I got ProcessingStatus = 1, then after I received the second one `LinkCustomer` ProcessingStatus still 1 – pilkha Jun 21 '22 at 12:24