A Curious Mind
#tastic

Idempotence

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?

Feedback

# re: Idempotence

I see you are sending back the current status of the contact as well. Would the caller know what the most up-to-date contact should look like?
We push the burden onto the caller to verify correct data, but it's possible they are not the only committer.

Go with me on this for a minute, because it is really hypothetical. What if a repeat customer calls the support staff to fix a typo they gave the salesman a few minutes after the salesman leaves their home? The support staff updates the data (typo on telephone number). For some reason beyond our control, the dates match exactly when we finally get the message from the salesman.
If we double check with the salesman, he will probably tell us what he gave us was correct. Mostly because he doesn't carry the same knowledge that our system should.

Perhaps we can ask the customer. It's not like this would happen very often (we hope).

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)
{
//save conflict to anomalies table
//send ticket for call to customer
bus.Send(new SupportRequest(conflict,contact,trail));
}

} 3/13/2008 11:46 PM | Robz

# re: Idempotence

Sure does remove all my formatting when I click post. :D

bus.Send(new SupportRequest(conflict.Id,contact.Id,trail.Id)); to pass along less info. The support personnel can look all of that up. 3/13/2008 11:52 PM | Robz

# re: Idempotence

All idempotence means is that if the message were processed multiple times the result would be the same as if were processed only once.

You're right in understanding that this leads to interesting concurrency scenarios, but the problem your analysing is just that - a concurrency issue. 3/16/2008 4:44 PM | Udi Dahan

Comments have been closed on this topic.