Thursday, March 13, 2008 5:45 PM
So lately i have been thinking a lot about how I want to implement idempotence. Well this week I had the pleasure to discuss the topic with Evan Hoff, who is also thinking about this. Today he emailed me the following 'acid' test for idempotence.
If the message was generated by a traveling salesman in his hotel room and got forwarded to the application 10 days later, what are the chances of success?
If processing that message causes data loss by overwriting newer values (as an example), you have an idempotence problem.
Based on our conversation, the first step was to decide how much idempotence do we want. Every column, or every record, or groups of columns, etc? To make this simple lets go with idempotence of the record.
The next thing we discussed was keeping the message goal oriented, not 'CRUDy'. Hopefully that makes sense.
Based on those two things I came up with the following message, to update a sales prospect contact info.
public class SalesContactInfo : IMessage
{
public string Address { get; set; }
public string Telephone { get; set; }
//more as necessary
public Guid TransactionId { get; }
public Guid SourceId { get; }
public DateTime DateSubmitted {get; }
}
And the message handler
public void HandleSalesContactInfo(SalesContactInfo message)
{
Contact contact = repository.Get(message.ContactId);
AuditTrail trail = repository.Get<AuditTrail>(contact.Id);
if(trail.LastUpdate < message.DateSubmitted)
{
//update
}
}
But what if the dates match? Then I start to run into trouble and we have to start employing some smarts about which system 'wins.' But I think if Udi were here he would say, send a message back to the salesman to confirm the info. ?
public void HandleSalesContactInfo(SalesContactInfo message)
{
Contact contact = repository.Get(message.ContactId);
AuditTrail trail = repository.Get<AuditTrail>(contact.Id);
if(trail.LastUpdate < message.DateSubmitted)
{
//update
}
else if(trail.LastUpdate == message.DateSubmitted && message.SourceId != trail.SourceId)
{
bus.Reply(new DoubleCheckInfo(message, contact, trail));
}
}
Thoughts?