Idempotency یعنی اگر یک درخواست یکسان چند بار به API ارسال شود، اثر نهایی روی سیستم فقط یکبار اعمال شود و تکرار همان درخواست باعث ایجاد دادهی تکراری، عملیات تکراری یا تغییر وضعیت ناخواسته نشود.
به بیان سادهتر:
اگر یک Request به دلیل timeout، retry، network issue، double click کاربر یا ارسال مجدد از سمت Client دوباره فرستاده شود، سیستم باید بتواند تشخیص دهد این همان درخواست قبلی است و دوباره همان عملیات را تکرار نکند.
در سیستمهای واقعی، مخصوصاً بانکی، مالی، پرداخت، انتقال وجه، ثبت سفارش و ایجاد مشتری، تکرار ناخواستهی یک درخواست میتواند فاجعه بسازد.
مثلاً:
یک تراکنش دوبار ثبت شود
یک مشتری دوبار ساخته شود
یک پرداخت دوبار از حساب کم شود
یک درخواست ثبت وام دوبار پردازش شود
Idempotency دقیقاً برای جلوگیری از همین duplicate processing و double execution است.
اگر یک Operation را با ورودی یکسان چند بار اجرا کنیم، نتیجهی نهایی سیستم باید معادل اجرای یکبار آن Operation باشد.
در API Testing، Idempotency یعنی تکرار یک Request نباید باعث ایجاد رکورد تکراری، تراکنش تکراری یا تغییر وضعیت غیرمنتظره شود.
خیلی زیاد.
در سیستمهای توزیعشده یا حتی در APIهای معمولی، این سناریو زیاد پیش میآید:
Client درخواست را میفرستد
Server عملیات را انجام میدهد
ولی Response به Client نمیرسد، چون timeout یا network issue رخ میدهد
Client فکر میکند درخواست fail شده و دوباره همان Request را میفرستد
اگر API Idempotent نباشد، ممکن است همان عملیات دوباره انجام شود.
پس Idempotency جلوی آسیب ناشی از retry را میگیرد.
بیشترین اهمیت را در این سناریوها دارد:
Transfer Money
Payment
Create Order
Create Customer
Submit Application
Create Invoice
Reserve Seat / Booking
هر جایی که Create یا Business Transaction داریم و تکرار آن خطرناک است
اگر یک درخواست را چند بار تکرار کنیم، نتیجهی نهایی همان نتیجهی اجرای اول است.
مثال:
چند بار یک وضعیت را روی inactive بگذاری و هر بار همان inactive بماند
یک PUT که هر بار دقیقاً همان داده را جایگزین میکند
اگر تکرار درخواست باعث تغییر جدید، رکورد جدید یا اثر جدید شود.
مثال:
هر بار POST /payments یک پرداخت جدید بسازد
هر بار POST /customers یک مشتری جدید ثبت کند
هر بار POST /transfer دوباره از حساب کم کند
در طراحی RESTful API معمولاً از نظر مفهومی اینطور نگاه میشود:
باید Idempotent باشد.
چند بار صدا زدن GET نباید state سیستم را تغییر دهد.
معمولاً Idempotent است.
چون قرار است یک Resource را با یک representation مشخص جایگزین کند. اگر همان PUT را چند بار با همان Body بفرستی، نتیجهی نهایی نباید فرق کند.
از نظر مفهومی Idempotent در نظر گرفته میشود.
چون اگر Resource یک بار حذف شده باشد، حذف دوباره نباید اثر جدیدی بسازد. ممکن است بار دوم 404 بدهد یا پاسخ دیگری، ولی نباید دوباره اثر بیزینسی جدیدی ایجاد کند.
بهطور پیشفرض Idempotent نیست.
چون معمولاً برای Create یا Action استفاده میشود و هر بار میتواند اثر جدیدی بسازد.
برای همین در POSTها، مخصوصاً عملیات مالی، Idempotency را معمولاً با مکانیزمهای اضافی پیادهسازی میکنند.
رایجترین راه، استفاده از Idempotency Key است.
یک شناسهی یکتا است که Client همراه Request میفرستد تا Server بتواند بفهمد این درخواست قبلاً پردازش شده یا نه.
معمولاً در Header میآید، مثلاً:
Idempotency-Key: 8f6d9b1a-45e2-4c3a-9e11-abc123xyz
یا گاهی:
X-Idempotency-Key: 8f6d9b1a-45e2-4c3a-9e11-abc123xyz
فرض کن Client میخواهد انتقال وجه انجام دهد:
POST /transfers Idempotency-Key: 12345-abc Content-Type: application/json
{ "fromAccount": "1001", "toAccount": "2001", "amount": 500000 }
Server این درخواست را میگیرد، Idempotency Key را ذخیره میکند، عملیات را انجام میدهد و Response برمیگرداند.
Server باید تشخیص دهد این درخواست قبلاً پردازش شده و نباید دوباره انتقال وجه انجام شود.
در عوض باید:
یا همان نتیجهی قبلی را برگرداند
یا بهصورت کنترلشده بگوید این Request قبلاً پردازش شده
گرچه Idempotency-Key رایجترین روش است، اما تنها راه نیست.
گاهی سیستم با اینها هم جلوی duplicate را میگیرد:
Unique business key
Database unique constraint
Request fingerprint
Deduplication logic
Transaction lock
Message deduplication در سیستمهای async
اما از دید تست API، مهمترین چیزی که زیاد با آن روبهرو میشوی Idempotency-Key است.
فرض کن یک POST /payments با یک Idempotency-Key مشخص ارسال شده و موفق بوده. حالا همان Request دوباره ارسال میشود.
رفتار درست معمولاً یکی از اینهاست:
همان نتیجهی قبلی را برگرداند
بگوید این درخواست قبلاً پردازش شده
به هیچوجه پرداخت جدید یا رکورد جدید نسازد
نکته مهم این است که state سیستم دوباره تغییر نکند.
فرض کن کاربر روی دکمهی «انتقال وجه» دوبار کلیک میکند یا اپلیکیشن بهخاطر timeout دوباره درخواست را میفرستد.
Request:
POST /transfers Idempotency-Key: transfer-9981
{ "fromAccount": "1111", "toAccount": "2222", "amount": 1000000 }
اگر API Idempotency نداشته باشد، ممکن است:
دوبار از حساب کم شود
دوبار سند حسابداری ثبت شود
دوبار notification ارسال شود
اگر Idempotency درست پیاده شده باشد:
فقط یک تراکنش واقعی ثبت میشود
درخواست دوم اثر جدیدی ندارد
این دو به هم نزدیکاند، ولی یکی نیستند.
یعنی سیستم بررسی کند مثلاً email یا nationalCode تکراری ثبت نشود.
یعنی همان درخواست تکراری دوباره باعث اجرای عملیات نشود.
مثلاً:
Duplicate Validation: نگذارد دو مشتری با یک nationalCode ساخته شوند
Idempotency: نگذارد همان درخواست ثبت مشتری به خاطر retry دوبار اجرا شود
ممکن است هر دو لازم باشند.
در سیستمهای بانکی فقط retry مهم نیست؛ همزمانی هم مهم است.
مثلاً اگر دو درخواست تقریباً همزمان با یک Idempotency-Key برسند، سیستم باید طوری طراحی شده باشد که هر دو را بهعنوان دو عملیات مستقل پردازش نکند.
پس Idempotency فقط یک validation ساده نیست؛ بخشی از system design، transaction control و concurrency handling هم هست.
Request اول موفق شود
Request دوم با همان Key و همان Body ارسال شود
سیستم نباید رکورد جدید بسازد
سیستم نباید تراکنش جدید انجام دهد
Response باید مطابق Contract باشد
این خیلی مهم است.
اگر همان Idempotency-Key را با payload متفاوت بفرستی، رفتار سیستم باید مشخص و کنترلشده باشد. معمولاً این حالت باید خطا بدهد، چون یک Key واحد نباید برای دو عملیات متفاوت استفاده شود.
اگر API طبق Contract برای عملیات حساس به Idempotency-Key نیاز دارد، نبودن آن باید رفتار مشخصی داشته باشد.
حتی اگر دستی timeout را شبیهسازی نکنی، باید سناریوی منطقیاش را در نظر بگیری:
آیا درخواست دوم باعث double processing میشود یا نه؟
برای تست Idempotency فقط Status Code کافی نیست. باید در Database هم چک کنی:
فقط یک رکورد ساخته شده؟
فقط یک تراکنش ثبت شده؟
فقط یک سند/لاگ/مرجع بیزینسی ساخته شده؟
در تست Idempotency باید اینها را نگاه کنی:
Status Code
Response Body
Reference Number / Transaction Id
DB state
Logs / audit trail اگر دسترسی داری
مثلاً اگر بار اول transactionId=TX1001 ساخته شد، درخواست دوم با همان Idempotency-Key نباید TX1002 بسازد.
اینجا یک پاسخ ثابت جهانی وجود ندارد؛ بستگی به Contract دارد.
اما معمولاً این حالتها را میبینی:
200 OK
201 Created
ممکن است:
همان 200/201 قبلی برگردد
یا پاسخ دیگری که نشان دهد request قبلاً پردازش شده
مهم این است که اثر بیزینسی تکرار نشود.
مثلاً اگر:
یک POST /transfer با یک Idempotency-Key مشخص ارسال شود
دوباره همان Request با همان Key ارسال شود
و سیستم دوباره انتقال وجه انجام دهد
این یک Failure است، چون Actual Result با Expected Result برابر نیست.
پشت این Failure معمولاً یکی از این Defectهاست:
عدم پیادهسازی Idempotency check
ذخیرهنشدن Idempotency-Key
race condition در پردازش همزمان
ضعف در transaction boundary
مشکل در unique constraint یا deduplication logic
باید بدانی کدام methodها ذاتاً idempotent هستند و کدامها نه.
چون Idempotency-Key معمولاً در Header میآید.
چون باید رفتار پاسخ در request اول و دوم را تحلیل کنی.
چون بدون بررسی دیتابیس، تست idempotency ناقص است.
چون تکرار درخواست فقط از سمت کاربر نیست؛ ممکن است از retryهای سیستمی، message redelivery یا timeout هم بیاید.
Backend Tester باید بتواند:
تشخیص دهد کدام APIها نیاز جدی به Idempotency دارند
برای عملیات مالی، create و transaction-based سناریوی تکرار Request طراحی کند
تفاوت duplicate validation و idempotency را بداند
هم Response و هم Database state را بررسی کند
Idempotency-Key، retry، timeout و concurrency risk را در تحلیل تست لحاظ کند
در پاسخ امتحانی فقط نگویید:
Idempotency یعنی درخواست تکراری دوباره اثر نگذارد.
این تعریف ناقص است. پاسخ خوب باید این کلیدواژهها را داشته باشد:
same request, same effect, retry, duplicate processing, Idempotency-Key, POST operations, financial transactions, double execution, database verification, concurrency risk
Idempotency یعنی اگر یک Request یکسان چند بار به API ارسال شود، اثر نهایی آن روی سیستم معادل اجرای یکبار همان Request باشد و تکرار درخواست باعث ایجاد دادهی تکراری، تراکنش تکراری یا تغییر وضعیت ناخواسته نشود. این مفهوم در APIهای مالی، پرداخت، انتقال وجه و عملیات POST بسیار مهم است، چون ممکن است به دلیل timeout یا retry یک درخواست دوباره ارسال شود. برای پیادهسازی آن معمولاً از Idempotency-Key در Header استفاده میشود تا Server تشخیص دهد Request قبلاً پردازش شده است. در تست Idempotency باید علاوه بر Status Code، Response Body و Database state هم بررسی شود تا مطمئن شویم عملیات دوباره اجرا نشده است.
Idempotency Idempotent Operation Non-Idempotent Operation Retry Duplicate Processing Double Execution Idempotency-Key POST PUT DELETE GET Timeout Network Retry Request Replay Duplicate Request Database Verification Unique Constraint Deduplication Concurrency Race Condition Transaction Financial Operation
Idempotency (ویژگیای که باعث میشود تکرار یک Request یکسان اثر بیزینسی جدید ایجاد نکند): برای جلوگیری از پردازش تکراری در APIهای حساس استفاده میشود.
Idempotent Operation (عملیاتی که اجرای چندبارهی آن با ورودی یکسان، نتیجه نهایی یکسان دارد): در تحلیل رفتار GET، PUT و DELETE اهمیت دارد.
Non-Idempotent Operation (عملیاتی که تکرار آن اثر جدید ایجاد میکند): در POSTهای مالی، create و transaction-based زیاد دیده میشود.
Retry (ارسال مجدد Request بعد از timeout، failure یا عدم دریافت پاسخ): یکی از مهمترین دلایل نیاز به Idempotency است.
Duplicate Processing (پردازش تکراری یک عملیات واحد): در سیستمهای مالی و ثبت اطلاعات ریسک جدی ایجاد میکند.
Double Execution (اجرای دوبارهی یک عملیات بهجای تشخیص تکراری بودن آن): نشانهی failure در Idempotency است.
Idempotency-Key (شناسه یکتایی که همراه Request ارسال میشود تا Server بتواند درخواست تکراری را تشخیص دهد): رایجترین روش پیادهسازی Idempotency در POSTهای حساس است.
POST (متدی که معمولاً بهطور پیشفرض idempotent نیست): برای عملیات create و business action نیاز بیشتری به Idempotency دارد.
PUT (متدی که معمولاً idempotent در نظر گرفته میشود): تکرار همان درخواست نباید state نهایی را تغییر دهد.
DELETE (متدی که از نظر مفهومی idempotent است): حذف دوباره نباید اثر بیزینسی جدید ایجاد کند.
GET (متد خواندن داده که نباید state سیستم را تغییر دهد): بهطور طبیعی باید idempotent باشد.
Timeout (عدم دریافت پاسخ در زمان مشخص): یکی از علتهای رایج retry و تکرار Request است.
Network Retry (ارسال مجدد درخواست به دلیل اختلال شبکه یا timeout): سناریوی کلیدی برای تست Idempotency است.
Request Replay (ارسال مجدد همان Request قبلی): اگر کنترل نشود میتواند منجر به duplicate transaction شود.
Duplicate Request (درخواست تکراری با همان محتوا یا همان intent بیزینسی): در طراحی تست Idempotency باید بررسی شود.
Database Verification (بررسی دیتابیس برای اطمینان از عدم ثبت رکورد یا تراکنش تکراری): بخش ضروری تست Idempotency است.
Unique Constraint (قید یکتایی در دیتابیس): یکی از لایههای کمکی برای جلوگیری از دادهی تکراری است، ولی جایگزین کامل Idempotency نیست.
Deduplication (منطق تشخیص و حذف درخواست یا پیام تکراری): در معماری API و سیستمهای توزیعشده برای جلوگیری از duplicate processing استفاده میشود.
Concurrency (پردازش همزمان چند درخواست): در سناریوهای تکرار همزمان Request میتواند باعث شکستن Idempotency شود.
Race Condition (شرایط رقابتی در پردازش همزمان): یکی از دلایل فنی failure در پیادهسازی Idempotency است.
Transaction (واحد عملیاتی بیزینسی یا دیتابیسی): در انتقال وجه، پرداخت و عملیات حساس باید در برابر duplicate execution محافظت شود.
Financial Operation (عملیات مالی مثل پرداخت یا انتقال وجه): مهمترین حوزهی کاربرد Idempotency به دلیل ریسک بالای تکرار عملیات است.
Request Replay (ارسال مجدد همان درخواست قبلی): در سناریوهای timeout، retry و تست Idempotency برای تشخیص پردازش تکراری استفاده میشود.
Replay Result (برگرداندن نتیجهی قبلی به درخواست تکراری بهجای اجرای مجدد عملیات): در رفتار صحیح APIهای idempotent بهکار میرود.
Duplicate Processing (پردازش دوبارهی یک عملیات واحد): ریسک اصلی در انتقال وجه، پرداخت و ثبت تراکنش است.
Request Fingerprint (نمایه/اثر انگشت منطقی یک درخواست شامل payload یا فیلدهای مهم آن): برای مقایسهی same key + same body در برابر same key + different body استفاده میشود.
Atomic Check-and-Save (بررسی و ثبت یک داده در یک عملیات اتمیک و غیرقابلتفکیک): در پیادهسازی امن Idempotency-Key برای جلوگیری از race condition حیاتی است.
Idempotency Store (محل ذخیرهسازی mapping بین Idempotency-Key و نتیجه/تراکنش قبلی): در تحلیل معماری و RCA مربوط به replay و duplicate transaction کاربرد دارد.
Transaction Reference / Reference Number (شناسهی بیزینسی یا بانکی یک تراکنش): در تست idempotency باید چک شود که در replay همان reference قبلی برگردد، نه reference جدید.
Ledger Entry (رکورد حسابداری بدهکار/بستانکار مرتبط با تراکنش): در تست بانکی برای اطمینان از اینکه فقط یک debit و یک credit ثبت شده استفاده میشود.
Contract Enforcement (اعمال دقیق قواعد تعریفشده در contract API): برای سناریوهایی مثل same idempotency key + different body یا missing required header مهم است.
Business Intent (قصد بیزینسی واقعی یک درخواست، مثل یک انتقال وجه مشخص): در Idempotency مهم است چون سیستم باید تشخیص دهد replay همان intent قبلی است یا یک عملیات جدید.