Thuta Learning
API Integration & Webhooks
BasicWeb Developmentintermediate

Idempotency

ဒီခန်းပြီးရင် ဘာတတ်သွားမလဲ

  • Idempotency concept ကို နားလည်ရှင်းပြနိုင်ရန်
  • Diagram ကို ဖတ်ပြီး request/response (သို့) event flow ဘယ်လိုစီးဆင်းသလဲ ခြေရာခံနိုင်ရန်
  • ကိုယ့် ကိုယ်ပိုင် API integration အတွက် ဘယ်လို အသုံးချသင့်သလဲ ရှင်းပြနိုင်ရန်

နားလည်ထားရမယ့် အချက်

Timeout နဲ့ retry ပေါင်းလိုက်တာနဲ့ ပြဿနာအသစ်တစ်ခု ဖြစ်လာပါတယ် — client က request ပို့တယ်၊ server က တကယ် လက်ခံပြီး process လုပ်တယ် (card ကို charge လုပ်တာ (သို့) record ဖန်တီးတာ)၊ ဒါပေမယ့် response က client ရဲ့ timeout ထက် နှေးနေတယ်ဆိုပါစို့။

Client က failure တစ်ခုအနေနဲ့ မြင်ပြီး retry logic အတိုင်း request တစ်ခုတည်းကို ထပ်ပို့လိုက်တယ်။ Server ရဲ့ အမြင်ကတော့ identical request နှစ်ခု ရောက်လာခဲ့တာဖြစ်ပြီး၊ ပထမတစ်ခုက အောင်မြင်ပြီးသားလို့ design မလုပ်ထားရင် server မှာ သိစရာ လမ်းမရှိပါဘူး။

Idempotency ဆိုတာ အဲဒီ design property ပါ — operation တစ်ခုကို ကြိမ်ဖန်များစွာ ပြုလုပ်တာက တစ်ကြိမ်တည်း ပြုလုပ်တာနဲ့ effect အတိအကျ တူညီရင် idempotent ဖြစ်ပါတယ်။ Resource ဖတ်တာက သဘာဝအရ idempotent ပါ၊ charge (သို့) order အသစ် ဖန်တီးတာကတော့ မဟုတ်ပါဘူး — call တိုင်းက default အနေနဲ့ side effect အသစ်တစ်ခု ဖြစ်ပါတယ်။

Client က key generate လုပ်

Logical operation တစ်ခုတိုင်းအတွက် unique idempotency key တစ်ခုကို client ဘက်က တစ်ကြိမ်တည်း ဖန်တီးတယ်။

Header ထဲ ပေးပို့

Key ကို request header ထဲမှာ ထည့်ပြီး server ဆီ ပေးပို့တယ်။

Server က ပထမဆုံးအကြိမ် သိမ်းထား

Server က key ကို ပထမဆုံးအကြိမ် တွေ့တဲ့အခါ operation ကို လုပ်ပြီး result နဲ့အတူ သိမ်းထားတယ်။

Duplicate ရောက်လာရင် cached result ပြန်ပေး

Key တစ်ခုတည်း ထပ်ရောက်လာရင် server က operation ကို ထပ်မလုပ်ဘဲ သိမ်းထားတဲ့ result ကို ပြန်ပေးတယ်။

API အားလုံးက ဒါကို support မလုပ်ကြပါဘူး၊ ဒါကြောင့် အားထားခင် provider ရဲ့ documentation ထဲမှာ idempotency-key support ရှိမရှိ စစ်ဆေးဖို့ optional မဟုတ်ဘဲ မဖြစ်မနေ လုပ်ရမှာပါ။

Idempotency
Operation တစ်ခုကို ကြိမ်ဖန်များစွာ ထပ်လုပ်ခြင်းက တစ်ကြိမ်တည်း လုပ်ခြင်းနဲ့ effect တူညီနေတဲ့ design property။
text
IDEMPOTENCY KEY PREVENTS A DOUBLE CHARGE
----------------------------------------
CLIENT                                  SERVER
------                                  ------
  |-- POST /charge, key=key-abc -------->|
  |                                       |  charges card,
  |                                       |  saves result
  |                                       |  under key-abc
  |      (response lost, client times out)
  |
  |-- POST /charge, key=key-abc (retry)-->|
  |                                       |  key seen before!
  |<-- SAME result, no new charge --------|

လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်

အောက်က function က idempotency-key handling ရဲ့ server side ကို plain in-memory `Map` တစ်ခုကို store အနေနဲ့ သုံးပြီး model လုပ်ထားပါတယ်၊ real backend က unique constraint ပါတဲ့ database row ကို သုံးလေ့ရှိတဲ့နေရာကို ကိုယ်စားပြုတာပါ။

Scripted request ငါးခုက timeout-then-retry scenario ကို simulate လုပ်ဖို့ key နှစ်ခု, `key-abc` နဲ့ `key-xyz`, ကို တမင် ထပ်ခါထပ်ခါ ထည့်ထားပြီး၊ တစ်ကြိမ်ပဲ ရောက်လာမယ့် unique key, `key-def`, တစ်ခုပါ ထည့်ထားပါတယ်။

Output ထဲက `action` field ကို ကြည့်ပါ — key တစ်ခု ပထမဆုံးအကြိမ် ပေါ်လာတဲ့အခါ function က logical charge ကို ပြုလုပ်ပြီး result ကို သိမ်းထားပါတယ်, `processed_new_charge`။ ဒုတိယအကြိမ်တွင် charge အသစ် ဘာမှ မဖြစ်ဘဲ သိမ်းထားတဲ့ result အတိအကျကို ပြန်ပေးပါတယ်, `returned_cached_result`။

သတိထားစရာ တစ်ခုက — request 2 အတွက် `key-abc` အတွက် ပြန်ပေးတဲ့ cached result က request 1 ကနေ သိမ်းထားတဲ့ object အတိအကျဖြစ်ပြီး အသစ် generate လုပ်ထားတာ မဟုတ်ပါဘူး။ Lookup တိုင်းမှာ charge ID အသစ် generate လုပ်တဲ့ buggy implementation တစ်ခုက ခေတ္တကြည့်ရင် idempotent လိုပဲ ထင်ရပေမယ့် အောက်မှာ charge အစစ် ဒုတိယတစ်ခု တိတ်တဆိတ် ဖန်တီးနေမှာပါ။

Idempotency မရှိရင် ဖြစ်တတ်တာ

Idempotency handling မရှိတဲ့ payment endpoint တစ်ခုမှာ timeout ပြီး client က retry လုပ်ရင် customer ရဲ့ card ကို နှစ်ကြိမ် charge လုပ်မိနိုင်ပါတယ် — customer တစ်ယောက်က ပစ္စည်းတစ်ခုတည်းအတွက် ငွေနှစ်ဆ ပေးရတဲ့ ဖြစ်ရပ်ကို တွေ့ရလိမ့်မယ်။

အတူတူ စမ်းရေးကြည့်မယ်

javascript
function processRequests(requests) {
  const store = new Map();
  const results = [];
  for (const req of requests) {
    if (store.has(req.idempotencyKey)) {
      results.push({
        id: req.id,
        key: req.idempotencyKey,
        action: "returned_cached_result",
        result: store.get(req.idempotencyKey)
      });
    } else {
      const result = { chargeId: `ch_${req.id}`, amount: req.amount };
      store.set(req.idempotencyKey, result);
      results.push({
        id: req.id,
        key: req.idempotencyKey,
        action: "processed_new_charge",
        result
      });
    }
  }
  return results;
}

const requests = [
  { id: 1, idempotencyKey: "key-abc", amount: 5000 },
  { id: 2, idempotencyKey: "key-abc", amount: 5000 },
  { id: 3, idempotencyKey: "key-xyz", amount: 1200 },
  { id: 4, idempotencyKey: "key-xyz", amount: 1200 },
  { id: 5, idempotencyKey: "key-def", amount: 750 }
];

console.log(processRequests(requests));
You should see
request 1 (key-abc): processed_new_charge -> chargeId ch_1, amount 5000
request 2 (key-abc): returned_cached_result -> chargeId ch_1, amount 5000
request 3 (key-xyz): processed_new_charge -> chargeId ch_3, amount 1200
request 4 (key-xyz): returned_cached_result -> chargeId ch_3, amount 1200
request 5 (key-def): processed_new_charge -> chargeId ch_5, amount 750
(key-abc နဲ့ key-xyz ဒုတိယအကြိမ် ရောက်လာတဲ့အခါ charge အသစ် လုံးဝ မဖြစ်ဘဲ cached result ကိုပဲ ပြန်ပေးတာ သတိပြုပါ)

၅ မိနစ် စမ်းကြည့်

`processRequests` ရဲ့ scripted request list ထဲ key `key-def` ပါတဲ့ request တစ်ခု ထပ်ထည့်ပြီး run ကြည့်ပါ။ Output ရဲ့ `action` field ကို run မလုပ်ခင် ခန့်မှန်းပြီးမှ run ကြည့်ပါ။

သတိလေးတစ်ချက်

Idempotency key ကို client ဘက်က retry တိုင်း random regenerate လုပ်ခြင်း — key အသစ်ဖြစ်နေရင် server က duplicate ကို မှတ်မိမှာ မဟုတ်ပါ

API အားလုံးက idempotency key ကို support လုပ်တယ်လို့ မှတ်ယူခြင်း — documentation ကို မစစ်ဘဲ အားကိုးလိုက်ရင် ကာကွယ်မှု လုံးဝ မရှိနိုင်ပါ

Idempotent Requests - Stripe API DocsAPI Integration & Webhooks

ဒီနေရာမှာ လူအများမှားတတ်တယ်

  • Idempotency key ကို client ဘက်က retry တိုင်း random regenerate လုပ်ခြင်း — key အသစ်ဖြစ်နေရင် server က duplicate ကို မှတ်မိမှာ မဟုတ်ပါ
  • API အားလုံးက idempotency key ကို support လုပ်တယ်လို့ မှတ်ယူခြင်း — documentation ကို မစစ်ဘဲ အားကိုးလိုက်ရင် ကာကွယ်မှု လုံးဝ မရှိနိုင်ပါ
  • API Tutorial (apiguide) ကို မလေ့လာရသေးရင် ဒီ course ကို စမလိုက်ခင် အရင် ပြီးအောင် လေ့လာထားသင့်ပါတယ် — ဒီ course က REST/HTTP/Auth အခြေခံတွေကို ထပ်မသင်ဘဲ webhook, testing, reliability, integration architecture တို့ကိုသာ ဆက်လက် တည်ဆောက်ပါတယ်။

လေ့ကျင့်ခန်း

`processRequests` ရဲ့ scripted request list ထဲ key `key-def` ပါတဲ့ request တစ်ခု ထပ်ထည့်ပြီး run ကြည့်ပါ။ Output ရဲ့ `action` field ကို run မလုပ်ခင် ခန့်မှန်းပြီးမှ run ကြည့်ပါ။

You'll know it worked when: request 1 (key-abc): processed_new_charge -> chargeId ch_1, amount 5000 request 2 (key-abc): returned_cached_result -> chargeId ch_1, amount 5000 request 3 (key-xyz): processed_new_charge -> chargeId ch_3, amount 1200 request 4 (key-xyz): returned_cached_result -> chargeId ch_3, amount 1200 request 5 (key-def): processed_new_charge -> chargeId ch_5, amount 750 (key-abc နဲ့ key-xyz ဒုတိယအကြိမ် ရောက်လာတဲ့အခါ charge အသစ် လုံးဝ မဖြစ်ဘဲ cached result ကိုပဲ ပြန်ပေးတာ သတိပြုပါ)

Idempotency | Thuta Learning