نمط السَّاغَا
نمط السَّاغَا
في الدرس السابق تعلمت أن Commit ذو المرحلتين (2PC) يوفر تناسقاً قوياً عبر الخدمات الموزعة — لكن بتكلفة باهظة: يجب أن يُقفَل جميع المشاركين ويظلوا متاحين طوال مدة المعاملة. في الواقع العملي، إقفال سجل طلب وسجل مخزون وسجل دفع عبر ثلاث خدمات مستقلة لمدة 200 مللي ثانية فحسب يخلق تنافساً شديداً على الموارد عند التوسع، ويجعل النظام هشاً كلما كانت أي خدمة بطيئة أو غير متاحة مؤقتاً.
نمط الساغا هو البديل العملي. بدلاً من معاملة موزعة ذرية واحدة، تقسّم الساغا العملية التجارية إلى سلسلة من المعاملات المحلية، كل منها يحدّث خدمة واحدة وينشر حدثاً أو يرسل أمراً لتشغيل الخطوة التالية. إذا فشلت أي خطوة، تنفّذ الساغا سلسلة من المعاملات التعويضية — إجراءات تلغي دلالياً العمل المُنجز سابقاً — لإعادة النظام إلى حالة متسقة دون الحاجة إلى أقفال موزعة.
مثال ملموس: تسجيل طلب في التجارة الإلكترونية
تأمل عملية تسجيل طلب تمتد عبر أربع خدمات مستقلة: خدمة الطلبات، وخدمة المخزون، وخدمة الدفع، وخدمة الإشعارات. تتطلب مسار النجاح نجاح الخطوات الأربع جميعها؛ وأي فشل في خطوة ما يستوجب التراجع عن العمل المُنجز السابق.
الخطوات الأمامية هي:
- خدمة الطلبات — إنشاء سجل طلب بحالة
PENDING. - خدمة المخزون — حجز المخزون المطلوب (طرحه من الكمية المتاحة).
- خدمة الدفع — تحصيل المبلغ من وسيلة الدفع.
- خدمة الإشعارات — إرسال رسالة تأكيد وتحديث حالة الطلب إلى
CONFIRMED.
المعاملات التعويضية المقابلة (تنفّذ بالترتيب العكسي عند الفشل):
- تحديث حالة الطلب إلى
CANCELLED. - تحرير المخزون المحجوز (إعادة الكمية).
- إصدار استرداد للمبلغ.
- إرسال رسالة إلغاء.
التنسيق بالكوريوغرافي مقابل التنسيق بالأوركسترا
هناك طريقتان مختلفتان جذرياً لتنسيق خطوات الساغا:
الكوريوغرافي (مدفوع بالأحداث): كل خدمة تستمع للأحداث على ناقل رسائل مشترك وتتفاعل بشكل مستقل. تنشر خدمة الطلبات OrderCreated؛ تستهلكه خدمة المخزون وتنشر StockReserved؛ تستهلكه خدمة الدفع وهكذا. لا يوجد منسق مركزي — الساغا تنبثق من التفاعلات.
الأوركسترا (مدفوع بالأوامر): يقوم منسق الساغا المخصص (غالباً ما يُنفَّذ كآلة حالة) بإصدار أوامر صريحة لكل مشارك بالتسلسل، وينتظر ردوده، ثم يقرر الخطوة التالية — بما في ذلك إصدار التعويضات عند فشل أي خطوة. المنسق هو المصدر الوحيد للحقيقة بشأن الحالة الحالية للساغا.
الكوريوغرافي: المقايضات
- المزايا: اقتران خفيف — الخدمات لا تعرف بعضها؛ إضافة خطوات جديدة سهلة بالاشتراك في الأحداث الحالية؛ لا نقطة فشل واحدة للتنسيق.
- العيوب: يصعب فهم التدفق الكامل — ضمني وموزع على خدمات عديدة؛ صعوبة التصحيح عند الفشل في منتصف الساغا؛ التعويضات يجب أن تكون مدفوعة بالأحداث أيضاً مما يزيد تعقيد الشبكة.
يعمل الكوريوغرافي جيداً مع الساغات البسيطة الخطية ذات 2–4 خطوات حيث يكون عقد الأحداث محدداً مسبقاً.
الأوركسترا: المقايضات
- المزايا: تدفق الساغا صريح ومركزي — سهل القراءة والمراقبة والتصحيح؛ منطق الإلغاء متجاور مع المنطق الأمامي؛ آلة حالة المنسق هي سجل تدقيق طبيعي.
- العيوب: تُدخل مكوناً جديداً (المنسق) يجب أن يكون متاحاً بدرجة عالية؛ خطر التحول إلى "خدمة إله" إذا كان نطاقها سيئاً؛ قد يخلق اقتراناً أوثق عبر قنوات الأوامر المباشرة.
الأوركسترا هي الخيار السائد في أنظمة الخدمات المصغرة الإنتاجية ذات التدفقات متعددة الخطوات والطويلة. أطر مثل Apache Camel وConductor (Netflix) وTemporal وAWS Step Functions تنفذه كبدائي أساسي.
تصميم المعاملات التعويضية
المعاملات التعويضية ليست تراجعات بالمعنى القاعدي — المعاملة الأصلية نُفِّذت فعلاً. التعويض هو معاملة جديدة متحركة للأمام تلغي الأثر التجاري دلالياً.
ثلاث خصائص يجب أن تتوفر في كل تعويض:
- الاستهلاك المتعدد الآمن (Idempotency) — يمكن تنفيذ التعويض مرات متعددة بأمان. إذا أعادت الشبكة إرسال أمر التعويض، يجب أن يكون التنفيذ الثاني بلا أثر (مثل "إذا كان المخزون قد حُرِّر مسبقاً، لا تفعل شيئاً").
- التبادلية حيثما أمكن — يجب ألا تعتمد التعويضات على ترتيب محدد للأحداث المتزامنة، لأن ترتيب تسليم الرسائل غير مضمون في الأنظمة الموزعة.
- الاكتمال — كل خطوة أمامية تُغيِّر الحالة يجب أن يكون لها تعويض محدد جيداً. إذا لم تستطع تعريف تعويض، فهذه الخطوة لا يمكن أن تكون جزءاً من ساغا (فكر في استخدام 2PC أو استدعاء متزامن بدلاً من ذلك).
العزل ومشكلة "القراءة القذرة"
خلافاً لمعاملات ACID، لا توفر الساغات أي عزل بين حالات الساغا المتزامنة. بينما تحجز الساغا أ المخزون وتُحصِّل الدفع، يمكن للساغا ب قراءة الحالة الوسيطة (مثل المخزون الذي يظهر محجوزاً لكن الدفع لم يُؤكَّد بعد). هذه هي مشكلة "القراءة القذرة" في الساغات الموزعة.
استراتيجيات التخفيف:
- القفل الدلالي — وسّم الموارد بعلامة "قيد التنفيذ" (مثل
status = PENDING_PAYMENT) تتحقق منها الساغات الأخرى قبل المتابعة. - التحديثات التبادلية — صمّم التحديثات بحيث لا يهم الترتيب (مثل زيادة/تخفيض العدادات بدلاً من تعيين قيم مطلقة).
- إعادة قراءة القيم — قبل الخطوة الأخيرة، أعد قراءة الحقول الحرجة وألغِ الساغا إذا تغيرت بشكل غير متوقع منذ بدئها.
متى تستخدم نمط الساغا
- أي عملية تمتد عبر خدمتين مصغرتين أو أكثر بقواعد بيانات مستقلة ومتطلب تجاري للاتساق.
- سير العمل طويلة الأمد (حجز رحلة: طيران + فندق + سيارة؛ معالجة قرض: فحص ائتماني + تقييم + موافقة + صرف) حيث لا يمكن قبول إقفال موزع طوال المدة.
- عندما تكون الخطوات الفردية مستهلَكة بشكل آمن أو يمكن جعلها كذلك.
الساغات هي آلية الاتساق المعيارية في الصناعة لبنيات الخدمات المصغرة. كل منصة تجارة إلكترونية كبرى، ونظام تقنية مالية، وخدمة مشاركة في الركوب تستخدم نوعاً من هذا النمط. إتقانه — مع فهم مقايضاته في العزل والانضباط المطلوب للتعويضات الصحيحة — هو أحد أكثر المهارات قيمةً عملياً في تصميم الأنظمة الموزعة.