Search code examples
c#.netazureservicebusmasstransitsaga

.NET Mass Transit - MassTransit.NotAcceptedStateMachineException


Hi I have a problem in state machine and I really don't know what to do anymore. The thing is that the state machine works as it should, but if an exception occurs in the consumer from the DataRunningFault message, it correctly switches the state to ClearDataState and waits to see if DataCalculated or DataReport is running (the error in the consumer can occur after the DataCalculated/DataReport message is sent). Thus, I wait to see if it runs and if it does, I delete the data. More or less it works, but I get this exception.

public class DataState : SagaStateMachineInstance
{
    public Guid CorrelationId { get; set; }

    public int CurrentState { get; set; }

    public int ReadyEventStatus { get; set; }

    public Guid RequestId { get; set; }
}

public class DataStateMachine : MassTransitStateMachine<DataState>
{
    public Event<DataRequested>? DatakRequested { get; }
    public Event<Fault<DataRequested>>? DataRequestedFault { get; }

    public Event<DataRunning>? DataRunning { get; }
    public Event<Fault<DataRunning>>? DataRunningFault { get; }

    public Event<DataCalculated> DataCalculated { get; }
    public Event<Fault<DataCalculated>>? DataCalculatedFault { get; }
    public Event<DataReport> DataReport { get; }
    public Event<Fault<DataReport>>? DataReportFault { get; }

    public MassTransit.Event? Finished { get; }
    public State? ClearDataState { get; }


    public DataStateMachine()
    {
        InstanceState(x => x.CurrentState);

        Event(() => DataRequested, x => x.CorrelateById(context => context.Message.RequestId));
        Event(() => DataRequestFault, x => x.CorrelateById(context => context.Message.Message.RequestId));

        Event(() => DataRunning, x => x.CorrelateById(context => context.Message.RequestId));
        Event(() => DataRunningFault, x => x.CorrelateById(context => context.Message.Message.RequestId));

        Event(() => DataCalculated, x => x.CorrelateById(context => context.Message.RequestId));
        Event(() => DataCalculatedFault, x => x.CorrelateById(context => context.Message.Message.RequestId));

        Event(() => DataReport, x => x.CorrelateById(context => context.Message.RequestId));
        Event(() => DataReportFault, x => x.CorrelateById(context => context.Message.Message.RequestId));

        Initially(
             When(DataRequested)
                .Then(x =>
                {
                    x.Saga.Entities = x.Message.Entities;
                    x.Saga.BatchDate = x.Message.BatchDate;
                    x.Saga.RequestId = x.Message.RequestId;
                }),
             When(DataRequestFault)
               .Publish(x => Fault(x.Saga))
               .TransitionTo(ClearDataState),

             When(DataRunning),
             When(DataRunningFault)
               .Publish(x => Fault(x.Saga))
               .TransitionTo(ClearDataState),

             When(DataCalculated),
             When(DataCalculatedFault)
               .Publish(x => Fault(x.Saga))
               .Finalize(),

             When(DataReport),
             When(DataReportFault)
               .Publish(x => Fault(x.Saga))
               .Finalize()
        );


        During(ClearDataState,
            When(DataCalculated)
                .Publish(x => new DeleteCalculateData() { RequestId = x.Message.RequestId })
                .Finalize(),
            When(DataReport)
                .Publish(x => new DeleteCalculateData() { RequestId = x.Message.RequestId })
                .Finalize()
        );

        CompositeEvent(() => Finished,
            x => x.ReadyEventStatus,
            CompositeEventOptions.IncludeInitial,
            DataRequested,
            DataRunning,
            DataCalculated
        );

        During(Initial,
            When(Finished)
            .Publish(context =>
            {
                return new DataFinished() { RequestId = context.Saga.RequestId };
            })
            .Finalize()
        );

        SetCompletedWhenFinalized();
    }

    private DataFinishedFault Fault(RunDataState runDataState)
    {
        return new DataFinishedFault() { RequestId = runDataState.RequestId };
    }
}

Exception:

MassTransit: Error: R-FAULT sb://testbusfortestingstandard.servicebus.windows.net/messaging-run-data-state bca30000-4395-e04f-ceb8-08dbe1bde727 SharedContracts.Messages.ReportCalculateFinished Messaging.StateMachines.DataState(00:00:00.8759128)

MassTransit.NotAcceptedStateMachineException: Messaging.StateMachines.DataState(63331cb1-6165-4260-9577-66be080a3d9e) Saga exception on receipt of SharedContracts.Messages.DataReport: Not accepted in state Final
 ---> MassTransit.UnhandledEventException: The DataReport event is not handled during the Final state for the DataStateMachine state machine
   at MassTransit.MassTransitStateMachine`1.DefaultUnhandledEventCallback(UnhandledEventContext`1 context) in /_/src/MassTransit/SagaStateMachine/MassTransitStateMachine.cs:line 219
   at MassTransit.MassTransitStateMachine`1.UnhandledEvent(BehaviorContext`1 context, State state) in /_/src/MassTransit/SagaStateMachine/MassTransitStateMachine.cs:line 1260
   at MassTransit.MassTransitStateMachine`1.<.ctor>b__13_2(BehaviorContext`1 context, State state) in /_/src/MassTransit/SagaStateMachine/MassTransitStateMachine.cs:line 52
   at MassTransit.SagaStateMachine.StateMachineState`1.MassTransit.State<TSaga>.Raise[T](BehaviorContext`2 context) in /_/src/MassTransit/SagaStateMachine/SagaStateMachine/StateMachineState.cs:line 162
   at MassTransit.MassTransitStateMachine`1.MassTransit.StateMachine<TInstance>.RaiseEvent[T](BehaviorContext`2 context) in /_/src/MassTransit/SagaStateMachine/MassTransitStateMachine.cs:line 132
   at MassTransit.Middleware.StateMachineSagaMessageFilter`2.Send(SagaConsumeContext`2 context, IPipe`1 next) in /_/src/MassTransit/Middleware/StateMachineSagaMessageFilter.cs:line 66
   --- End of inner exception stack trace ---
   at MassTransit.Middleware.StateMachineSagaMessageFilter`2.Send(SagaConsumeContext`2 context, IPipe`1 next) in /_/src/MassTransit/Middleware/StateMachineSagaMessageFilter.cs:line 81
   at MassTransit.Middleware.StateMachineSagaMessageFilter`2.Send(SagaConsumeContext`2 context, IPipe`1 next) in /_/src/MassTransit/Middleware/StateMachineSagaMessageFilter.cs:line 104
   at MassTransit.Middleware.SendSagaPipe`2.Send(SagaRepositoryContext`2 context) in /_/src/MassTransit/Middleware/SendSagaPipe.cs:line 43
   at MassTransit.Middleware.SendSagaPipe`2.Send(SagaRepositoryContext`2 context) in /_/src/MassTransit/Middleware/SendSagaPipe.cs:line 67
   at MassTransit.Saga.InMemorySagaRepositoryContextFactory`1.Send[T](ConsumeContext`1 context, IPipe`1 next) in /_/src/MassTransit/Sagas/Saga/InMemoryRepository/InMemorySagaRepositoryContextFactory.cs:line 40
   at MassTransit.DependencyInjection.DependencyInjectionSagaRepositoryContextFactory`1.Send[T](ConsumeContext`1 context, Func`3 send) in /_/src/MassTransit/DependencyInjection/DependencyInjection/DependencyInjectionSagaRepositoryContextFactory.cs:line 101
   at MassTransit.DependencyInjection.DependencyInjectionSagaRepositoryContextFactory`1.Send[T](ConsumeContext`1 context, Func`3 send) in /_/src/MassTransit/DependencyInjection/DependencyInjection/DependencyInjectionSagaRepositoryContextFactory.cs:line 110
   at MassTransit.Middleware.CorrelatedSagaFilter`2.Send(ConsumeContext`1 context, IPipe`1 next) in /_/src/MassTransit/Middleware/CorrelatedSagaFilter.cs:line 45

I tried looking for a solution on the internet and moving events to other states or adding the state to the instancestate, but nothing helped.


Solution

  • The Final state is meant to be a terminal state, and once a state machine transitions to the Final state, it should not accept any more events.

    • Here you are trying to handle the DataReport event in the Final state, which is not allowed.
    During(ClearDataState,
        When(DataCalculated)
            .Publish(x => new DeleteCalculateData() { RequestId = x.Message.RequestId })
            .Finalize(),
        // Move the logic to handle DataReport to a state before Final state
        When(DataReport)
            .Publish(x => new DeleteCalculateData() { RequestId = x.Message.RequestId })
    );
    
    CompositeEvent(() => Finished,
        x => x.ReadyEventStatus,
        CompositeEventOptions.IncludeInitial,
        DataRequested,
        DataRunning,
        DataCalculated,
        // Add DataReport to the CompositeEvent
        DataReport
    );
    
    During(Initial,
        When(Finished)
            .Publish(context => new DataFinished() { RequestId = context.Saga.RequestId })
            .Finalize()
    );
    
    • If the purpose of handling DataReport in the ClearDataState is to perform some finalization logic, you can move that logic to a different state that comes before the Final state.If the purpose of handling DataReport in the ClearDataState is to perform some finalization logic, you can move that logic to a different state that comes before the Final state.

    enter image description here

    1. In the ClearDataState state, it handles the DataCalculated and DataReport events, publishing the corresponding messages and finalizing the state.
    2. If the Finished event is received during the Initial state, it publishes a DataFinished message and finalizes the state.