From HN today: an IETF draft for a standardized Idempotency-Key
header, more or less identical to the convention used by Stripe.
This is a subject that’s near and dear and which I’ve written about extensively before, and it’s nice to see it getting some non-Stripe attention. For me, the draft’s most interesting aspect is that it compiles a list of heavy hitters who are already using an Idempotency-Key
almost exactly as proposed:
Along with a few smaller ones. One quirk is that the draft seems to be authored by a PayPal employee, and although PayPal has an idempotency concept, it notably does not appear to use Idempotency-Key
(preferring PayPal-Request-Id
instead).
The HN discussion goes a little off the rails – mostly going to show that idempotency is a nuanced enough subject that even hacker types don’t understand it very well (“just use PUT!”, “well I for one don’t think a standard adds value …”, “it should be called Request-Id
”, etc.). A Google employee argues that that an idempotency key more appropriately belongs in a request’s payload – likely a post-hoc rationalization of a design decision made because Google uses protobuf/GRPC everywhere, which don’t gel well with HTTP headers.
Any API where a leaked resource could make a difference (that is to say, most of them) should consider implementing Idempotency-Key
. I can’t speak for most of the companies on the list above, but Stripe is living proof that an extremely simple implementation goes a long way. Here’s how it works:
account_id
+ idempotency_key
. Store when the request started and its parameters.422 Unprocessable entity
is definitive because it’s determined based on the request’s parameters, but a 429 Too many requests
isn’t – those should remove the idempotency key’s partial record to give the client a chance to try again.Now, when another request comes in with the same idempotency key, do one of the following:
409 Conflict
, which indicates to a client to retry later for a more definite answer.Stripe used a unique index for the job, detecting duplicates atomically by handling a duplicate key error. Unique indexes in most databases would work well, with the only major consideration being that idempotency keys should generally expire, and not all implementations easily allow this at large volumes (running big DELETE
jobs can be a problem). Mongo TTL indexes do the trick, as would Redis/Redis Cluster where key expiry is a core feature.
Personally, I think that a more sophisticated idempotency key approach than Stripe’s is warranted, but it’s a case of not letting perfect be the enemy of good, and many API providers should consider including at least a basic version of the idea.
Did I make a mistake? Please consider sending a pull request.