المعالجة غير المتزامنة والمراسلة

ضمانات التسليم: مرة واحدة كحد أقصى، ومرة واحدة على الأقل، ومرة واحدة بالضبط

18 دقيقة الدرس 5 من 10

ضمانات التسليم: مرة واحدة كحد أقصى، ومرة واحدة على الأقل، ومرة واحدة بالضبط

كل نظام مراسلة موزّع يجب أن يجيب عن سؤال جوهري واحد: حين يُرسل المُنتِج رسالةً، كم مرةً سيستقبلها المُستهلك ويعالجها؟ الإجابة ليست "مرةً واحدةً بالضبط" بصفة افتراضية تقريباً في أي وقت — بل تعتمد على طيف من الضمانات، لكلٍّ منها مُقايضاته الملموسة من حيث زمن الاستجابة والإنتاجية والتعقيد والصحة. الخطأ هنا قد يعني ضياع معاملات مالية، أو إرسال تأكيدات طلبات مكرّرة، أو بيانات تحليلية تالفة.

ثمة ثلاثة دلالات قياسية للتسليم. فهمها — ومعرفة أيّها تحتاج فعلاً — هو أحد أهم القرارات في تصميم الأنظمة غير المتزامنة.

التسليم مرة واحدة كحد أقصى (At-Most-Once)

يُطلق الوسيط الرسالةَ وينسى أمرها. تُسلَّم الرسالة صفراً أو مرةً واحدة — لن تُسلَّم مرتين أبداً، لكنها قد تضيع كلياً. يُرسل المُنتِج الرسالة دون انتظار تأكيد، ويتخلى الوسيط عنها فور محاولة التسليم الأولى بصرف النظر عن نجاح المُستهلك.

أين يظهر: قياسات الأداء عبر UDP، شحن السجلات دون انتظار استجابة، مقاييس الوقت الفعلي (حين يُقبل فقدان نقطة بيانات واحدة)، قراءات أجهزة استشعار إنترنت الأشياء حيث البيانات القديمة عديمة القيمة.

المقايضات: أدنى زمن استجابة، أعلى إنتاجية، وصفر تكلفة لإزالة التكرار. لكن فقدان البيانات احتمال حقيقي عند أي اضطراب شبكي أو عطل في المُستهلك. استخدم هذا الأسلوب فقط حين يكون فقدان الرسائل أرخص من تكلفة الموثوقية.

مثال واقعي: خط أنابيب تحليلات لعبة يُصدر 500,000 حدث في الثانية لبيانات سلوك اللاعبين. فقدان 0.1% من تلك الأحداث في حالة ازدحام شبكي له تأثير ضئيل على التقارير التجميعية. تكلفة التأكيد وإعادة المحاولة ستُخفّض الإنتاجية إلى النصف دون فائدة عملية.

التسليم مرة واحدة على الأقل (At-Least-Once)

يُعيد الوسيط المحاولة حتى يتلقى تأكيداً. تُسلَّم الرسالة مرةً أو أكثر — لا تضيع بيانات، لكن التكرار ممكن. يجب على المُستهلك أن يُقرّ (ACK) كل رسالة صراحةً. إن لم يتلقَّ الوسيط تأكيداً خلال مهلة زمنية، يُعيد وضع الرسالة في قائمة الانتظار ويُسلّمها مجدداً.

أين يظهر: RabbitMQ مع تفعيل ack، وKafka مع acks=all وعدم الالتزام بإزاحة المُستهلك إلا بعد اكتمال المعالجة، وقوائع انتظار SQS القياسية، ومعظم قوائع مهام الشركات.

المقايضات: ضمان متانة قوي — تصمد الرسائل أمام أعطال الوسيط والمُستهلك. لكن التكرار حتمي: قد تتسبب مهل الشبكة في إعادة التسليم حتى بعد أن يكون المُستهلك قد عالج الرسالة بنجاح (وعطل قبل إرسال التأكيد). يجب أن يكون منطق مُستهلكك مُتكافئاً (idempotent) (يُغطى في الدرس 6).

مثال واقعي: خدمة معالجة طلبات. إن تعطّل المُستهلك بعد خصم بطاقة الائتمان لكن قبل إرسال التأكيد، يُعيد الوسيط التسليم. على المُستهلك اكتشاف التكرار (مثلاً بالتحقق مما إذا كان الطلب بذلك المعرّف موجوداً بالفعل) بدلاً من خصم البطاقة مرة ثانية.

Three delivery guarantee semantics compared At-Most-Once At-Least-Once Exactly-Once Producer Broker Consumer send deliver (maybe) ⚠ may lose no duplicate Producer Broker Consumer send + ACK deliver 1+ ✓ no loss ⚠ duplicates Producer Broker (+ dedup store) Consumer (transactional) idempotent send deliver exactly 1 ✓ no loss ✓ no duplicate
مقارنة جنباً إلى جنب للدلالات الثلاث — تُظهر الأسهم ما يستقبله المُستهلك لرسالة واحدة أرسلها المُنتِج.

التسليم مرة واحدة بالضبط (Exactly-Once)

الهدف المنشود: تُسلَّم الرسالة وتُعالَج مرةً واحدةً بالضبط، بصرف النظر عن إعادات المحاولة والأعطال وإخفاقات الشبكة. لا تضيع بيانات ولا تظهر تكرارات. في الممارسة العملية يُنفَّذ هذا عبر مزيج من المُنتِجين المتكافئين وإزالة التكرار على مستوى الوسيط والمُستهلكين الترانزاكشينيين.

كيف يُنفّذه Kafka: تستخدم دلالات exactly-once في Kafka ثلاث آليات معاً:

  1. المُنتِج المتكافئ — كل رسالة تحصل على رقم تسلسلي؛ يُزيل الوسيط التكرار في الرسائل المُعاد إرسالها ضمن جلسة واحدة.
  2. المعاملات (Transactions) — يمكن للمُنتِج الكتابة ذرياً إلى تقسيمات متعددة؛ إما تُكتب جميع البيانات أو لا تُكتب أيّها.
  3. ذرية القراءة-المعالجة-الكتابة — يُلتزم المُستهلك بإزاحته والمخرجات المعالجة في معاملة ذرية واحدة، فيترك أي عطل أثناء المعالجة النظامَ في حالة متسقة.

المقايضات: التسليم مرة بالضبط أبطأ بنسبة 20-30% من "مرة على الأقل" في معايير Kafka بسبب عبء الالتزام ثنائي المرحلة. يتطلب أيضاً مشاركة خط الأنابيب بأكمله — فإن كتب المُستهلك إلى قاعدة بيانات خارجية لا تدعم المعاملات الموزّعة، يصبح التسليم الحقيقي مرة بالضبط مستحيلاً عند تلك الحدود.

مثال واقعي: دفتر أستاذ مالي يستهلك من Kafka. كل حدث يمثّل خصماً أو إيداعاً. تسليمه مرتين سيُفسد أرصدة الحسابات؛ فقدانه سيُسبّب تفاوتات غير مُفسَّرة. Kafka EOS مع قاعدة بيانات ترانزاكشينية (PostgreSQL) هو الحل الصحيح.

فكرة جوهرية: "التسليم مرة واحدة بالضبط" كضمان على مستوى الشبكة مستحيل نظرياً دون تنسيق. ما تسمّيه الأنظمة الحديثة "exactly-once" هو في الحقيقة تسليم at-least-once + معالجة متكافئة — قد يُسلِّم الوسيط أكثر من مرة، لكن المُستهلك مُصمَّم بحيث معالجة الرسالة ذاتها مرتين تُنتج النتيجة ذاتها كمعالجتها مرة واحدة.

كيف تُولِّد الأعطال التكرار — فجوة التأكيد (ACK Gap)

السبب الأكثر شيوعاً للتكرار في دلالات "مرة على الأقل" هو فجوة التأكيد: النافذة الزمنية بين انتهاء المُستهلك من المعالجة وإرساله التأكيد للوسيط.

The ACK gap: crash between processing and acknowledgment causes re-delivery time Broker delivers t=0 Consumer processes ✓ t=50ms CRASH before ACK sent t=80ms Broker re-delivers (timeout expired) t=30s ACK gap duplicate window
فجوة التأكيد: يعالج المُستهلك الرسالة في t=50ms ثم يتعطّل في t=80ms قبل إرسال التأكيد. يُعيد الوسيط التسليم في t=30s مُسبِّباً تكراراً.

هذه الفجوة لا يمكن تجنّبها في الأنظمة الموزّعة. الحل ليس القضاء على الفجوة بل جعل مُستهلكك متكافئاً (idempotent) — مُصمَّماً بحيث معالجة الرسالة ذاتها مرتين له الأثر ذاته لمعالجتها مرةً واحدة. التكافؤ يُعالَج بعمق في الدرس 6.

اختيار الضمان المناسب

اختَر بناءً على ما يتحمّله عملك التجاري:

  • At-most-once — استخدمه حين يكون فقدان الرسائل مقبولاً وتكون الإنتاجية أو زمن الاستجابة هو الأولوية. المقاييس، والبيانات التحليلية، والتوصيات الآنية، ونتائج المباريات المباشرة.
  • At-least-once — استخدمه حين لا يمكنك فقدان البيانات ويمكنك جعل مُستهلكك متكافئاً. هذا الافتراضي الصحيح لمعظم أنظمة الإنتاج: معالجة الطلبات، وإرسال البريد الإلكتروني، وتحديثات المخزون.
  • Exactly-once — استخدمه فقط حين الفقدان والتكرار كلاهما غير مقبولَين وخط الأنابيب بأكمله يدعمه. دفاتر الأستاذ المالية وأنظمة الفوترة وسجلات التدقيق حيث يكون التكافؤ على مستوى التطبيق أمراً غير عملي.
الافتراضي العملي: صمِّم لـat-least-once واجعل مُستهلكيك متكافئين. هذا يُعطيك فقداناً شبه معدوم للبيانات مع تعقيد قابل للإدارة، ويعمل مع كل وسيط رسائل تقريباً. احتفظ بـexactly-once للحالات التي يمكنك فيها إثبات أن التكافؤ مستحيل معمارياً.
لا تثق بتسميات الوسيط بشكل أعمى. كثير من الخدمات تُعلن عن "exactly-once" لكنها تُقدّمه فقط ضمن قسمة واحدة، أو ضمن جلسة واحدة، أو على مستوى الوسيط فحسب — ليس من طرف إلى طرف. تحقّق دائماً من الضمان المطبَّق في كل نقطة: المُنتِج → الوسيط، الوسيط → المُستهلك، المُستهلك → التخزين النهائي. السلسلة لا تتجاوز أضعف حلقة فيها.

مرجع سريع

الدلالة فقدان بيانات؟ تكرار؟ التعقيد متى تستخدم
At-most-once ممكن أبداً منخفض المقاييس، البيانات التحليلية
At-least-once أبداً ممكن متوسط الطلبات، البريد (متكافئ)
Exactly-once أبداً أبداً مرتفع دفاتر مالية، الفوترة