使用预取>运行的MassTransit传奇1

时间:2016-08-02 09:04:11

标签: c# masstransit saga

我有一个MassTransit saga状态机(派生自Automatonymous.MassTransitStateMachine),我正在尝试解决只有当我将端点配置prefetchCount设置为大于1的值时才会出现的问题。

问题是'StartupCompletedEvent'已发布,然后在saga状态持久保存到数据库之前立即处理。

状态机配置如下:

State(() => Initialising);
State(() => StartingUp);
State(() => GeneratingFiles);

Event(() => Requested, x => x.CorrelateById(ctx => ctx.Message.CorrelationId).SelectId(ctx => ctx.Message.CorrelationId));
Event(() => StartupCompleted, x => x.CorrelateById(ctx => ctx.Message.CorrelationId));
Event(() => InitialisationCompleted, x => x.CorrelateById(ctx => ctx.Message.CorrelationId));
Event(() => FileGenerationCompleted, x => x.CorrelateById(ctx => ctx.Message.CorrelationId));


Initially(
    When(Requested)
        .ThenAsync(async ctx => 
        {
          Console.WriteLine("Starting up...");
          await ctx.Publish(new StartupCompletedEvent() { CorrelationId = ctx.Instance.CorrelationId }));
          Console.WriteLine("Done starting up...");
        }
        .TransitionTo(StartingUp)
);


During(StartingUp,
    When(StartupCompleted)
        .ThenAsync(InitialiseSagaInstanceData)
        .TransitionTo(Initialising)
);

// snip...!

当我的传奇收到请求事件时会发生什么:

  1. 初始化块的ThenAsync处理程序被命中。此时,没有任何传奇数据持久存储到repo(正如预期的那样)。
  2. StartupCompletedEvent发布到总线。这里没有任何传奇数据存在于回购。
  3. 初始化声明的ThenAsync块完成。在此之后,传奇数据最终会持续存在。
  4. 没有其他事情发生。
  5. 此时,队列中没有消息,并且StartupCompletedEvent丢失。但是,数据库中有一个saga实例。

    我在启动时玩过,并确定其他线程之一(因为我的预取是> 1)已经拾取了事件,没有在数据库中找到任何带有correlationId的传奇,并丢弃了该事件。因此,在传奇有机会被保留之前,该事件正在发布和处理。

    如果我将以下内容添加到初始处理程序:

    When(StartupCompleted)
        .Then(ctx => Console.WriteLine("Got the startup completed event when there is no saga instance"))
    

    然后我执行Console.WriteLine。我对此的理解是事件已被接收,但路由到初始处理程序,因为correlationId不存在传奇。如果我在这一点上设置一个断点并检查传奇回购,那么还没有传奇。

    值得一提的是其他几点:

    1. 我的saga repo上下文设置为使用IsolationLevel.Serializable
    2. 我正在使用EntityFrameworkSagaRepository
    3. 当预取计数设置为1
    4. 时,一切都按预期工作
    5. 我正在使用Ninject进行DI,而我的SagaRepository是Thread scoped,所以我想预取计数允许的每个处理程序都有自己的saga存储库副本
    6. 如果我在一个单独的线程中发布StartupCompletedEvent并在其之前休眠1000ms,那么事情就可以正常工作了。我认为这是因为saga repo已经完成了对saga状态的持久化,因此当事件最终发布并由处理程序拾取时,可以正确地从repo中检索saga状态。
    7. 如果我遗漏了任何东西,请告诉我。我试图提供我认为值得的所有东西,而不会让这个问题太长......

1 个答案:

答案 0 :(得分:2)

我也有这个问题,我想发布Chris的评论作为答案,以便人们可以找到它。

解决方案是启用发件箱,以便保留消息,直到保留saga。

c.ReceiveEndpoint("queue", e =>
{
    e.UseInMemoryOutbox();
    // other endpoint configuration here
}