Idempotency and Two Phase Commit

The requirement for services to be idempotent is often stated as being important for enterprise applications. But what does that mean, and why?

Idempotent means that a service can be called multiple times with the same data, and the result will always be the same. For example, if a service call results in a value being written to a database, the same service call made again would result in the same value being written to the database. As such, additative processes where values are incremented cannot be idempotent, for example an insert statement in a database is not idempotent, whereas an update statement usually is.

Imagine the case of purchasing a ticket from a web service offering airline tickets. The process probably includes getting an offer to see the price and tarif, reserving an instance of that ticket and finally when the shopping cart is full, confirming that ticket by booking it. Getting an offer would be an idempotent call, since we are just effectively reading data, not writing it. Reserving the ticket cannot be idempotent because each call should result in an individual seat being temporarily reserved – you don’t want to reserve the same seat for two passengers. However, should such a reserved ticket not be booked, a background process would need to cancel the temporary reservation, so idempotency is effectively achieved. In the final call, to book a reservation (to guarantee the seat), the call should be idempotent – setting the status of the ticket to "booked" can be made over and over, it is not an additative process.

Why is it important? When it comes to booking, you need to make a payment to confirm the booking. That payment and the booking need to be done in a single transaction. There are two systems involved (booking system and payment system). That means either rolling back everything if either one of the booking or payment fails, or having the ability to redo one of the calls in case the other one fails and you want to try again later (immediately by clicking the "PAY" button, or perhaps in BPEL, at some time later, when the failed process is inspected). If you can rollback both calls, within an XA (two phase commit) transaction context, then you have no problem. But in this day and age, where foreign systems are called over non-transactional web services there is no XA context. So what you need to do, is have an idempotent booking service where you can call to make the booking again. You also need a background process which runs in order to explicitly cancel such bookings, if the payment really cannot be made. But in the time between the first failure, and the background process cancelling the ticket, you do need to be able to try and book again, which requires an idempotent service. If neither two phase commit nor idempotency are available, then in the case of a failure, your only choice is to cancel the ticket and make a new reservation and try and book it and pay for it.

So, the result is that when you integrate enterprise systems you really need to analyse the transaction concept in great detail, to ensure your customer gets the best experience in case of any system failures.

Copyright (c) 2009 Ant Kutschera