یکی از مشکلاتی که تقریبا در هر سیستم بکاند واقعی دیر یا زود ظاهر می شود، «دوبارهکاری» است. کاربر دکمه پرداخت را دو بار می زند، شبکه قطع میشود و کلاینت retry می کند، یا یک queue message دوباره تحویل داده می شود. نتیجه؟ سفارش تکراری، شارژ دوباره، یا ثبت دو رکورد یکسان.
اینجاست که Idempotency وارد می شود. یعنی عملیات را طوری طراحی کنید که اگر چند بار اجرا شود، نتیجه نهایی دقیقا مثل یک بار اجرا باشد.
بسیاری از تیم ها فکر میکنند فقط endpointهای پرداخت نیاز به این ویژگی دارند. واقعیت این است که هر عملیاتی که side-effect دارد (ایجاد سفارش، ارسال ایمیل، آپدیت موجودی، ثبت تراکنش) باید idempotent باشد. بدون آن، با اولین مشکل شبکه یا retry خودکار، سیستم شما رفتار غیرقابل پیش بینی نشان می دهد.
راهکار عملی ساده است:
کلاینت یک Idempotency-Key یکتا (معمولاً UUID) همراه درخواست ارسال می کند.
سرور قبل از اجرای عملیات واقعی، این کلید را چک می کند. اگر قبلا دیده شده باشد، همان پاسخ قبلی را برمی گرداند و عملیات را دوباره اجرا نمی کند.
کلید را معمولا برای ۲۴ تا ۷۲ ساعت در Redis یا دیتابیس نگه می دارید و بعد منقضی می کنید.
در عمل این کار را میتوانید با یک middleware یا interceptor ساده پیاده کنید. مهم این است که کلید را در لایه business logic چک کنید، نه فقط در سطح HTTP. چون ممکن است همان عملیات از مسیرهای مختلف (API، queue، webhook) فراخوانی شود.
تیم هایی که این الگو را جدی می گیرند، تعداد incidentهای مربوط به داده های تکراری و شارژهای اشتباه را به طور محسوسی کاهش می دهند. در مقابل آن تیم هایی که آن را به حال «بعدا اضافه میکنیم» می گذارند، معمولا بعد از اولین مشکل واقعی مجبور میشوند عجله ای و ناقص آن را پیاده سازی کنند.
اگر الان در حال طراحی یا بازنگری APIهای حساس هستید، همین امروز از خودتان بپرسید: «اگر این درخواست دو بار برسد، چه اتفاقی می افتد؟» اگر جوابش «مشکل ساز می شود» است، وقت اضافه کردن Idempotency-Key است.
این یک تغییر کوچک در طراحی است، اما تاثیر بزرگی روی پایداری و اعتماد سیستم دارد.
