使用raven db持久性的Saga错误nservicebus

使用raven db持久性的Saga错误nservicebus,nservicebus,saga,Nservicebus,Saga,我有两条消息,clientChangeMessage(负责创建客户)和clientContractChangeMEssage(负责客户的预订详细信息)。现在在我的数据库中,只有拥有客户合同才能创建客户机,反之亦然。在我的本地系统上,一切正常,即,如果首先收到客户机更改消息,我将其存储在saga中,等待客户机合同消息,当消息到达时,saga将执行这两条消息。但在我的测试机上,当客户变更消息出现时,它会存储在传奇中,但当客户合同发生变更时,传奇不会找到客户变更传奇,因此会创建另一个传奇。我用测试人员

我有两条消息,clientChangeMessage(负责创建客户)和clientContractChangeMEssage(负责客户的预订详细信息)。现在在我的数据库中,只有拥有客户合同才能创建客户机,反之亦然。在我的本地系统上,一切正常,即,如果首先收到客户机更改消息,我将其存储在saga中,等待客户机合同消息,当消息到达时,saga将执行这两条消息。但在我的测试机上,当客户变更消息出现时,它会存储在传奇中,但当客户合同发生变更时,传奇不会找到客户变更传奇,因此会创建另一个传奇。我用测试人员尝试过的相同消息尝试了它,它在我的机器上工作,但无法找出可能出现的问题。我正在使用raven db持久性。(很抱歉,我想不起为此粘贴任何代码)

客户萨加斯塔特

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();


       }
public类ClientSagaState:IContainSagaData
{
#区域服务总线
公共Guid Id{get;set;}
公共字符串发起者{get;set;}
公共字符串OriginalMessageId{get;set;}
#端区
公共Guid ClientRef{get;set;}
公共ClientMessage ClientChangeMessage{get;set;}
公共ClientContractChangeMessage ClientContractChange{get;set;}
}
公共类客户传奇:传奇,
我开始发短信,
我开始发短信了
{
公共覆盖无效配置HowtoFindsaga()
{
配置映射(s=>s.ClientRef,m=>m.EntityRef);
配置映射(s=>s.ClientRef,m=>m.PrimaryEntityRef);
}
公共无效句柄(ClientChangeMessage消息)
{
if(BusRefTranslator.GetLocalRef(EntityTranslationNames.ClientChange,message.EntityRef.Value)!=null)
{
GetHandler().Handle(消息);
CompleteTheSaga();
返回;
}
HandleServiceUserChangeAndDependencies(消息);
//MarkAsComplete();
CompleteTheSaga();
}
公共无效句柄(ClientContractChangeMessage消息)
{
var state=此.Data;
//一些处理逻辑
//检查客户端是否不在数据库中,然后存储状态
state.ClientContractChange=消息;
state.ClientRef=message.PrimaryEntityRef;
//如果客户机在数据库中,则
MarkAsComplete();
}

谢谢,

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

这将解决您的问题:

[Unique]
public Guid ClientRef { get; set; }
有了这些信息,Raven saga persister将基于此属性创建一个额外的文档(因为在Raven中按Id加载是完全原子的),以便第二条消息一定会找到它

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

根据评论进行编辑

独特的约束文档和您的传奇数据将完全一致,因此根据传入消息的时间,将发生以下三种情况之一

  • 该消息确实是第一条到达并被处理的消息,因此没有找到任何saga数据,因此创建了saga数据
  • 消息是第二条到达的消息,因此它会查找saga数据,找到它,并成功地进行处理
  • 第二条消息与第一条消息非常接近,因此它们同时在不同的线程中进行处理。两个线程都在查找saga数据,但什么也没有找到,因此它们都开始处理。第一条消息完成的线程成功提交并保存其saga数据。第二条消息完成的线程尝试保存saga数据,但发现另一个线程在工作时移动了它的奶酪,因此Raven抛出一个并发异常。您的消息返回队列并重试,现在saga数据已经存在,重试的行为类似于场景2

  • 因为您是通过ClientRef属性映射到saga数据的,所以需要告诉持久性(在本例中为Raven)该属性是唯一的第二条消息对Raven索引执行的查询检索陈旧数据,假设没有saga数据,并创建新的索引

    这将解决您的问题:

    [Unique]
    public Guid ClientRef { get; set; }
    
    有了这些信息,Raven saga persister将基于此属性创建一个额外的文档(因为在Raven中按Id加载是完全原子的),以便第二条消息一定会找到它

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

    根据评论进行编辑

    独特的约束文档和您的传奇数据将完全一致,因此根据传入消息的时间,将发生以下三种情况之一

  • 该消息确实是第一条到达并被处理的消息,因此没有找到任何saga数据,因此创建了saga数据
  • 消息是第二条到达的消息,因此它会查找saga数据,找到它,并成功地进行处理
  • 第二条消息与第一条消息非常接近,因此它们同时在不同的线程中处理。两个线程都在查找saga数据,但什么也没有找到,因此它们都开始处理。第一条消息完成的线程成功提交并保存其saga数据。第二条消息