पाठ 9 / 27

Idempotency Keys

Client द्वारा दी गई idempotency key से POST अनुरोधों को retry के लिए सुरक्षित बनाएँ।

दोहरे शुल्क की समस्या

Client POST /payments भेजता है, connection टूट जाता है, और उसे पता नहीं कि भुगतान हुआ या नहीं। आँख मूँदकर retry करने से दो बार शुल्क लग सकता है। Idempotency key इसे हल करती है: client इरादे के लिए अनोखा मान (UUID) बनाता है और Idempotency-Key header में भेजता है। Server उस key के नीचे नतीजा रखता है; वही key फिर आए तो कार्य दोहराने की जगह रखा नतीजा लौटाता है, आम तौर पर ऐसे header के साथ जो उसे replay बताए। Keys एक अवधि (जैसे 24 घंटे) बाद expire होनी चाहिए, और अलग request body के साथ key दोबारा उपयोग करना अस्वीकार होना चाहिए। असली side effects वाले ऑपरेशनों, जैसे भुगतान, orders, emails, के लिए ऐसी keys अनिवार्य करें।

सुरक्षित retries, सस्ते दोहराव

Networks विफल होते हैं, इसलिए अच्छी तरह डिज़ाइन किए APIs retries को सुरक्षित और बार-बार की reads को सस्ता बनाते हैं।

चार साधन: idempotency, caching, सीमाएँ, async।
चित्र 3.1 — Idempotency, caching, सीमाएँ और async।

दोहराया गया भुगतान, चलाकर

मैंने यह केवल Python standard library से बने छोटे असली HTTP API पर चलाया (पूरा कोड केस स्टडी में)। पहला POST key abc-123 के साथ pay_1 बनाता है (201, Location header के साथ)। वैसा ही retry वही भुगतान 200 और Idempotent-Replayed: true के साथ लौटाता है, और b1 == b2 True है। Key ग़ायब हो तो 400, और अमान्य amount पर field विवरण के साथ 422 मिलता है।

# uses srv, base and call() from the runnable demo in the case study
k = {"Idempotency-Key": "abc-123", "Content-Type": "application/json"}
s1, h1, b1 = call("POST", "/v1/payments", {"amount": 500}, k); s2, h2, b2 = call("POST", "/v1/payments", {"amount": 500}, k)
print(s1, b1, h1.get("Location")); print(s2, b2, h2.get("Idempotent-Replayed"), b1 == b2)
print(call("POST", "/v1/payments", {"amount": 500}, {"Content-Type": "application/json"})[0], call("POST", "/v1/payments", {"amount": -5}, {**k, "Idempotency-Key": "z"})[2]["errors"])

Output:

201 {'id': 'pay_1', 'amount': 500, 'status': 'succeeded'} /v1/payments/pay_1
200 {'id': 'pay_1', 'amount': 500, 'status': 'succeeded'} true True
400 [{'field': 'amount', 'message': 'must be > 0'}]

Key और नतीजा अणु-रूप से रखें

दो retries एक ही समय आ सकते हैं। काम से पहले key आरक्षित करें (जैसे database की unique constraint से) ताकि सिर्फ़ एक अनुरोध चले और दूसरा इंतज़ार करे या रखा नतीजा पाए।

त्वरित जाँच: समान अनुरोध के साथ दोहराई Idempotency-Key मिलने पर server क्या करता है?

  • पहला नतीजा हटाता है
  • कार्य फिर करता है
  • कार्य दोहराए बिना रखा नतीजा लौटाता है
  • Crash हो जाता है
Answer

कार्य दोहराए बिना रखा नतीजा लौटाता है — Key retries को मूल प्रयास से जोड़ती है ताकि side effect एक बार हो।