نمط الساجا: إدارة المعاملات الموزعة دون الالتزام ثنائي المرحلة

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

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

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

لماذا لا يتوسع الالتزام ثنائي المرحلة في المعماريات الحديثة

الالتزام ثنائي المرحلة (2PC) هو الإجابة التقليدية عن الذرّية الموزعة. يطلب المنسّق من كل مشارك أن يستعد، فإذا صوّت الجميع بالموافقة أمرهم بالتثبيت، وإذا رفض أحدهم أو لم يستجب أمرهم جميعًا بالإلغاء. نظريًا يمنح هذا السلوك نفسه الذي تمنحه المعاملة المحلية: الكل أو لا شيء.

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

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

الفكرة الجوهرية: معاملات محلية وتعويض

وصف هيكتور غارسيا-مولينا وكينيث سالم نمط الساجا لأول مرة عام 1987، في سياق المعاملات الطويلة الأمد في قواعد البيانات، وقد صمدت الفكرة بشكل لافت. تقسّم الساجا العملية الطويلة إلى سلسلة خطوات T1 وT2 حتى Tn. كل خطوة معاملة محلية عادية ذات خصائص ACID داخل خدمة واحدة. ولكل خطوة Ti يمكن عكسها، يحدد المصمم كذلك معاملة تعويضية Ci تلغي أثرها من الناحية الدلالية.

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

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

التنسيق بالأحداث (Choreography)

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

في مسار الطلب، تنشئ خدمة الطلبات طلبًا بحالة معلّق وتنشر حدث «تم إنشاء الطلب». تستهلك خدمة المخزون الحدث وتحجز البضاعة وتنشر «تم حجز المخزون». تستهلكه خدمة الدفع وتخصم المبلغ وتنشر «تم تحصيل الدفعة». وأخيرًا تستهلك خدمة الطلبات هذا الحدث وتؤكد الطلب. وعند حدوث خطأ، كأن تُرفض البطاقة، تنشر خدمة الدفع حدث «فشل الدفع»، فتستجيب خدمة المخزون بإلغاء الحجز.

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

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

التنسيق بالمُنسِّق (Orchestration)

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

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

والمقايضات هنا تعكس ما سبق. فالمنسّق مكوّن جديد يجب أن يكون عالي التوافرية وثابت الحالة، وهناك خطر تسرب منطق الأعمال إليه حتى يصبح خدمة ضخمة تعرف كل شيء. غير أن محركات سير العمل الحديثة مثل Temporal وCamunda وAWS Step Functions وAzure Durable Functions تخفف هذا العبء كثيرًا، إذ توفر حالة دائمة ومؤقتات وإعادة محاولات وإمكانية رؤية جاهزة، فيركز الفريق على خطوات الأعمال بدل إعادة بناء البنية التحتية. وفي كثير من بيئات المؤسسات أصبح المُنسِّق الخيار الافتراضي لأي ساجا تتجاوز بضع خطوات.

تصميم المعاملات التعويضية

التعويض هو الموضع الذي ينجح فيه تصميم الساجا أو يفشل، وتساعد عدة إرشادات.

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

ثانيًا، اجعل كل تعويض متساوي الأثر عند التكرار (idempotent). فقد تصل الرسائل أكثر من مرة، وقد يعيد المنسّق إرسال الأوامر بعد انتهاء المهلة. وإذا نُفِّذ أمر الاسترداد مرتين فيجب ألا يُسترد المبلغ مرتين. ومفاتيح تكافؤ الأثر والقيود الفريدة وفحوص الحالة هي الأدوات المعتادة.

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

رابعًا، اقبل أن بعض التعويضات دلالية وغير مثالية. لا يمكنك سحب إشعار أُرسل، لكن يمكنك إرسال تصحيح. ولا يمكنك التراجع عن شحنة مادية، لكن يمكنك إنشاء عملية إرجاع. هذه تعويضات مشروعة، ويجب الاتفاق عليها مع أصحاب الأعمال، لا أن يخترعها المهندسون وحدهم.

التعامل مع غياب العزل

أدق نتائج الساجا أنها تتخلى عن حرف I في ACID، أي العزل. فبما أن كل معاملة محلية تُثبَّت فورًا، تستطيع معاملات أخرى رؤية الحالة الوسيطة لساجا لم تنتهِ بعد. فقد يرى العميل طلبًا معلّقًا بينما المخزون محجوز فعلًا، وقد تتنافس ساجتان على آخر وحدة في المخزون، وقد يقرأ تقرير ساجا سيُعوَّض عنها لاحقًا.

تصف الأدبيات مجموعة من التدابير المضادة، منها الأقفال الدلالية. القفل الدلالي علامة على مستوى التطبيق، مثل حالة الطلب «معلّق»، تخبر المعاملات الأخرى أن السجل في منتصف ساجا وينبغي التعامل معه بحذر. كما تقلل التحديثات التبادلية، التي يمكن تطبيقها بأي ترتيب وتعطي النتيجة نفسها، حالات التعارض. ويمكن إعادة ترتيب الخطوات لتأتي التحديثات الأخطر آخرًا. وتساعد إعادة قراءة القيم قبل التثبيت واستخدام الإصدارات أو الفحوص المتفائلة على اكتشاف التغييرات التي أجرتها ساجا متزامنة.

ولا تأتي هذه التقنيات مجانًا، ولا تغني عن التفكير الدقيق فيما يُسمح للمستخدمين والأنظمة اللاحقة برؤيته في كل مرحلة. فالمفتاح هو تصميم الحالات الوسيطة عن قصد وإظهارها في نموذج المجال، بدلًا من الأمل في ألا يلاحظها أحد.

المراسلة الموثوقة: نمط صندوق الصادر (Outbox)

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

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

المهلات وإعادة المحاولة ومعالجة الفشل

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

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

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

قابلية الرصد واختبار الساجا

لأن الساجا معاملة تجارية طويلة الأمد موزعة على الخدمات، فهي تحتاج إلى قابلية رصد من الدرجة الأولى. عيّن معرّف ارتباط عند البداية ومرّره عبر كل أمر وحدث وسطر سجل. سجّل كل انتقال حالة بختم زمني ليمكن إعادة بناء دورة حياة أي طلب. وأصدر مقاييس لمدة الساجا وعدد التعويضات ومحاولات الإعادة لكل خطوة وعدد الساجا في كل حالة. وتتيح التتبعات الموزعة المرتبطة بمعرّف الارتباط للمهندسين رؤية المسار كاملًا دفعة واحدة.

ويتطلب اختبار الساجا أكثر من اختبارات الوحدة. ابدأ باختبارات وحدة لإجراءات كل مشارك الأمامية والتعويضية، بما فيها تكافؤ الأثر. ثم أضف اختبارات لآلة حالات المنسّق تحاكي كل فشل عند كل خطوة. واستخدم اختبارات العقود للتحقق من بقاء مخططات الأوامر والأحداث متوافقة. وأخيرًا شغّل عددًا قليلًا من السيناريوهات الشاملة، واستخدم حقن الأعطال للتأكد من أن التعويض يترك النظام متسقًا فعلًا عند إيقاف خدمة في منتصف العملية. والفرق التي تمارس تجارب الفوضى على الساجا تكتسب ثقة أكبر بكثير من التي تكتفي بمراجعة المخططات.

متى لا تستخدم الساجا

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

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

مثال تطبيقي: تنفيذ الطلبات

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

في الخطوة الأولى يأمر المنسّق خدمة الطلبات بإنشاء الطلب بحالة «معلّق»، فتثبّته محليًا وترد. في الثانية يرسل «احجز المخزون»، فتتحقق خدمة المخزون من الكمية وتنقصها وتسجل حجزًا بمعرّف فريد وترد بالنجاح. في الثالثة يرسل «فوّض الدفع»، فتتصل خدمة الدفع بمعالج البطاقات وتضع حجزًا بقيمة 120 دولارًا وتعيد رمز التفويض. وهذه آخر خطوة قابلة للتعويض قبل المحور.

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

الآن لنأخذ حالة فشل. افترض أن البطاقة رُفضت في الخطوة الثالثة. ترد خدمة الدفع بـ«رُفض الدفع». يتحول المنسّق إلى مسار التعويض، فيرسل «ألغِ الحجز» مع معرّف الحجز الأصلي، وتعيد خدمة المخزون الكمية. ولأن الأمر متساوي الأثر عند التكرار، فإذا أعاد المنسّق إرساله بعد انتهاء مهلة فلن تُحرَّر الكمية إلا مرة واحدة. ثم يأمر المنسّق خدمة الطلبات بتعليم الطلب «مرفوضًا» مع رمز السبب، ويُبلَّغ العميل بفشل الدفع.

يبيّن سيناريو فشل ثانٍ قيمة الترتيب. فلو كان المخزون غير كافٍ في الخطوة الثانية لتوقفت الساجا فورًا قبل أي تفاعل مع الدفع. إن وضع الفحص الرخيص القابل للعكس والكثير الفشل أولًا جنّب الاتصال بمعالج البطاقات. وفي الوقت نفسه تعمل حالة «معلّق» قفلًا دلاليًا: تعرض الواجهة الطلب قيد المعالجة، ولا يستطيع العميل تعديله حتى تنتهي الساجا.

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

الخاتمة

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

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

أرسل رسالة

×
اضغط على فريق المساعدة في الاسفل لكي يتم نقلك لتطبيق الواتساب