Hacker News new | ask | show | jobs
by xlii 47 days ago
That's why you need to separate work from actual input.

It's not about trying again but about making sure you get consistent state.

Imagine request for payment. You made one and timeouted. Why did it timeout? Your network or payment service error?

You don't know, so you can't decide between retry and not retry.

Thus practice is: make request - ack request with status request id (idempotent, same request gives same status id) - status checks might or might not be idempotent but they usually are - each request need to have unique id to validate if caller even tried to check (idenpotency requires state registration).

If you want to try again you give new key and that's it.

There might of course be bug in implementation (naive example: idempotency key is uint8) but proper implementation should scope keys so they don't clash. (Example implementation: idempotency keys are reusable after 48h).

If same calls result in different responses (doesn't matter if you saw it or not) then API isn't idempotent.

1 comments

> You don't know, so you can't decide between retry and not retry

I'm well aware that the first order went through, even though the dumb system fumbled the translation of the success message and gave me a 500 back.

I do retry because I wanted the outcome. I'm not giving it a new key (firstly because I'm a user clicking a form, not choosing UUIDs for my shopping cart) but more importantly, if I did supply a second key, it's now my fault for ordering two copies.

> I do retry because I wanted the outcome. I'm not giving it a new key (firstly because I'm a user clicking a form, not choosing UUIDs for my shopping cart) but more importantly, if I did supply a second key, it's now my fault for ordering two copies.

Upon initial request I have you "URPAY1". If you never check URPAY1 for status, we'll callback you and expect the result. If neither check nor callback succeeded rollback actions are ran (this is contractual agreement on partnership level).

You can verify your status with URPAY1. You need to provide your status check with check ID (URPAY1) and an unique request ID. You will receive a timestamped response. You won't get different responses for same CheckID + RequestID because it's a activity log and also procedure check (e.g. grossly simplifying success at 23:59:58 might be something different than success at 00:00:05 - these times can vary depending on partner, continent, so it's not only midnight etc.) If at any point you didn't get response you can retry and you will always get the same response.

Didn't get URPAY1 for the first time? No problem try againt second time. You'll get the same URPAY1. No new effects needed.

In this design you, as requester are in full power. You can make the same request 100 times which will cause only 1 effect. If networking is lost, something will crash you're still guaranteed to have effect AT MOST once.

In case you're curious for the full flows and handling edge cases Stripe has great documentation regarding how process looks like from merchant's customer's side (as this is their business and you can integrate with them).