مبرمجين سباك ، أو قصة تسرب واحد وصعوبات التعامل معه

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

من أين؟ كانت آخر التغييرات الرئيسية في قاعدة كود المنسق في النسخة السابقة منذ أكثر من شهرين ، وبعد ذلك لم يحدث شيء ملحوظ للذاكرة. ولكن ، لسوء الحظ ، كانت جداول المراقبة ثابتة - من الواضح أن ذاكرة المنسق بدأت تتسرب في مكان ما ، كان هناك بركة كبيرة في أرضية الخدمة ، مما يعني أن فريق السباكة لديه الكثير من العمل للقيام به.



أولاً نقوم بعمل انحراف صغير. من بين أمور أخرى ، يسمح لك VLSI بتتبع ساعات العمل ومراقبة الوصول عن طريق نموذج الوجه أو بصمة الإصبع أو بطاقات الوصول. في نفس الوقت ، تتواصل VLSI مع وحدات التحكم في نقطة النهاية (أقفال ، بوابات دوارة ، محطات وصول ، إلخ). تتواصل خدمة منفصلة مع الأجهزة. إنه سلبي ، يتفاعل مع أجهزة التحكم في الوصول استنادًا إلى بروتوكولاتها المنفذة عبر HTTP (S). هو مكتوب على أساس المكدس القياسي للخدمات في شركتنا: قاعدة بيانات PostgreSQL ، يتم استخدام Python 3 لمنطق الأعمال ، الموسعة مع أساليب C / C ++ من منصتنا.

تتكون عقدة خدمة الويب النموذجية من العمليات التالية:

  • مراقب هو عملية الجذر.
  • المنسق هو عملية تابعة لجهاز العرض.
  • إجراءات العمل.

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

على من يقع اللوم وماذا تفعل؟


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



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

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

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



يبدو أننا حصلنا على نتائج عمل tracemalloc على الذاكرة المخصصة في Python ، والآن سننظر فيها ونجد الجاني للتسرب ، لكنه لم يكن موجودًا - في البيانات التي تم جمعها لا توجد قمم 5.5GB رأيناها على الرسوم البيانية للمراقبة. الحد الأقصى للذاكرة المستخدمة هو 250 ميجا بايت فقط ، وحتى traceMalloc يأكل 130 ميجا بايت منها. هذا يمكن تفسيره جزئيًا - tracemalloc يسمح لك بمشاهدة ديناميكيات الذاكرة في Python ، لكنه لا يعرف عن تخصيص الذاكرة في حزم C و C ++ التي يتم تنفيذها من خلال منصتنا. لم يكن من الممكن العثور على شيء مثير للاهتمام في البيانات التي تم الحصول عليها ، حيث يتم تخصيص الذاكرة بأحجام مقبولة للكائنات العادية مثل التيارات والسلاسل والقواميس - بشكل عام ، لا شيء مريب. ثم قررنا إزالة كل شيء غير ضروري من البيانات ، مع ترك إجمالي استهلاك الذاكرة والوقت فقط ، والتصور.على الرغم من أن التصور لم يساعد في الإجابة على الأسئلة "ماذا يحدث" و "لماذا" ، إلا أننا بمساعدتنا رأينا ارتباطًا مع البيانات من المراقبة - مما يعني أن لدينا بالتأكيد مشكلة في مكان ما ، ونحن بحاجة إلى البحث عنها.



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

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

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

ها هو ، الرمز الخبيث لوحدة Python مع فتحة كبيرة (يظهر الرمز في Python 3.5 على اليسار قبل التغييرات ، على اليمين - في 3.7 ، بعد):



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



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

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

حسنًا ، أود أن أعبر عن امتناني لكل من ساعد في التعامل مع أعمال السباكة في حالات الطوارئ والعودة مرة أخرى إلى قدرتها التشغيلية السابقة في خدمتنا!

All Articles