ایدمپوتنسی واقعی: فراتر از یک کش ساده
خلاصهٔ کاملتر
خیلیها ایدمپوتنسی رو اینطوری توصیف میکنن: یه Idempotency-Key بذار توی هدر، response رو ذخیره کن، و روی retry همون رو برگردون. برای مسیر معمول (happy path) این کافیه. اما مقاله استدلال میکنه که این بخش آسون ماجراست؛ بخش سخت از درخواست دوم شروع میشه، چون retry همیشه یه replay ساده نیست.
سناریوهایی که یه replay cache نمیتونه جوابشون رو بده اینان: وقتی درخواست اول هنوز داره اجرا میشه و درخواست دوم میرسه (مشکل concurrency)؛ وقتی دادهی local ثبت شده ولی event منتشر نشده (partial success)؛ وقتی سرویس خارجی پرداخت رو قبول کرده ولی سیستم قبل از ثبت نتیجه کرش کرده؛ یا وقتی همون کلید با محتوای متفاوت میرسه — مثلاً همون key ولی مبلغ ۱۰ یورو به ۱۰۰ یورو تغییر کرده.
نویسنده معتقده برای APIهایی که side effect دارن، اگه کلید یکسانه ولی محتوای درخواست فرق داره، باید با خطای صریح رد بشه. این کار باگهای سمت کلاینت رو زود لو میده. یه کلاینت که فکر میکنه داره یه پرداخت ۱۰ یورویی رو retry میکنه، نباید سرور بیسروصدا درخواستش رو طور دیگهای تفسیر کنه.
برای اینکه ایدمپوتنسی درست کار کنه، باید یه رکورد دائمی نگه داری که به سه سوال جواب بده: این کلید متعلق به کیه؟ اولین دستور دقیقاً چی بود؟ و چه نتیجهای رو میشه replay کرد؟ یه نمونهی minimal از این جدول در PostgreSQL اینه:
create table idempotency_requests
(
tenant_id text not null,
operation_name text not null,
idempotency_key text not null,
request_hash text not null,
status text not null,
response_status int,
response_body jsonb,
resource_type text
);نکات کلیدی:
- ایدمپوتنسی یعنی اثر عملیات یکسان باشه، نه فقط response یکسان برگرده
- یه replay cache فقط retry کامل و موفق رو مدیریت میکنه
- retry همزمان، موفقیت جزئی، و کرش بین call خارجی و ثبت نتیجه از مشکلات جدی هستن
unique constraintروی دیتابیس به تنهایی کافی نیست؛ کلاینت باید بدونه عملیات موفق بوده یا نه- کلید یکسان با محتوای متفاوت باید به عنوان خطا رد بشه، نه بیسروصدا پردازش بشه




