I have implemented idempotent order placement (mostly to avoid accidental double submissions) but I am not sure how to handle incomplete operations. Example scenario:
- User tries to place an order.
- An order instance with status
PENDING_PAYMENT
is created in the DB. - Order payment succeeds (3rd party processor, supporting idempotence keys, e.g. Stripe).
- My DB fails to update order status to
PAID
(e.g. it suddenly went down for a minute) and user receives some error.
Since the whole operation is idempotent, it is safe to retry the operation, and some (most?) users would choose to do that.
But what if the user abandons the operation?
- I could implement a Completer process, which would push all incomplete operations through to completion. However, this might come as a surprise to the user.
- I could combine the Completer with the assumption that it will eventually be able to successfully place the order, in which case I wouldn't even have to alert the user. However, in an odd case of a failure, I'd have an even more surprising outcome - the once successful order would now appear to be failed.
Questions:
- What are some ways of dealing with this situation?
- What would the user typically expect?
- 2.1. Should I let the user know exactly what happened (i.e. payment ok, status not ok), inform the user of a generic failure (something went wrong, please retry), or let them know nothing at all?
- 2.2. If I inform the user of a generic error, they might decide to update their basket and then resubmit the order. I was thinking that the way to deal with this is to simply generate a fresh idempotence key and create a second order. What are the alternatives?
Additional details:
- I don't expect a high rate of failures, but I want to be prepared.
- I am not dealing with big money or sensitive data - consider this a simple e-shop.
Update
I actually followed this article from Brandur Leach whilst implementing my idempotent operations, in case you're interested: https://brandur.org/idempotency-keys.
I contacted Brandur directly regarding my problem and you can see what he had to say for yourselves: https://github.com/brandur/sorg/issues/268. The gist is that I should always push all operations to completion, which agrees with the answers here. I can then decide what to do with the result. There may be multiple ways of informing the user too.
PAID
. At this point I report an unexected error to the user. Since the whole operation is 100% retryable (including the call to the Payment provider), I expect most people to actually retry the operation but some might decide not to, leaving the order state ambiguous.