6 ms·
But that's not idempotent? If I'm a client and I don't know if the original request went through, getting a 409 on any subsequent requests tells me nothing abou
by halestock 5mo ago
But that's not idempotent? If I'm a client and I don't know if the original request went through, getting a 409 on any subsequent requests tells me nothing about whether the original request was successful or not.
- onionisafruit 5mo agoThe 409 should come with an id or a url the client can use to find the original result.
- stickfigure 5mo ago...and if you're using the approach of "let the client pick ids", you don't even need that. The client has everything it needs.
- deleted 5mo ago[deleted]
- stickfigure 5mo agoRetries will only receive 409 if the original request was successful. If the original request failed, the server performs the operation as normal on the second request. It doesn't replay failures. The whole point of the idempotence mechanism is so you can make a reliable distributed system. If the first try fails, the client doesn't know if it succeeded or not, so the client should try again later ("at-least-once"). The idempotence mechanism just ensures that we don't get duplicates in the case that the first try actually succeeded. If you replayed failures there wouldn't be any point to the idempotency key.
- pdonis 5mo agoWhat if the original request is still being processed when the retry comes in? That doesn't fall into either of your categories: the request isn't successful, but it hasn't failed either.
- stickfigure 5mo agoThat's usually solved with traditional database transactions. Even if you have a complex long-running multistep orchestration problem, you can break it down into simpler transactions. Eg you could start with a "lock the resources" txn. But 99% of these conversations around idempotence are simple POST operations like "create order" that regular old database concurrency management handles just fine.
- pdonis 5mo ago> That's usually solved with traditional database transactions. That doesn't answer my question. What response do you return to the client in the case I described?
- matsemann 5mo agoThe lock would normally make the second request wait, aka not return a response, until the first one is done. Then it sees it's a duplicate and returns that. Or it times out and returns an error. Then the client hopefully have some exponential back off strategy, so the third attempt doesn't suffer the same fate.
- MattDaEskimo 5mo agoIf a transaction is locked then subsequent requests would return a 409, ideally with an error message indicating that it's currently being processed.
- stickfigure 5mo agoThis is just normal concurrent programming? If two requests come in for the same idempotency key/customer reference id, only one will succeed. Use standard database transaction isolation. So one will complete with 200, one will complete with 409. It doesn't matter which. That said, there's something odd about the way you phrased this question. If the original request hasn't gotten a response yet, why is it sending a retry? What you're asking is more general: What happens when two conflicting requests come in? This is something we've been solving with RDBMSes since the 1970s.
- whoamii 5mo agoYou are improvising, and in the process, changing the semantics of a well established design pattern. Do not recommend.
- stickfigure 5mo agoYour comment contributes nothing to the discourse. It's FUD. The pattern I describe was the dominant design pattern for financial transaction processing systems before Stripe. Stripe's API makes life for the clients slightly easier at the expense of making life for servers more complicated, but the two approaches are equivalent in function.
- whoamii 5mo agoThe topic is about idempotency though, and what you are describing is not idempotency. You are describing something different, and arguing “but it accomplishes a similar thing.” It appears you have come to the same conclusion as the article that building idempotency isn’t trivial.
- stickfigure 5mo agoIt absolutely is idempotent behavior of the system. The goal is "make an idempotent payment", and this approach ("return an error for duplicates") was standard for financial APIs before Stripe. It still dominates for ecommerce APIs. It isn't complicated, though I can see how if your entire experience with financial APIs is Stripe, you might not be aware of how simple it is. Because Stripe's approach, while mildly more convenient for clients, is a PITA to implement properly.
- whoamii 5mo agoYou seem to believe idempotency is a fancy word for avoiding duplicates, which is why we seem to be having different conversations.
- 5mo ago
- hamdingers 5mo agoIf you're a client using the same idempotency key for a materially different request you have a bug.
- theptip 5mo agoYes, and if you are building a payment API you need to be robust to client bugs.
- anamexis 5mo agoRejecting the conflicting request is being robust to improperly reused idempotency keys. There's no other reasonable answer.
- jeremyjh 5mo agoThe client is part of a distributed transaction. It can't be oblivious to this. Clear semantics and accurate adherence to them is the only answer that doesn't make the overall system unsound. Client bugs are expected and so the simplest semantics that ensure data integrity and accurate responses are the best way to help them identify and fix their bugs.
- kelnos 5mo agoYou provide in the response body a JSON blob or something that indicates a detailed error code, like DUPLICATE_IDEMPOTENCY_KEY, with some more information that points to the record ID of the corresponding transaction that the client can fetch. Sure, a strict reading of "idempotence" might require that the response for subsequent requests be identical to the first, but for practical concerns, what matters is the API contract you define, document, and adhere to. The purpose of idempotence is to ensure that you don't end up with duplicate transactions. That's what actually matters. How that's represented in the protocol is an implementation detail.