【问题标题】:Saga error nservicebus using raven db persistence使用 raven db 持久性的 Saga 错误 nservicebus
【发布时间】:2013-10-24 03:14:00
【问题描述】:

我有两条消息,clientChangeMes​​sage(负责创建客户端)和clientContractChangeMEssage(负责客户端的预订详情)。现在,在我的数据库中,只有拥有客户合同才能创建客户,反之亦然。在我的本地系统上,一切正常,即如果首先获取客户端更改消息,我将其存储在 saga 中并等待客户端合同消息,当该消息到达时,saga 会执行这两个消息。但是在我的测试机器上,当客户端更改消息出现时,它会存储在 saga 中,但是当客户端合同更改出现时,saga 找不到客户端更改 saga,因此会创建另一个 saga。我已经用我的测试人员尝试过的完全相同的消息进行了尝试,它可以在我的机器上运行,并且无法弄清楚可能出了什么问题。我正在使用乌鸦数据库持久性。 (对不起,我想不出为此粘贴任何代码)

ClientSagaState

public class ClientSagaState:IContainSagaData
    {
        #region NserviceBus
        public Guid Id { get; set; }
        public string Originator { get; set; }
        public string OriginalMessageId { get; set; }
        #endregion

        public Guid ClientRef { get; set; }

        public ClientMessage ClientChangeMessage { get; set; }

        public ClientContractChangeMessage ClientContractChange { get; set; }


    }


    public  class ClientSaga:Saga<ClientSagaState>,
           IAmStartedByMessages<ClientChangeMessage>,
           IAmStartedByMessages<ClientContractChangeMessage>
       {

        public override void ConfigureHowToFindSaga()
           {

               ConfigureMapping<ClientChangeMessage>(s => s.ClientRef, m => m.EntityRef);
               ConfigureMapping<ClientContractChangeMessage>(s => s.ClientRef, m => m.PrimaryEntityRef);
           }

        public void Handle(ClientChangeMessage message)
           {

               if (BusRefTranslator.GetLocalRef(EntityTranslationNames.ClientChange, message.EntityRef.Value) != null)
               {

                   GetHandler<ClientChangeMessage>().Handle(message);
                   CompleteTheSaga();
                   return;
               }
               HandleServiceUserChangeAndDependencies(message);
               //MarkAsComplete();
               CompleteTheSaga();
           }

          public void Handle(ClientContractChangeMessage message)
            {
                var state=this.Data;
                //Some handling logic
                //Check if client is not in database then store the state
                state.ClientContractChange=message;
                state.ClientRef =message.PrimaryEntityRef;
                //if client is in the data base then 
                MarkAsComplete();


       }

谢谢,

【问题讨论】:

  • saga 数据和 saga 映射的代码至少会有帮助。

标签: nservicebus saga


【解决方案1】:

因为您通过 ClientRef 属性映射到 saga 数据,所以您需要告诉持久性(在本例中为 Raven)该属性是唯一的。可能发生的情况是,在某些情况下(归结为竞态条件),第二条消息对 Raven 索引执行的查询检索过时数据,假设没有 saga 数据,然后创建新数据。

这应该可以解决您的问题:

[Unique]
public Guid ClientRef { get; set; }

有了这些信息,Raven saga 持久化器将基于这个属性创建一个额外的文档(因为在 Raven 中按 Id 加载是完全原子的),以便第二条消息一定会找到它。

如果您使用另一种持久性介质,如 NHibernate,则将使用相同的属性在该列上构造唯一索引。

根据评论编辑

唯一约束文档和您的 saga 数据将完全一致,因此根据传入消息的时间,会发生三件事之一。

  1. 消息确实是第一个到达并被处理的消息,所以没有找到 saga 数据,所以创建了它。
  2. 消息是第二个到达的,因此它会查找 saga 数据,找到它并成功处理。
  3. 第二条消息与第一条消息非常接近,因此它们同时在不同的线程中处理。两个线程都在 saga 数据中查找,但一无所获,因此它们都开始处理。完成第一个成功提交并保存其传奇数据的那个。完成第二次保存 saga 数据的尝试,但发现当它正在工作时,另一个线程已经移动了它的奶酪,所以 Raven 抛出了一个并发异常。您的消息返回队列并被重试,现在 saga 数据存在,重试的行为类似于场景 #2。

【讨论】:

  • 所以让我们说 raven db 没有返回所需的 saga,我的程序开始创建一个具有相同唯一 id 的新 saga,那么 raven db 不会抛出异常,我的消息可能迷失。否则不会出现这种情况
  • 已编辑答案作为回应。
  • 由于我的测试人员在使用上述解决方案之前没有遇到任何问题,因此我将其标记为已回答。谢谢
猜你喜欢
  • 2012-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多