كيفية نشر بأمان وظائف القضبان الخاصة بك

التوضيح من جانب بيلي ماكجين

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

في Doctolib ، نستخدم Unicorn كخوادم ويب و Resque كعاملين في العمل.

ثلاث خطوات في عملية النشر ضرورية للنجاح في نشر مهام تعطل صفر:

  1. ترحيل قاعدة البيانات (إذا لزم الأمر)
  2. أعد تشغيل عمال الويب على التوالي
  3. إعادة تشغيل العمال في الخلافة

ما يمكن أن يحدث الخطأ؟

دعنا نفترض أن قاعدة الكود N هي قاعدة الكود الحالية التي يتم إنتاجها قبل النشر ، وقاعدة الكود N + 1 هي التي ننشرها.

الحالة 1 - النشر قيد التقدم

المشكلة الأولى التي قد تواجهها تحدث أثناء إعادة تشغيل عمال الويب وقبل إعادة تشغيل عمال الوظائف. يعمل موظفو الويب (الذين يعملون على أساس كود N + 1) على توفير وظائف ذات حمولة N + 1 للعمال الذين يعملون على أساس N code.

حالة 2 - النشر الانتهاء تقريبا

المشكلة الثانية: كان من الممكن إنشاء الوظائف ذات الحمولة النافعة N ومعالجتها بعد إعادة تشغيل عمال الوظائف الذين يعملون الآن على قاعدة الكود N + 1.

في كل من هذه المواقف ، قد تفشل المهام لأن قواعد الكود اثنين غير متوافقة. قد ينشأ هذا عندما يكون هناك تغيير في حمولة الوظائف ، على سبيل المثال عندما:

  • تتم إضافة معلمة إلزامية جديدة في قاعدة الكود N + 1 التي تثير استثناء عند معالجتها بواسطة قاعدة الكود N ،
  • تم حذف الوظيفة ،
  • تتم إعادة تسمية الوظيفة ، إلخ.

لتلخيص ، قد تواجه مشكلة بمجرد أن لا يكون أحد هذه العناصر الثلاثة في نفس الإصدار مثل الاثنين الآخرين:

  • إرسال رمز وظائف
  • وظائف الحمولة
  • وظائف معالجة التعليمات البرمجية

الاستنساخ المبسط للمشكلة

لنفترض أن هذا هو رمز الوظيفة التي تعمل في الإنتاج:

# رمز قاعدة N
مثال # Payload: {id: 42 ، message: ello hello! ’}
فئة UserConfirmationJob 

ونريد إضافة معلمة جديدة:

# N + 1 رمز قاعدة
# مثال الحمولة النافعة: {المعرف: 42 ، الرسالة: "مرحبًا" ، التاريخ: "2017–12–24"}
فئة UserConfirmationJob 

هذا ما سيحدث أثناء النشر إذا تم نشر قاعدة كود الوظيفة الجديدة على النحو الوارد أعلاه:

  • في البداية ، يتم تحديد الوظائف باستخدام الحمولة النافعة N وتتم معالجتها بواسطة العاملين في الوظائف الذين يعملون على أساس كود N.
  • أثناء وبعد إعادة تشغيل العاملين على شبكة الإنترنت ، يتم تحديد الوظائف باستخدام حمولة N + 1 وتتم معالجتها بواسطة العاملين في الوظائف الذين يعملون على أساس كود N (الحالة 1).
    ستفشل الوظائف: ArgumentError: عدد خاطئ من الوسائط (معطى 3 ، متوقع 2).
  • بعد إعادة تشغيل العاملين في الوظائف ، قد تبقى بعض الوظائف التي يتم تحميلها باستخدام الحمولة النافعة (N) وستتم معالجتها بواسطة العاملين في الوظائف الذين يعملون على أساس الكود N + 1 (الحالة 2).
    ستفشل الوظائف: ArgumentError: عدد غير صحيح من الوسائط (إعطاء 2 ، المتوقع 3).

ظهرت مشكلة في البطاقة البرية: حل سريع وسخ

الحالة 1 - النشر قيد التقدم

إذا تسببت إحدى الوظائف ذات الحمولة الصافية لـ N + 1 في حدوث خطأ بعد معالجتها بواسطة عامل يعمل على أساس كود N ، فهناك فرصة أن تؤدي إعادة التشغيل البسيطة إلى حل المشكلة. انتظر انتهاء النشر ، ثم أعد محاولة المهمة. نظرًا لأننا نستخدم Resque ، فمن الممكن استخدام Resque web interface لحل كمية صغيرة من المهام الفاشلة الصغيرة. خلاف ذلك ، يمكن تكرار المهام الفاشلة من وحدة تحكم Rails.

حالة 2 - النشر الانتهاء تقريبا

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

ولكن هناك طريقة أفضل!

كيفية نشر بأمان؟

يجب عليك التأكد من أن الحمولات النافعة N + 1 متوافقة مع الحمولات النافعة N.

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

دعونا نطبق هذا على مثالنا السابق. بدلاً من نشر التغيير بالكامل ، سننشر هذا الإصدار الانتقالي:

فئة UserConfirmationJob 

باستخدام هذا الإصدار المؤقت ، نتعامل مع الحالتين الإشكاليتين:

  • عندما تتم معالجة مهمة محددة مع حمولة N + 1 من قبل عامل عمل يعمل على أساس كود N (إذا كانت الفقرة) ،
  • عندما تتم معالجة مهمة محددة بحمولة حمولة N بواسطة عامل وظيفة يعمل على أساس كود N + 1 (بند آخر).

بعد فترة وجيزة من النشر ، لا يوجد لدينا سوى وظائف يتم تحديدها باستخدام حمولة N + 1 التي تتم معالجتها بواسطة عمال الوظائف الذين يعملون على أساس كود N + 1. بند آخر لدينا لا طائل منه: يمكننا نشر النسخة النهائية من التغيير:

فئة UserConfirmationJob 

وفويلا ، تم نشر التغييرات دون أي خطأ أثناء النشر!

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

عند حذف الوظيفة:

  • حذف الكود الذي يضفي على الوظيفة (وليس الوظيفة نفسها)
  • نشر
  • لم تعد المهمة مثبتة بعد ذلك: احذفها ، وانتشر مرة أخرى

عندما تتم إعادة تسمية الوظيفة (على سبيل المثال ، من OldJob إلى NewJob):

  • إنشاء وظيفة جديدة NewJob التي هي نسخة من (أو إشارة إلى) OldJob.
    احتفظ بـ OldJob كما هي ، ولا تقم بتغيير الكود الذي يؤدي المهمة.
  • نشر
  • قم بتغيير الكود الذي يقوم بتنفيذ المهمة لاستخدام NewJob.
    لا تحذف OldJob.
  • نشر
  • لم يعد يستخدم OldJob: حذفه ، ونشر مرة أخرى

قد تبدو الحالات التي ستواجهها مختلفة ، لكن الحل دائمًا ما يشبه ما ورد أعلاه.

كيف تعرف أن الكود الخاص بك سوف يكسر العمال؟

في كل مرة تقوم فيها بتغيير رمز الوظيفة ، والنظر ببساطة إلى الفرق بين الكود القديم والجديد ومعرفة أن هذه المشكلة يمكن أن تحدث ، يساعد على اكتشاف 80 ٪ من المشاكل المحتملة.

ستة عيون أفضل من اثنين: في Doctolib نقوم ، كحد أدنى ، بمراجعة كودين لكل طلب سحب.

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

في Doctolib ، التحديات التقنية كثيرة! إذا كنت مهتمًا بالعمل معنا ، فلا تتردد في إلقاء نظرة على صفحة الوظائف الهندسية الخاصة بنا :)