إعادة النشر الفاشلة لا تحذف خلفية تعمل أبداً
في 25 سبتمبر، حذفت إعادة نشر فاشلة خلفية أحد التطبيقات كانت تعمل بشكل سليم منذ نحو ساعتين ونصف. الكود الذي يتراجع عن النشر الفاشل أزال كل تطبيق خادم لمسه النشر، بما فيها التطبيقات التي كانت تخدم المستخدمين قبل أن يبدأ. الآن لا يُحذف إلا التطبيقات التي أنشأها النشر نفسه، أما التطبيق الموجود مسبقاً فيُعاد إلى النسخة التي كان يعمل بها قبل النشر تماماً. وأضاف إصلاحان لاحقان في اليوم نفسه ضماناً بأن يعمل هذا التراجع أيضاً عندما تفشل أداة النشر التابعة لمنصة الاستضافة، وبأن التطبيق السليم لا يُعتبر فاشلاً لمجرد أنه كان في وضع السكون.
كيف تُنشر الخلفية
عندما يكون لتطبيق Mythex الخاص بك خلفية، أي واجهة برمجة تطبيقات أو خادم وليس ملفات ثابتة فقط، فإن النشر يبني الخلفية من Dockerfile (وصفة لتغليف التطبيق في صورة)، ثم يطرح هذه الصورة على آلات في منصة الاستضافة لدينا. بعد ذلك ينتظر فحص صحة حتى تعمل النسخة الجديدة وتستمر في العمل، ولا يُعلن النشر حياً قبل ذلك.
إذا فشل شيء في الطريق، يعمل تراجع للتراجع عنه. وإصلاح هذا التراجع بشكل صحيح من أهم أجزاء النشر، لأنه يعمل تحديداً حين يكون هناك خطأ قائم بالفعل.
المشكلة الأولى: التراجع حذف ما كان يجب أن يستعيده
كان التراجع يحتفظ بقائمة تطبيقات الخوادم التي وصلها النشر، وعند الفشل يدمّر كل واحد منها. وهذا صحيح في النشر الأول، فالتطبيق أُنشئ للتو ولا يعتمد عليه شيء، وحذفه يعيد الأمور إلى ما كانت عليه.
لكنه خطأ تماماً في إعادة النشر. فالتطبيق كان موجوداً ويخدم زواراً حقيقيين. وفي 25 سبتمبر، حذفت إعادة نشر فاشلة واجهة برمجة تطبيقات كانت حية منذ نحو ساعتين ونصف.
وكان هناك عيب ثانٍ مختبئ في الكود نفسه. ففي الحالات التي لا يُحذف فيها التطبيق، تبقى الصورة الجديدة المعطوبة على الآلات، بينما نخبر صاحب الموقع أن موقعه الحالي ما زال يعمل. وبحلول الوقت الذي يمكن أن يفشل فيه فحص الصحة، تكون الصورة الجديدة قد طُرحت بالفعل على كل آلة. وعدم فعل شيء ليس هو نفسه التراجع.
الحل: احذف ما أنشأته فقط، واستعد الباقي
يسجّل كل نشر الآن، لكل تطبيق خادم يلمسه:
- هل أنشأه هذا النشر،
- وإذا لم يكن كذلك، أي صورة كان يعمل بها مباشرة قبل النشر.
عند الفشل، يأخذ كل تطبيق واحدة من ثلاث معاملات:
- أنشأه هذا النشر: يُحذف كما كان من قبل.
- كان موجوداً والصورة السابقة معروفة: تُعاد نشر الصورة السابقة بالإعدادات نفسها. لا بناء جديداً، بل تعود الصورة فقط إلى مكانها.
- كان موجوداً والصورة السابقة غير معروفة: يُترك كما هو. التطبيق الموجود لا يُحذف أبداً، حتى عندما لا نستطيع استعادته.
هذه القواعد دالة صغيرة نقية لها اختباراتها الخاصة، بحيث لا يمكن للقرار أن ينحرف مجدداً إلى «احذف كل شيء» دون أن يفشل اختبار.
المشكلة الثانية: أداة النشر قد تفشل قبل أن نكون قد دوّنّا كيفية التراجع
بعد هذا الإصلاح بوقت قصير، وجدنا فيه ثغرة. فأداة النشر في منصة الاستضافة تشغّل فحوصها الخاصة: إذا تعطلت الآلات الجديدة، تنتهي بخطأ. لكنها تفعل ذلك بعد أن تكون الصورة الجديدة قد وصلت إلى الآلات.
كان كودنا يسجّل التطبيق للتراجع فقط بعد أن تعيد الأداة النجاح. وعندما تُفشل الأداة نفسها النشر، لم يكن التطبيق قد دخل قائمة التراجع بعد، فلم يُستعد شيء. وفي بيئة الاختبار (staging) لدينا، ظلت صورة معطوبة تخدم المستخدمين بعد إعادة نشر فاشلة.
الحل: سجّل التراجع قبل أن تبدأ
يُسجَّل التطبيق الآن للتراجع، مع معرفة هل أُنشئ وأي صورة كان يعمل بها، بمجرد تجهيزه وقبل طرح أي شيء عليه. مهما فشل بعد ذلك، يعرف التراجع ما الذي يجب إعادته. تحققنا من ذلك محلياً: فشلت إعادة نشر معطوبة في فحوصات الأداة الخاصة بها، واستمرت النسخة السابقة في الرد.
المشكلة الثالثة: تطبيق في وضع السكون فُشل بوصفه «لم يبدأ أبداً»
تدخل تطبيقات Mythex المنشورة في السكون عند الخمول لتوفير أرصدتك. وإعادة النشر تترك الآلة النائمة متوقفة، لذلك على فحص الصحة أن يوقظها. وكان الفحص يطلب من المنصة تشغيل الآلة مرة واحدة ويتجاهل الرد.
في 25 سبتمبر، لم يصل طلب التشغيل الوحيد هذا إلى أي مكان. فبقيت واجهة برمجة تطبيقات سليمة متوقفة طوال فحص الصحة البالغ خمس دقائق، وفُشلت بالرسالة «آلة واحدة لم تبدأ أبداً (آخر حالة: متوقفة)».
والمنصة ترفض أحياناً طلب التشغيل. فأثناء إعادة إنتاج المشكلة، أعادت استجابة «القفل محجوز حالياً» دون أن نستفزها، وهي ترفض طلبات التشغيل أيضاً أثناء تحديث الآلة.
الحل: استمر في الطلب، واطرق الباب
يفعل فحص الصحة الآن التالي:
- يطلب تشغيل الآلة المتوقفة من جديد كل 20 ثانية أثناء الانتظار،
- يسجّل ما ردّت به المنصة عندما ترفض، و
- يرسل طلباً عادياً واحداً إلى التطبيق، كما يفعل الزائر تماماً. فالوكيل التابع للمنصة يوقظ الآلة النائمة عند وصول أي طلب.
أعدنا إنتاج المشكلة على المنصة الحقيقية باستخدام تطبيق تجريبي مؤقت رُفض طلب تشغيله الأول. قبل الإصلاح، فشل النشر بعد 91 ثانية. وبعده، نُشر في 20 ثانية. أما التطبيق الذي ينهار فعلاً عند الإقلاع فما زال يفشل كما ينبغي.
ما استخلصناه
- يجب أن يعرف التراجع الفرق بين الجديد والموجود. «التراجع» عن شيء أنشأته يعني حذفه، أما «التراجع» عن شيء كان موجوداً فيعني استعادته. ومعاملة الاثنين بالطريقة نفسها هي ما يجعل آلية الأمان تحذف تطبيقاً سليماً.
- دوّن كيفية التراجع قبل أن تتصرف. إذا سُجّل التراجع فقط بعد نجاح خطوة ما، فإن الخطوة التي تفشل في منتصف الطريق لا تترك شيئاً يمكن التراجع به.
- لا تتجاهل رد طلب تعتمد عليه أبداً. «لا» واحدة تم تجاهلها حوّلت تطبيقاً سليماً إلى نشر فاشل.
ومن الإصلاح المرتبط به في اليوم نفسه: التقاط التطبيقات التي تنهار مباشرة بعد النشر. أما ما يحدث عندما يفشل النشر من جهتك، فراجع استكشاف أخطاء النشر في الوثائق.