5 ms·
I authored this article and just wanted to leave a quick note on here that I'm more than happy to answer any questions, or debate/discuss the finer points of HT
by brandur 10y ago
I authored this article and just wanted to leave a quick note on here that I'm more than happy to answer any questions, or debate/discuss the finer points of HTTP and API semantics ;)
An ex-colleague pointed out to me on Twitter today that there are other APIs out there that have developed a concept similar to Stripe's `Idempotency-Key` header, the "client tokens" used in EC2's API for example [1]. To my knowledge there hasn't really been a concerted effort to standardize such an idea more widely, but I might be wrong about that.
[1] http://docs.aws.amazon.com/AWSEC2/latest/APIReference/Run_Instance_Idempotency.html#client-tokens http://docs.aws.amazon.com/AWSEC2/latest/APIReference/Run_In...
- Johnie 10y agoHave you considered using the resource identifier as an idempotency key? Basically, have the client generate the ID (UUID, but namespaced on the server side [client+date]). This eliminates the need for the client to generate the idempotency key as well as the eliminates the need for the server to maintain an idempotency repository?
- deleted 10y ago[deleted]
- brandur 10y agoI think that would be a fine alternative to the system we have. I don't have a perfect history of events, but I suspect that our current design is basically the result of two things: 1. Idempotency keys as a concept were introduced quite a bit later than the API was originally conceived, so it made sense to make them an optional augmentation to existing integrations. 2. Our resource IDs have a fairly specific formats including a prefix (i.e. `acct_` for an account or a `ch_`). It would still be possible to generate this client-side, but it's a little extra trouble.
- Johnie 10y agoIf you were to do it over again, would you have done it the same way? What would you have changed?
- brandur 10y agoIdempotency keys are a simple enough concept, that I think that they're mostly okay as is (we could have done a few smarter things on the server side implementation, but luckily that can still be fixed). There are certainly a few things around HTTP semantics that Stripe got wrong. e.g. Most updates should probably be `PATCH` instead of `POST`, but it's probably not worth changing at this point.
- zwegner 10y agoIs there any plan for the ability to discard previously used idempotency keys, so that a request with all the same parameters can actually be duplicated? Sometimes a payment will fail because a customer's card is declined (e.g. because of fraud detection or insufficient funds), and after the customer sorts it out, we'd like to try recharging their card, and not just receive the same error message. Our current workaround is to wait 24 hours for the idempotency key to expire, but it would be nice to be able to retry the request sooner without maintaining state on our side to generate a different idempotency key for the same payment.
- jey 10y agoThis is by design. If you want to make "another attempt", you should use a new idempotency key. Think of it as "one attempt/transaction/request" == "one idempotency key".
- brandur 10y agoYes, exactly! Like you say, the idea is that an idempotency key represents a single request. It's perfectly okay to make a new request that's almost identical to the original in that it shares the same endpoint and all the same parameters, but that should be done using a freshly generated idempotency key.
- allworknoplay 10y agoMy team and I would love a blog post from you guys about the architecture of your idempotency tokens!
- brandur 10y agoOur implementation is probably less sophisticated than you're thinking, but thanks! I'll pitch the idea to the team. To put it very simply, it's a middleware that stores a known response by an idempotency key's value and does a lookup on it on subsequent requests.
- kazagistar 10y agoHow would this handle a distributed system, where a request might be routed to a different backend server? Do you try to guarantee that a request is statefully resent to the same target each time? Do you force your idempotency keys to be stored in some kind of distributed database, like a redis cluster?
- andorov 10y agoAnd so they did: https://stripe.com/blog/idempotency https://stripe.com/blog/idempotency
- whack 10y agoHi. Thanks for the very interesting article. Quick question regarding the following: > "On a response failure (i.e. the operation executed successfully, but the client couldn’t get the result), the server simply replies with a cached result of the successful operation." Have you considered having the server respond with different http-status-codes for the initial successful request as opposed to any nop-retries? This provides the client with additional information that may prove useful. A lazy client could simply choose to treat both response codes identically, as a success indicator. And a more diligent/sophisticated/paranoid client could choose to act upon this information in some other way. From a side-effects/persisted-data perspective, returning a different status-code would have the exact same effects as what you described. But it would also give clients additional information that they can choose to act upon if desired.
- AgentME 10y agoHow would the client act differently upon it? Sounds about as useful as telling the client how many TCP packets in the connection had to be retransmitted by the server.
- whack 10y agoTo give one extreme example: it's possible that the duplicate requests are occurring due to a client-side bug. If the duplicated request produces a different status code, a diligent/paranoid client can use this as a warning-signal that there might be a bug in the client's implementation.
- kainosnoema 10y agoIn principle, what you've built is quite similar to a cryptographic nonce [1], usually used to prevent replay attacks against authentication services, etc. We use similar tokens in our API for idempotency as well, though we called them nonces. Standardizing a bit on terminology and how to implement them properly would be really helpful. [1] https://en.m.wikipedia.org/wiki/Cryptographic_nonce https://en.m.wikipedia.org/wiki/Cryptographic_nonce
- brandur 10y agoThere are similarities, but given that people are pretty use to the word "nonce" being limited in use to crytography, I'm not sure that we could say definitively that re-using it here would be less confusing overall. From a more practical sense, the naming is most likely to stay unchanged just because a lot of people are used to already.
- kainosnoema 10y agoAbsolutely, I wasn't suggesting that you should change the naming. In fact I agree that nonce is likely not the best term for this. But the concept as it applies to HTTP APIs is a great one that I think could have a broadly recognized implementation. Whether folks start adopting the "Idempotency-Key" header or some other approach, having awareness and maybe even a standard could be great.
- brandur 10y ago> Whether folks start adopting the "Idempotency-Key" header or some other approach, having awareness and maybe even a standard could be great. Oh yes, hugely agree there! Part of the reason that I wrote this is that I couldn't find much prior art around idempotency keys (there is some, but not much). I am at least partially hoping that people will remember this article (or that it'll show up in a Google search) as they're implementing their own similar concept, and re-use the naming.