Hacker News new | ask | show | jobs
by stickfigure 42 days ago
While I'd love to take credit for it, this isn't literally "my" approach; this is just the standard way that transaction processing systems on the web work. And it's so standard that you don't even have to write code for it. Databases handle all the isolation and concurrency issues for you.

Part of the problem here is that we're confusing how do you structure the API (replay? 409? something else?) with how we implement the API. The original article (and my original response) focused on API structure. We're wandering into the details of implementation, which is fine, but there are of course many ways to do the implementation. Some simpler than others.

Here's the simplest and most reliable way to implement idempotency for a trivial "create payment" operation, where the client submits an idempotency key. This pattern is incredibly common. Every request looks something like this:

* Start a transaction

* Lookup "does this idempotency key already exist"

* If it doesn't, insert the payment record with the idempotency key

* Commit the transaction

* Return the result. Successful insert is always 200OK. "key already exists" results in either replay of the original result (Stripe model) or an explicit error like 409 (my favored approach, still ubiquitous in ecommerce, and very common in financial APIs that predate Stripe).

Does that help? If you're using your database to handle concurrency, you need every request to start inside the transaction. You can't check the idempotency key outside of the transaction or you can't guarantee once-and-only-once behavior.

[Before someone mentions it, yes you can use a unique constraint instead of an explicit transaction, and this is conceptually identical - the check-for-dup transaction is inside a single INSERT]

2 comments

> Does that help?

What you said up to that point didn't really. But then you said this:

> If you're using your database to handle concurrency, you need every request to start inside the transaction. You can't check the idempotency key outside of the transaction or you can't guarantee once-and-only-once behavior.

Which answers the question that what you said earlier in your post raised. If I'm understanding you right, "lookup the idempotency key" is also relying on the same database, so you need the whole operation to be inside a single transaction in that database.

> Part of the problem here is that we're confusing how do you structure the API (replay? 409? something else?) with how we implement the API.

It would seem to me that you would want "what happens if a second request comes in with the same idempotency key while the first is still in progress" to be part of the API, so clients would know what your server is going to do in that scenario.

Nobody cares. These are short lived transactions (generally milliseconds); collisions are a rare edge case; it's fine to block. One request succeeds, the other gets a dup error (or a replay).

You could invent your own more sophisticated idempotency API but good luck finding someone that wants to implement it or use it. What real-world problem are you trying to solve?

> Nobody cares.

Meaning, clients don't care about the thing I asked about?

> What real-world problem are you trying to solve?

I'm trying to understand your answers to my questions. When there seems to me to be something missing, I ask about it.

Correct, clients don't care to be notified if some other short lived request is in flight.