1

我有两条消息,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();


       }

谢谢,

4

1 回答 1

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。
于 2013-10-23T14:40:56.940 回答