I have two messages , clientChangeMessage( responsible for creating the client) and clientContractChangeMEssage( responsible for the booking details of the client). Now in my database a client cannot be created until it has the client contract and vice-versa. On my local system everything is working fine i.e. if get a client change message first i store it in the saga and wait for the client contract message and when that arrives the saga executes both the messages. But on my testers machine when the client change message comes it gets stored in the saga but when a client contract change comes the saga does not find the client change saga and hence creates another saga. I have tried it with the exact same messages that my tester has tried ,it works on my machine, and am unable to figure out what might be going wrong. I am using raven db persistence. (Sorry i could not think of pasting any code for this)
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();
}
Thanks,
Because you are mapping to the saga data via the ClientRef property, you need to tell the persistence (Raven in this case) that this property is unique. What is probably happening is that, in some cases (it comes down to a race condition) the query done on the Raven index by the second message retrieves stale data, assumes there is no saga data, and creates new.
This should fix your issue:
[Unique]
public Guid ClientRef { get; set; }
With this information, the Raven saga persister will create an additional document based on this property (because loading by Id in Raven is fully atomic) so that the second message will be sure to find it.
If you were using another persistence medium like NHibernate, the same attribute would be used to construct a unique index on that column.
Edit based on comment
The unique constraint document and your saga data will be fully consistent, so depending on timing of incoming messages, one of 3 things will happen.