المستوى المطلوب: متوسط — هذا المقال يفترض أنك تعرف REST API، Redis كـ key-value store، وHTTP status codes الأساسية. لو لسه بتبني أول API بـ Python أو Node.js، اقرا أساسيات HTTP الأول.
Idempotency Keys: ازاي تخلّي endpoint الدفع آمن من التكرار
لو زبون ضغط على زر "ادفع" مرة، الشبكة قطعت قبل ما الـ response يوصل، الـ frontend عمل retry تلقائي، والنتيجة العميل اتسحبله المبلغ مرتين — المشكلة مش في bank gateway. السبب إن endpoint الدفع عندك مش idempotent. سطرين Redis قبل البزنس لوجيك بيقفلوا الباب ده نهائياً.
المشكلة باختصار
أي طلب POST بيغيّر state على السيرفر (دفع، إنشاء حجز، إرسال إيميل، إصدار فاتورة) لو اتنفّذ مرتين بيعمل effect مرتين. الشبكة بتفشل في الموبايل بنسبة معروفة، التطبيق بيعمل retry تلقائي، والنتيجة charge مكرر أو حجز ضايع أو إيميل خرج 4 مرات لنفس العميل. على workload فيه 10,000 طلب دفع يومياً، نسبة التكرار الطبيعي بسبب موبايل على 4G قياس Stripe بيلاقيها بين 0.4% و 0.9% — يعني من 40 لـ 90 شحنة مكررة كل يوم بدون حماية.
مثال يوضّح الفكرة (للمبتدئ تماماً)
تخيّل إنك بتطلب قهوة في كافيه. قلت للكاشير "كابتشينو واحد"، شبكة الـ POS عنده اتقطعت قبل ما يطبعلك الفاتورة. لو قلتله تاني "كابتشينو واحد" من غير ما يفتكر إنه سمعك أول مرة، هتاخد كوبين وتدفع تمن اتنين. لكن لو الكاشير معاه دفتر كل طلب فيه رقم مرجعي مكتوب على إيصال صغير، أول ما تقوله الرقم تاني هيرد عليك "آه ده طلبك الأولاني، فاتورتك جاهزة"، ومش هيكرّر العملية. الـ Idempotency Key هو الرقم المرجعي ده بالظبط.
التعريف العلمي بدقة
الـ Idempotency في الرياضيات: عملية f تبقى idempotent لو f(f(x)) = f(x). في APIs، الطلب يبقى idempotent لو إعادة إرساله بنفس المعرّف بترجع نفس النتيجة بدون أي side effect إضافي على السيرفر. RFC 9110 (HTTP Semantics) بيعرّفها كـ "method is idempotent if the intended effect on the server of multiple identical requests with that method is the same as the effect for a single such request". الـ GET وPUT وDELETE idempotent بطبيعتهم في الـ spec، لكن POST لأ — وده اللي خلّى Stripe والـ Payment Industry تخترع هيدر إضافي اسمه Idempotency-Key لتغطية الفجوة دي.
التطبيق العملي بـ FastAPI وRedis
الـ client بيولّد UUID v4 عند إنشاء الطلب لأول مرة، ويبعته في هيدر Idempotency-Key مع كل retry لنفس العملية. السيرفر بيستخدم Redis SETNX (set if not exists) كـ atomic lock. لو المفتاح موجود → رجّع الـ response المحفوظ من غير ما تلمس البزنس لوجيك. لو مش موجود → نفّذ مرة واحدة، احفظ النتيجة 24 ساعة، ورجّعها.