နားလည်ထားရမယ့် အချက်
Webhook delivery ဟာ အတိအကျ တစ်ကြိမ်တည်း ဖြစ်ပေါ်မယ်လို့ အာမခံမထားပါဘူး။ သင့် endpoint က response ပြန်ဖို့ နှေးကွေးရင် ဒါမှမဟုတ် timeout ဖြစ်ရင် provider အများစုက delivery မအောင်မြင်ဘူးလို့ ယူဆပြီး ထပ်ကာထပ်ကာ retry ပို့ပါလိမ့်မယ်။
ဒါက prerequisite course က idempotency သဘောတရားနဲ့ တိုက်ရိုက် ဆက်စပ်ပါတယ်။ Idempotent handler တစ်ခုက event တူတူ ဘယ်နှကြိမ်ရောက်ရောက် ရလဒ်တူတူ ပေးရပါမယ် — duplicate delivery တိုင်းက ဘေးကင်းတဲ့ no-op ဖြစ်သင့်ပါတယ်။
လက်ခံပြီး verify လုပ်ပါ
Request ကို လက်ခံပြီး body ထဲကဘာမှ မဖတ်ခင် signature ကို verify လုပ်ပါ။
Event ID ကို စစ်ပါ
ဒီ event ရဲ့ unique ID ကို သင့် processed-events store ထဲမှာ ရှာကြည့်ပါ။
အသစ်ဆိုရင် process + မှတ်တမ်းတင်ပါ
Real work ကို လုပ်ဆောင်ပြီး နောက်ပိုင်း duplicate တွေ သိအောင် ID ကို မှတ်တမ်းတင်ပါ။
Duplicate ဆိုရင် acknowledge ရုံသာ
Real work ကို ကျော်ပြီး provider retry ရပ်အောင် success ကို ချက်ချင်းပြန်ပေးပါ။
Provider ပို့တဲ့ event တိုင်းမှာ ဒီအတွက်ပဲ unique ID တစ်ခု ပါလေ့ရှိပါတယ် — အဲဒါကို သိမ်းထားပါ၊ စစ်ဆေးပါ၊ ပြီးရင် duplicate တွေဟာ ခြိမ်းခြောက်မှု တစ်ခု မဖြစ်တော့ပါဘူး။
IDEMPOTENT WEBHOOK HANDLING
---------------------------
-----
Receive request
|
v
Verify signature (reject if invalid)
|
v
Already processed this event ID?
|
+---+---+
| |
yes no
| |
v v
Acknowledge Process + record ID
only + acknowledge
(no reprocessing)လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်
အောက်က code က processedEvents set တစ်ခုနဲ့ sideEffects log တစ်ခုကို ထိန်းထားပါတယ် — database table တစ်ခုနေရာမှာ ကိုယ်စားပြုထားတာပါ။ handleWebhookEvent က ID အသစ်ဆိုမှသာ real side-effect logic ကို ထိရောက်ပါတယ်။
Delivery ငါးခု အစဉ်လိုက် ဝင်လာပါတယ် — ပြီးသား တွေ့ဖူးတဲ့ event နှစ်ခု ထပ်ခါဝင်လာတာလည်း ပါပါတယ်၊ retry mechanism အတိအကျပါပဲ။ Result တစ်ခုစီရဲ့ action field ကို sideEffects count နဲ့ နှိုင်းယှဉ်ကြည့်ပါ။
Request ငါးခု ရောက်လာပေမယ့် distinct event သုံးခုပဲ real processing ကို trigger လုပ်ပါတယ်။ ထပ်ခါဝင်လာတဲ့ နှစ်ခုက ACKNOWLEDGED_ONLY ရပြီး order ကို နှစ်ခါ update မလုပ်ဘူးလို့ ဆိုလိုပါတယ်။
အတူတူ စမ်းရေးကြည့်မယ်
const processedEvents = new Set();
const sideEffects = [];
function handleWebhookEvent(event) {
if (processedEvents.has(event.id)) {
return { id: event.id, action: "ACKNOWLEDGED_ONLY", reason: "Already processed (duplicate delivery)" };
}
processedEvents.add(event.id);
sideEffects.push(event.id);
return { id: event.id, action: "PROCESSED", reason: "New event, side effect applied" };
}
const incomingDeliveries = [
{ id: "evt_001", type: "payment.completed" },
{ id: "evt_002", type: "payment.completed" },
{ id: "evt_001", type: "payment.completed" },
{ id: "evt_003", type: "payment.completed" },
{ id: "evt_002", type: "payment.completed" }
];
const results = incomingDeliveries.map(handleWebhookEvent);
console.log(results);
console.log("Total side effects applied:", sideEffects.length);[
{ id: 'evt_001', action: 'PROCESSED', reason: 'New event, side effect applied' },
{ id: 'evt_002', action: 'PROCESSED', reason: 'New event, side effect applied' },
{ id: 'evt_001', action: 'ACKNOWLEDGED_ONLY', reason: 'Already processed (duplicate delivery)' },
{ id: 'evt_003', action: 'PROCESSED', reason: 'New event, side effect applied' },
{ id: 'evt_002', action: 'ACKNOWLEDGED_ONLY', reason: 'Already processed (duplicate delivery)' }
]
Total side effects applied: 3၅ မိနစ် စမ်းကြည့်
processedEvents store ကို event တစ်ခုစီအတွက် timestamp ပါ မှတ်တမ်းတင်အောင် ပြောင်းပြီး၊ နောက်ဆုံး ၂၄ နာရီအတွင်းက ID တွေကိုပဲ ထိန်းထားမယ့် cleanup အဆင့် ထပ်ထည့်ပါ။
သတိလေးတစ်ချက်
Event ID ကို မှတ်တမ်းမတင်ခင် side effect ကို process လုပ်ခြင်း — ကြားထဲမှာ server crash ဖြစ်ရင် နှစ်ခါ process ဖြစ်နိုင်ပါတယ်။
Event ID နဲ့ key လုပ်ထားတဲ့ ပြတ်သားတဲ့ processed-event check မထားဘဲ duplicate တွေကို ကာကွယ်ဖို့ သင့် request timeout ကိုပဲ မှီခိုခြင်း။
Idempotence — Wikipedia — API Integration & Webhooks