53 points by ludovicianul 2 days ago | 7 comments
mmillin 45 minutes ago
This is an excellent article, I’ve seen almost all of the issues it calls out in production for various APIs. I’ll be saving this to share with my team.

I’ve seen two separate engineers implement a “generic idempotent operation” library which used separate transactions to store the idempotency details without realizing the issues it had. That was in an organization of less than 100 engineers less than 5 years apart.

One other thing I would augment this with is Antithesis’ Definite vs Indefinite error definition (https://antithesis.com/docs/resources/reliability_glossary/#...). It helps to classify your failures in this way when considering replay behavior.

mrkeen 2 minutes ago
The point of idempotency is safe retries. Systems are completely fallible, all the way down to the network cables.

The user wants something + the system might fail = the user must be able to try again.

If the system does not try again, but instead parrots the text of the previous failure, why bother? You didn't build reliability into the system, you built a deliberately stale cache.

shiandow 1 hour ago
This seems to assume retrying a command should result in the same response, but I am not sure I agree.

Idempotency is about state, not communication. Send the same payment twice and one of them should respond "payment already exists".

Jolter 18 minutes ago
I don’t know if we’re reading the same article? The linked one states very plainly:

”Idempotency is about the effect

An operation is idempotent if applying it once or many times has the same intended effect.”

cocoto 51 minutes ago
In your example, idempotency means same request + same state = same response. State becomes part of the request, that’s why it is hard.
shiandow 44 minutes ago
That's just deterministic behaviour.

For idempotency you literally just want f(state) = f(f(state)). Whether you achieve this by just doing the same thing twice (no external effects) or doing the thing exactly once (if you do have side effects) is not important.

But if you have side effects and need something to happen exactly once it seems a lot more useful to communicate this, rather than pretending you did the thing.

adrianmsmith 2 minutes ago
> But if you have side effects and need something to happen exactly once it seems a lot more useful to communicate this, rather than pretending you did the thing.

I think it depends on whether the sender needs to know whether the thing was done during the request, or just needs to know that the thing was done at all. If the API is to make a purchase then maybe all the caller really needs to know is "the purchase has been done", no matter whether it was done this time or a previous time.

And in terms of a caller implementing retry logic, it's easier for the caller to just retry and accept the success response the second time (no matter if it was done the second time, or actually done the first time but the response got lost triggering the retry).

huflungdung 12 minutes ago
[dead]
zinkem 1 hour ago
Idempotency is easy if you don't use mutable state in your middleware.

Auth, logging, and atomicity are all isolated concerns that should not affect the domain specific user contract with your API.

How you handle unique keys is going to vary by domain and tolerance-- and its probably not going to be the same in every table.

It's important to design a database schema that can work independently of your middleware layer.

chaz6 58 minutes ago
You keep the hash of the request so that you can reject a subsequent request with a different body. This has helped me surface bugs and data issues in other systems.
villgax 6 minutes ago
skill issue lol, it's not idempotent anymore, same key for different requests? Heard of a nonce?
stavros 1 hour ago
Half of the mentioned issues are issues of atomicity, not idempotency. If I make a request, and the server crashes midway and doesn't send some crucial events, that's an issue whether or not I send a second request.

From a cursory read, only the part up to "what if the second request comes while the first is running" is an idempotency problem, in which case all subsequent responses need to wait until the first one is generated.

Everything else is an atomicity issue, which is fine, let's just call it what it is.

adamvendel 24 minutes ago
[dead]