الأمان من التكرار وإزالة التكرار
الأمان من التكرار وإزالة التكرار
في أي نظام رسائل موزّع، قد تصل الرسالة الواحدة أكثر من مرة. يعيد المنتج الإرسال بعد انتهاء المهلة، أو يتعطل الوسيط في منتصف التأكيد، أو يتسبب انقسام الشبكة في تأكيد التسليم على جانب دون الآخر. هذه ليست حالات حافة — بل هي واقع يومي على نطاق واسع. الحل هو تصميم كل مستهلك ليكون آمناً من التكرار (idempotent) واستخدام إزالة التكرار (deduplication) حيثما لا يكفي الأمان من التكرار وحده.
ما معنى الأمان من التكرار؟
العملية آمنة من التكرار إذا أنتجت تنفيذها مرات عديدة نفس النتيجة التي ينتجها تنفيذها مرة واحدة تماماً. المصطلح مستمد من الرياضيات: تطبيق نفس الدالة مراراً يبقي الحالة كما هي بعد التطبيق الأول.
أمثلة عملية:
- آمن من التكرار: ضبط بريد المستخدم إلى
alice@example.com. تنفيذUPDATEعشر مرات يُبقي نفس الصف. - غير آمن من التكرار: خصم 10 دولارات من المحفظة. تنفيذ هذه العملية عشر مرات يخصم 100 دولار.
- آمن من التكرار: تحديد حالة طلب إلى
SHIPPED. الانتقال منSHIPPEDإلىSHIPPEDلا يغيّر شيئاً. - غير آمن من التكرار: زيادة عداد المشاهدات. كل رسالة مكررة تضخّم العدد.
مفاتيح الأمان من التكرار (Idempotency Keys)
النمط المعياري هو إرفاق مفتاح أمان فريد ومستقر بكل رسالة عند نقطة الإنشاء — عادةً UUID يولّده المنتج. يسجّل المستهلك المفاتيح التي عالجها بالفعل. عند استلام رسالة يتحقق: هل رأيت هذا المفتاح من قبل؟ إذا كانت الإجابة نعم، يؤكد الاستلام ويتجاهل الرسالة دون إعادة تنفيذ العملية.
يجب أن يكون المفتاح:
- فريداً عالمياً — UUID v4 أو مفتاح مركّب مثل
order_id:event_type:attempt. - مستقراً عبر محاولات الإعادة — يجب أن يرسل المنتج نفس المفتاح في كل إعادة محاولة للعملية المنطقية ذاتها، لا أن يولّد UUID جديداً في كل مرة.
- مخزناً بصورة دائمة — في Redis مع مهلة انتهاء، أو في جدول قاعدة بيانات، لتحمّل إعادة تشغيل المستهلك.
أين تُخزَّن المفاتيح المعالَجة؟
مخزن إزالة التكرار مكوّن حاسم. الخيارات الشائعة:
- Redis
SET NXمع TTL — سريع (دون ميلي ثانية)، مناسب للمفاتيح التي يمكن أن تنتهي صلاحيتها (مثلاً 24 ساعة). الأمرSET key 1 EX 86400 NXيُعيد 1 عند الكتابة الأولى (نفّذ) و0 عند التكرار (تجاهل). هذا هو الخيار الأكثر شيوعاً في الأنظمة عالية الإنتاجية. - قيد فريد في قاعدة البيانات — جدول بفهرس
UNIQUEعلى مفتاح إزالة التكرار.INSERT IGNOREأوON CONFLICT DO NOTHINGيمنع المعالجة المزدوجة ذرياً. أبطأ قليلاً لكنه دائم وترانزاكشني — يمكنك تحديث صف العمل وإدراج المفتاح في ترانزاكشن واحدة. - إزالة التكرار على مستوى الوسيط — بعض الوسطاء (AWS SQS FIFO، RabbitMQ مع إضافة Dedup) يقبلون
MessageDeduplicationIdويرفضون التكرار خلال نافذة زمنية (5 دقائق لـ SQS FIFO). هذا يُفرغ المنطق لكنه لا يحمي من الإعادات على مستوى التطبيق خارج تلك النافذة.
الذرية: مشكلة الإنفاق المزدوج
تنشأ مشكلة دقيقة لكن بالغة الأهمية إذا أجريت الفحص في مخزن إزالة التكرار وتنفيذ منطق العمل كخطوتين منفصلتين. بين هاتين الخطوتين قد تعالج خيط آخر نفس الرسالة. الحل هو فحص-وضبط ذري:
- مع Redis:
SET key 1 EX 86400 NXأمر ذري واحد — لا شرط للتسابق. - مع قاعدة بيانات علائقية: اغلف إدراج المفتاح وعملية العمل في ترانزاكشن واحدة. قيد الفرادة سيُفشل الترانزاكشن الثانية ويُرجع التراجع بشكل نظيف.
SELECT → قرار → INSERT كثلاث عبارات منفصلة بدون ترانزاكشن أو عملية ذرية. في ظل التحميل المتزامن، قد يجتاز مستهلكان كليهما فحص SELECT ويتابعان التنفيذ، مما ينتج تكراراً. هذا هو سباق التسابق الكلاسيكي TOCTOU.
جعل العمليات غير الآمنة آمنة من التكرار
حين لا تستطيع إعادة كتابة العملية الأساسية لتكون آمنة بطبيعتها (مثل تحصيل دفعة من بوابة دفع خارجية)، النمط هو التحصين بآلة حالة:
- قبل استدعاء الخدمة الخارجية، اكتب سجلاً في قاعدة البيانات بحالة
PENDINGومفتاح الأمان من التكرار، ضمن ترانزاكشن. - إذا فشلت الكتابة بتعارض مفتاح فريد، فعامل آخر بدأ أو أكمل العملية — توقف وأعد.
- استدعِ الخدمة الخارجية، ثم حدّث السجل إلى
COMPLETEمع النتيجة. - عند أي عطل بين الخطوتين 2 و3، تجد وظيفة الاسترداد صف
PENDINGوتعيد المحاولة — مُرسلةً نفس مفتاح الأمان إلى API الخارجية (Stripe وPayPal وغيرهما يدعمون هذا) فتقوم بوابة الدفع بإزالة التكرار من جهتها.
TTL وانتهاء صلاحية المفاتيح
لا تحتاج مفاتيح إزالة التكرار إلى البقاء إلى الأبد. اختر TTL يغطي نافذة الإعادة مع هامش مريح. إذا كانت سياسة الإعادة تستسلم بعد ساعة واحدة بالتراجع الأسي، فإن TTL بمدة 24 ساعة آمن. لعمليات الدفع التي قد يطعن فيها العميل بعد أيام، احتفظ بالمفاتيح لمدة 7 إلى 30 يوماً. عند انتهاء صلاحية المفاتيح، تقبل أن تُعالَج رسالة تُعاد إرسالها بعد النافذة مجدداً — تأكد من أن ذلك مقبول لحالة استخدامك، أو استخدم سجلات قاعدة بيانات دائمة للعمليات ذات المخاطر العالية.
قائمة مراجعة عملية
- كل منتج يُسند مفتاح UUID من الأمان من التكرار مرة واحدة ويعيد استخدامه في جميع محاولات الإعادة.
- كل مستهلك يفحص المفتاح ذرياً قبل تنفيذ العملية الجانبية.
- يستخدم مخزن إزالة التكرار عملية ذرية (
SET NXأو قيد فريد). - يحدث منطق العمل وتسجيل المفتاح في نفس الترانزاكشن حيثما أمكن.
- يكون TTL طويلاً بما يكفي لتغطية نافذة الإعادة الكاملة بزيادة.
- تستقبل الخدمات الخارجية (بوابات الدفع، مزودو البريد الإلكتروني) نفس المفتاح لإزالة التكرار باستقلالية.