Live data from Hacker News

Idempotency is easy until the second request is different

blog.dochia.dev

81–90 of 244 posts

Re: Idempotency is easy until the second request is different

#81
post #48

Don't fix other people problems. If idempotent key was seen then send back response. Clients intention is outside the scope. If contract says "idempotency on key" the idempotent response on key. If contract says "idempotent on body hash" then response on body hash (which might or might not include extra data). APIs are contracts. Not the pinky promise of "I'll do my best guess"

IMHO it's more: fix problems, or at least mitigate them, regardless whose problem it is.

I've been in this situation, a clientside bug meant that different requests arrived with the same idempotency key.

In my case, updating the client would have taken weeks, in the best case scenario. Updating the backend to check for a matching request body would have taken minutes, maybe hours.

It took me a surprising amount of arguing to convince people that, even if it was a clientside bug, we couldn't let users suffer for weeks in name of "correctness".

Re: Idempotency is easy until the second request is different

#82
post #81
post #48

Don't fix other people problems. If idempotent key was seen then send back response. Clients intention is outside the scope. If contract says "idempotency on key" the idempotent response on key. If contract says "idempotent on body hash" then response on body hash (which might or might not include extra data). APIs are contracts. Not the pinky promise of "I'll do my best guess"

IMHO it's more: fix problems, or at least mitigate them, regardless whose problem it is. I've been in this situation, a clientside bug meant that different requests arrived with the same idempotency key. In my case, updating the client would have taken weeks, in the best case scenario. Updating the backend to check for a matching request body would have taken minutes, maybe hours. It took me a surprising amount of ar…

Yeah but don't let them reify it.

Ideally you already send client version in requests (or have an API version prefix). Add the workaround only for legacy clients.

Next client version must distinguish itself from predecessor and must not require the bodge to work.

Re: Idempotency is easy until the second request is different

#83
post #37

Well, it is "reality has a surprising amount of detail"[1] all over again. Or rather a good specific example for it. [1] http://johnsalvatier.org/blog/2017/reality-has-a-surprising-...

Thanks for letting me read that again.

I once wrote about inherent, irreducible complexity and how we try to deal with it. The draft has sections on how complexity can be hidden, spread out, localized, passed off, or recreated from scratch. Unfortunately, people are now using LLMs to pile complexity on the simplest of tasks, and my essay isn't really worth finishing.

Re: Idempotency is easy until the second request is different

#84
post #48

Don't fix other people problems. If idempotent key was seen then send back response. Clients intention is outside the scope. If contract says "idempotency on key" the idempotent response on key. If contract says "idempotent on body hash" then response on body hash (which might or might not include extra data). APIs are contracts. Not the pinky promise of "I'll do my best guess"

Sometimes "best guess" is the contract. Obviously many applications can't tolerate that, but surprisingly many can.

The user just needs to know what the trade-off is. And "best guess" can be hard to characterize, so you need to be extremely careful. But sometimes it's a big win for a low price.

Re: Idempotency is easy until the second request is different

#86

A couple of years ago, we experienced a silent data corruption incident in our checkout process due to this specific edge case. A user would generate the idempotency key by loading the front-end application, adding item(s) to their cart, submitting their order but timing out. The user would then navigate back to the front-end application and add another item and submit the order again. Since the user is submitting an…

>Client_Provided_Key + Hash(Request_Payload)

Congrats on destroying the purpose of Idempotency Keys.

Ask yourself, why not just `Hash(Request_Payload)`? That'll give you half of what you need to know about why the Idempotency Key header is useful in the first place.

The other half you already know? You just described your bug, it's a bug, on your front-end, this has nothing to do with idempotency; if anything, the system is performing as expected.

If your requests do something different, they should have different Idempotency Keys. <- this brings down TFA and most of the comments here. I guess those are the perils of vibecoding.

Re: Idempotency is easy until the second request is different

#87
post #48

Don't fix other people problems. If idempotent key was seen then send back response. Clients intention is outside the scope. If contract says "idempotency on key" the idempotent response on key. If contract says "idempotent on body hash" then response on body hash (which might or might not include extra data). APIs are contracts. Not the pinky promise of "I'll do my best guess"

The best pattern I've seen is hash the request and validate that it is the same one. If not, error out as it is an invalid argument.

"Best guess" can be bad if it is not well-defined, but you can still make error detection obvious rather than hidden.

Re: Idempotency is easy until the second request is different

#88
> Maybe the first request called a payment provider, the provider accepted it, and your process died before recording the result. Now your database cannot infer whether money moved.

This entire example is bad design. It's bad, bad design. I'm sorry, but if this is your example, you are doing it wrong in every way. There are ways to handle these sorts of things, well-known and well-established patterns. You are using none of these here.

I get it, it's an example, but it's a poor example. You should change it before someone assumes what you are talking about is sensible or reasonable in a production environment. Or at least put a warning.

Re: Idempotency is easy until the second request is different

#89
This is all way too much. If you see a duplicate idempotency key, skip the replay and always return 409. This becomes a client problem. Clients already need to help enforce idempotent contracts; "check for conflict response" is not an onerous imposition.

I've built multiple ecommerce APIs with this approach and they work great. No heroic measures required. You can often satisfy this contract with a unique constraint; if not, a simple presence check in redis. No hashing or worrying about PII.

My rant about this: https://github.com/stickfigure/blog/wiki/How-to-%28and-how-n...

Re: Idempotency is easy until the second request is different

#90
post #48

Don't fix other people problems. If idempotent key was seen then send back response. Clients intention is outside the scope. If contract says "idempotency on key" the idempotent response on key. If contract says "idempotent on body hash" then response on body hash (which might or might not include extra data). APIs are contracts. Not the pinky promise of "I'll do my best guess"

> APIs are contracts. Not the pinky promise of "I'll do my best guess" You have never had to work with PHP backends, have you? JSON in PHP is a flustercluck. Undefined, null, "" or "null", that is always the question. If you use a typed Go/Rust client and schemas, you usually end up with "look ahead schemas" that try to detect the actual types behind the scenes, either with custom marshallers or with some v1/v2/v3 et…

Yes I have. You learn the quirks and then it’s ok.
Post reply on HN