مدیریت ریترایها در لاراول با Idempotency
خلاصهٔ کاملتر
وقتی کاربر روی دکمه پرداخت دوبار کلیک میکنه، یا موبایل کلاینت جواب سرور رو دریافت نمیکنه و درخواست رو دوباره میفرسته، یا یه صف کاری بعد از timeout دوباره API رو صدا میزنه — اگه سرور برای این سناریوها آماده نباشه، میتونه به دو سفارش، دو پرداخت، یا ایمیلهای تکراری ختم بشه. ایدمپوتنسی (Idempotency) راهحل اینه: اگه کلاینت همون درخواست رو با همون کلید تکرار کنه، سرور باید همون پاسخ اول رو برگردونه و عملیات رو دوباره اجرا نکنه.
پکیج wendelladriel/laravel-idempotency این مکانیزم رو برای لاراول پیادهسازی میکنه. یه کلید ایدمپوتنسی (Idempotency Key) یه شناسه منحصربهفرده که کلاینت توی هدر درخواست میفرسته و نشون میده «این درخواست مربوط به کدوم عملیات خاصه». پکیج از این کلید استفاده میکنه تا بفهمه آیا این یه درخواست جدیده یا ریترای.
یه نکته مهم: ایدمپوتنسی فقط تشخیص تکراری نیست. اگه کلاینت همون کلید رو با دادههای متفاوت بفرسته (مثلاً روش ارسال رو از standard به express تغییر بده)، پکیج باید با خطای 422 رد کنه، نه اینکه پاسخ قبلی رو پخش کنه. برای این کار از اثرانگشت درخواست (Request Fingerprint) استفاده میشه که از متد، مسیر، پارامترها، و بدنه درخواست ساخته میشه. اگه کلید یکسان ولی اثرانگشت متفاوت بود، درخواست رد میشه.
برای مقابله با درخواستهای همزمان — که هر دو قبل از ذخیره شدن پاسخ به سرور میرسن — پکیج از قفل اتمیک لاراول استفاده میکنه. اولین درخواست قفل رو میگیره؛ اگه درخواست مشابهی در همون لحظه برسه، 409 Conflict با هدر Retry-After: 1 برمیگرده. این بخش در محیط production اختیاری نیست و نیاز داره همه سرورها یه cache store مشترک داشته باشن.
سادهترین روش استفاده، اضافه کردن middleware به route هاست:
Route::post('/orders', StoreOrderController::class)
->name('orders.store')
->middleware(Idempotent::class);اگه یه endpoint نیاز به تنظیمات خاص داره — مثلاً TTL کوتاهتر یا scope متفاوت — میشه اینها رو مستقیماً روی همون route تعریف کرد:
Route::post('/payments', ChargePaymentController::class)
->middleware(Idempotent::using(
ttl: 600,
lockTimeout: 30,
scope: IdempotencyScope::Ip,
header: 'X-Idempotency-Key',
));علاوه بر middleware، میشه از PHP Attribute هم روی کنترلر یا متدهای خاص استفاده کرد. این روش وقتی چند action نوشتاری توی یه کنترلر داری خواناتره. پکیج سه scope داره: user (بر اساس کاربر لاگینکرده، با fallback به IP)، ip (بر اساس IP کلاینت)، و global (بدون تفکیک). انتخاب scope درست مهمه؛ مثلاً webhook ها اغلب به global scope نیاز دارن چون event ID خودش منحصربهفرده.
نکات کلیدی:
- ایدمپوتنسی یعنی اجرای چندباره یه عملیات نتیجه یکسانی داشته باشه
- پکیج برای متدهای POST، PUT، و PATCH طراحی شده
- کلید ایدمپوتنسی + اثرانگشت با هم تشخیص میدن آیا این ریترای واقعیه یا یه عملیات متفاوت
- قفل اتمیک از پردازش موازی درخواستهای همزمان جلوگیری میکنه
- پاسخهای replay با هدر Idempotency-Replayed: true مشخص میشن
- scope، TTL، و header برای هر route قابل تنظیم مجزاست




