الأخطاء السيئة السمعة وكيفية تجنبها على مثال ClickHouse

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


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

البق سيئ السمعة


إذا كتبت بعض التعليمات البرمجية ، فاستعد للمشكلات على الفور.

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

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

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

دعونا نلقي نظرة على هذه الأخطاء مع بعض الأمثلة.

البق التكوين


حذف البيانات . الحالة الأولى من الممارسة. لحسن الحظ ، ليس لي ولا ياندكس ، لا تقلق.

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

تعرف عقد البيانات عنوان الرئيسي وتتصل به. يقوم المعالج بمراقبة مكان البيانات التي يجب تحديد موقعها وما هي البيانات ، ويعطي أوامر مختلفة لعقد البيانات: "تنزيل البيانات X ، يجب أن يكون لديك البيانات Y ، وحذف البيانات Z". ما الخطأ الذي قد يحدث؟

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


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

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

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

إذا قام البرنامج بتنفيذ إجراءات مدمرة - عزل الاختبار والإنتاج على مستوى الشبكة(iptables). على سبيل المثال ، يعد حذف الملفات أو إرسال بريد إلكتروني إجراءً مدمرًا لأنه "سيلتفت" انتباه شخص ما. ضع عتبة عليهم: يمكن إرسال مائة حرف ، وللآلاف وضع مربع اختيار أمان ، والذي تم تعيينه قبل حدوث شيء رهيب.

التكوينات . المثال الثاني هو بالفعل من ممارستي.

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

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

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

كقاعدة عامة ، يتكون ZooKeeper من 3 أجهزة ، وأحيانًا 5. يتم سرد جميع الأجهزة في تكوين ClickHouse دفعة واحدة ، ويتم إنشاء اتصال مع جهاز عشوائي ، ويتفاعل معه ، ويقوم هذا الخادم بتكرار جميع الطلبات.

ماذا حدث؟ كان لدى الشركة ثلاثة خوادم ZooKeeper. لكنها لم تعمل كمجموعة من ثلاث عقد ، ولكن ثلاث عقد مستقلة - ثلاث مجموعات من عقدة واحدة. يتصل OneHouse بخادم واحد ويكتب البيانات. تريد النسخ المتماثلة تنزيل هذه البيانات ، ولكن لم يتم العثور عليها في أي مكان. عند إعادة التشغيل ، يتصل الخادم بـ ZooKeeper آخر: فهو يرى أن البيانات التي كان يعمل بها من قبل غير ضرورية ، ويجب تأجيلها في مكان ما. لا يحذفها ، ولكن ينقلها إلى دليل منفصل - في بيانات ClickHouse لا يتم حذفها بسهولة.

قررت إصلاح تكوين ZooKeeper. أعيد تسمية جميع البيانات وأطلب طلبًا ATTACHلأجزاء من البيانات من الدليل detached/unexpeted_*.

ونتيجة لذلك ، تمت استعادة جميع البيانات ، وتمت مزامنة النسخ المتماثلة ، ولم تكن هناك خسائر ، وكانت الرسوم البيانية مستمرة. الشركة راضية ، شاكرة ، كما لو كانت قد نسيت بالفعل كيف كان كل شيء يعمل بشكل سيئ من قبل.

كانت هذه أخطاء بسيطة في التكوين. المزيد من الأخطاء ستكون في الكود.

الخلل في التعليمات البرمجية


نكتب التعليمات البرمجية في C ++. هذا يعني أن لدينا بالفعل مشاكل.
المثال التالي هو خطأ حقيقي من الإنتاج على مجموعة Yandex.Metrica (2015) - نتيجة لرمز C ++. كان الخطأ هو أنه في بعض الأحيان تلقى المستخدم بدلاً من الرد على الطلب رسالة خطأ:

  • "المجموع الاختباري لا يتطابق ، البيانات التالفة" - لا يتطابق المجموع الاختباري ، البيانات معطلة - مخيف!
  • "أصبح LRUCache غير متناسق. يجب أن يكون هناك خطأ فيه ”- أصبحت ذاكرة التخزين المؤقت غير متناسقة ، على الأرجح خطأ فيها.

الكود الذي كتبناه يعلم نفسه بوجود خلل هناك.

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

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

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

"أصبح LRUCache غير متناسق. يجب أن يكون هناك خطأ فيه ". هذا خطأ واضح في الشفرة ، ونحن نكتب بلغة C ++ - ربما الوصول إلى الذاكرة؟ لكن الاختبارات تحت AddressSanitizer و ThreadSanitizer و MemorySanitizer و UndefinedBehaviorSanitizer في CI لا تظهر شيئًا.

ربما لم يتم تغطية بعض حالات الاختبار؟ أقوم بتجميع الخادم باستخدام AddressSanitizer ، وتشغيله عند الإنتاج - ولا يجذب أي شيء. لبعض الوقت ، يتم مسح الخطأ عن طريق إعادة تعيين بعض ذاكرة التخزين المؤقت للعلامة (ذاكرة التخزين المؤقت للكيس).

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

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

اتضح أن السبب هو أنه في مجموعة "المشكلة" استخدموا ميزة جديدة - قواميس ذاكرة التخزين المؤقت. يستخدمون أداة تخصيص الذاكرة المكتوبة بخط اليد ArenaWithFreeLists . نحن لا نكتب فقط في لغة C ++ ، ولكننا أيضًا رأينا نوعًا من المخصصات المخصصة - فنحن نعاني من المشاكل مرتين.

ArenaWithFreeLists هو جزء من الذاكرة حيث يتم تخصيص الذاكرة على التوالي في أحجام قابلة للقسمة على اثنين: 16 ، 32 ، 64 بايت. إذا تم تحرير الذاكرة ، فإنها تشكل قائمة مرتبطة بشكل فردي بمجموعات FreeLists المجانية.

دعونا نلقي نظرة على الرمز.

class ArenaWithFreeLists
{
    Block * free_lists[16] {};
    static auto sizeToPreviousPowerOfTwo(size_t size)
    {
        return _bit_scan_reverse(size - 1);
    }

    char * alloc(size_t size)
    {
        const auto list_idx = findFreeListIndex(size);
        free_lists[list_idx] ->...
    }
}

يستخدم دالة _bit_scan_reverseبشرطة سفلية في البداية.
هناك قاعدة غير مكتوبة: "إذا كانت هناك دالة تسطير سفلي واحد في البداية ، فاقرأ الوثائق عليها مرة واحدة ، وإذا كان اثنان ، اقرأها مرتين".
نستمع ونقرأ الوثائق: "int _bit_scan_reverse (int a). تعيين dst إلى فهرس أعلى بت مجموعة في عدد صحيح 32 بت أ. إذا لم يتم تعيين بت في dst ثم غير معرّف . " يبدو أننا وجدنا مشكلة.

في C ++ ، يعتبر هذا الوضع مستحيلاً بالنسبة للمترجم. يمكن للمترجم استخدام سلوك غير محدد ، وهذا "الاستحالة" ، كافتراض لتحسين الشفرة.

المترجم لا يرتكب أي خطأ - فهو يولد بصدق تعليمات التجميع bsr %edi, %eax. ولكن ، إذا كان المعامل صفراً ، فإن التعليمات لها bsrسلوك غير محدد ليس على مستوى C ++ ، ولكن على مستوى وحدة المعالجة المركزية. إذا كان السجل المصدر صفرًا ، فلن يتغير سجل الوجهة: كان هناك بعض القمامة عند الإدخال ، وستبقى هذه القمامة أيضًا عند الإخراج.

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

bsrl %edi, %eax
retq

ثم نظرت إلى مثال لرمز مشابه في ثنائي باستخدام objdump.



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

كيف يظهر هذا الخطأ نفسه؟

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

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

كما ترى ، يمكن إصلاح الرمز الخطأ على الأقل. ولكن كيف لإصلاح الخلل في الأجهزة؟

البق في الحديد


هذه ليست حشرات ، بل قوانين فيزيائية - تأثيرات لا مفر منها. وفقا للقوانين الفيزيائية ، فإن الحديد هو عربات التي تجرها الدواب حتما.

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

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

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

بت تقلب في ذاكرة الوصول العشوائي (ولكن ماذا عن ECC؟). في ذاكرة الوصول العشوائي في الخوادم ، تكون وحدات البت تالفة أيضًا. كما أن لديها رموز تصحيح الخطأ. عند حدوث أخطاء ، تكون عادة مرئية من الرسائل الموجودة في سجل Linux kernel في dmesg. عندما يكون هناك العديد من الأخطاء ، سنرى شيئًا مثل: "تم إصلاح N مليون خطأ في الذاكرة." ولكن لن يتم ملاحظة البتات الفردية ، وبالتأكيد سيكون هناك شيء عربات التي تجرها الدواب.

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

كيف تظهر أخطاء الحديد عادة؟ التذكرة " znode تالف يمنع ClickHouse من البدء " تأتي إلى GitHub - البيانات الموجودة في عقدة ZooKeeper تالفة.

في ZooKeeper ، نكتب عادةً بعض البيانات الوصفية بنص عادي. هناك شيء خاطئ معه - مكتوبة " نسخة طبق الأصل " غريبة جدا.



نادرا ما يحدث ذلك بسبب وجود خطأ في التعليمات البرمجية ، يتغير بت واحد. بالطبع ، يمكننا كتابة مثل هذا الرمز: نأخذ مرشح بلوم ، نغير البت في عناوين معينة ، نحسب العناوين بشكل غير صحيح ، نغير البت الخاطئ ، يقع على بعض البيانات. هذا كل شيء ، الآن في ClickHouse ليس " نسخة طبق الأصل" ، ولكن " repli b a " وعليه كل البيانات خاطئة. ولكن عادة ما يكون التغيير في جزء واحد من أعراض مشاكل الحديد.

ربما كنت تعرف مثال bitsquatting. أجرت Artyom Dinaburg تجربة : هناك نطاقات على الإنترنت بها عدد كبير من الزيارات ، على الرغم من أن المستخدمين لا يذهبون إلى هذه المجالات بمفردهم. على سبيل المثال ، مثل هذا المجال FB-CDN.com هو Facebook CDN.

سجلت Artyom نطاقًا مشابهًا (والعديد من المجالات الأخرى) ، لكنها تغيرت قليلاً. على سبيل المثال ، FA-CDN.com بدلاً من FB-CDN.com. لم يتم نشر المجال في أي مكان ، ولكن حركة المرور وصلت إليه. في بعض الأحيان تمت كتابة مضيف FB-CDN في رؤوس HTTP ، وذهب الطلب إلى مضيف آخر بسبب أخطاء في ذاكرة الوصول العشوائي على أجهزة المستخدمين. ذاكرة الوصول العشوائي مع تصحيح الخطأ لا تساعد دائمًا. في بعض الأحيان يتدخل ويؤدي إلى نقاط ضعف (اقرأ عن Rowhammer و ECCploit و RAMBleed).
الخلاصة: تحقق دائمًا من جمع البيانات بنفسك.
عند الكتابة إلى نظام الملفات ، قم بفحص المجموع دون فشل. عند الإرسال عبر الشبكة ، قم أيضًا بتلخيص الشيكات - لا تتوقع وجود أي مجموع تدقيقي هناك.

المزيد من الخلل! ..


مقاييس مجموعة الإنتاج . أحيانًا ما يحصل المستخدمون استجابةً لطلب ما على استثناء: "المجموع الاختباري غير مطابق: البيانات التالفة" - مجموع الاختيار غير صحيح ، والبيانات تالفة.



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

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

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

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

الفرضيات والأنماط


فشل 7 من أصل 9 خوادم مع E5-2683 v4. ولكن من الخطأ المعرض ، فقط حوالي نصف E5-2683 v4 هو فرضية فارغة.

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

لا يعتمد الخطأ على نواة لينكس - تم فحصه على خوادم مختلفة ، ولم يعثر على شيء. لا يوجد شيء مثير للاهتمام في kern.log ، machine check exceptionلا توجد رسائل . في رسومات الشبكة ، بما في ذلك أجهزة الإرسال ، وحدة المعالجة المركزية ، IO ، الشبكة ، لا شيء مثير للاهتمام. جميع محولات الشبكة على الخوادم التي تحدث أخطاء ولا تظهر هي نفسها.

لا توجد أنماط . ماذا أفعل؟ استمر في البحث عن الأنماط. محاولة ثانية.

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

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

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

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

لم أصلح الخطأ - كنت أبحث مرة أخرى عن أنماط. المحاولة الثالثة.

لسبب ما ، يظهر الخطأ فقط على إحدى المجموعات - على النسخ المتماثلة الثالثة في فلاديمير DC (نود استدعاء مراكز البيانات حسب أسماء المدن). في فبراير 2019 ، ظهر خطأ أيضًا في Vladimirs DC ، ولكن على إصدار مختلف من ClickHouse. هذه حجة أخرى ضد الفرضية القائلة بأننا كتبنا رمزًا خاطئًا. لقد قمنا بالفعل بإعادة كتابته ثلاث مرات من فبراير إلى مايو - ربما لا يوجد الخطأ في الرمز .

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

جميع الطلبات التي تحتوي على خطأ ، باستثناء واحد GLOBAL JOIN. ولكن للمقارنة ، هناك طلب واحد بسيط بشكل غير عادي ، وحجم الكتلة المضغوطة له هو 75 بايت فقط.

SELECT max(ReceiveTimestamp) FROM tracking_events_all 
WHERE APIKey = 1111 AND (OperatingSystem IN ('android', 'ios'))

نحن نرفض فرضية التأثير GLOBAL JOIN.

الأكثر إثارة للاهتمام هو أن الملقمات المتأثرة يتم تجميع في نطاقات بأسمائها :
mtxxxlog01-{39..44 57..58 64 68..71 73..74 76}-3.

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

  • مجموعات الخوادم المشكلة هي نفسها في فبراير.
  • توجد خوادم المشكلة في أجزاء معينة من مركز البيانات. في DC Vladimir هناك ما يسمى الخطوط - أجزائها المختلفة: VLA-02 ، VLA-03 ، VLA-04. يتم تجميع الأخطاء بشكل واضح: في بعض قوائم الانتظار تكون جيدة (VLA-02) ، وفي مشاكل أخرى (VLA-03 ، VLA-04).

كتابة التصحيح


بقي فقط لتصحيح الأخطاء باستخدام طريقة "الرمح". هذا يعني تشكيل الفرضية "ماذا يحدث إذا حاولت القيام بذلك؟" وجمع البيانات. على سبيل المثال ، لقد وجدت query_logاستعلامًا بسيطًا به خطأ في الجدول حيث يكون حجم الحزمة size of compressed blockصغيرًا جدًا (= 107).



أخذت الطلب ، وقمت بنسخه وتنفيذه يدويًا باستخدام clickhouse-local.

strace -f -e trace=network -s 1000 -x \
clickhouse-local --query "
    SELECT uniqIf(DeviceIDHash, SessionType = 0)
    FROM remote('127.0.0.{2,3}', mobile.generic_events)
    WHERE StartDate = '2019-02-07' AND APIKey IN (616988,711663,507671,835591,262098,159700,635121,509222)
        AND EventType = 1 WITH TOTALS" --config config.xml

بمساعدة السرعة ، تلقيت لقطة (تفريغ) من الكتل عبر الشبكة - نفس الحزم بالضبط التي يتم تلقيها عند تنفيذ هذا الطلب ، ويمكنني دراستها. يمكنك استخدام tcpdump لهذا ، ولكنه غير مريح: من الصعب عزل طلب محدد عن حركة الإنتاج.

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

يتم تنفيذ الطلب دون أخطاء - لم يتم تكرار الخطأ . إذا تم استنساخها ، سيتم حل المشكلة. لذلك ، قمت بنسخ الحزم إلى ملف نصي وبدأت تحليل البروتوكول يدويًا.



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

لقد كتبت برنامجًا بسيطًا يأخذ حزمة ويتحقق من مبلغ الشيك عند استبدال بت واحد في كل بايت. قام البرنامج بقلب البت في كل موضع ممكن وقراءة مبلغ الشيك.



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

مشكلة في الأجهزة


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

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

كيفية توطين مشكلة الأجهزة؟

  • نشأت المشكلة واختفت في تواريخ معينة.
  • يتم تجميع خوادم المتضررة بأسمائها: mtxxxlog01-{39..44 57..58 64 68..71 73..74 76}-3.
  • مجموعات خوادم المشكلة هي نفسها في فبراير.
  • خوادم المشكلة موجودة فقط في قوائم انتظار معينة لمركز البيانات.

كانت هناك أسئلة لمهندسي الشبكات - البيانات تتفوق على مفاتيح الشبكة. اتضح أن مهندسي الشبكات استبدلوا المفاتيح بالآخرين في تلك التواريخ بالضبط. بعد سؤال ، قاموا باستبدالهم بالسابقين واختفت المشكلة.

تم حل المشكلة ، ولكن لا تزال هناك أسئلة (لم يعد للمهندسين).

لماذا لا تساعد ECC (ذاكرة تصحيح الخطأ) على مفاتيح الشبكة؟ لأن عدة بتات يمكن أن تعوض بعضها البعض - تحصل على خطأ غير مكتشف.

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

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

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

تظل البيانات المخزنة في ClickHouse متسقة. استخدم مجموع الشيكات في ClickHouse. نحن نحب التحقق من المبالغ كثيرًا حتى نفكر في ثلاثة خيارات على الفور:

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

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

رسالة خطأ محسنة


كيف أشرح للمستخدم عندما يتلقى رسالة خطأ مفادها أن هذه مشكلة في الجهاز؟



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

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

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

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

الهذيان في البيانات


جعلتني هذه المشكلة المثيرة للاهتمام وغير المتوقعة تقلق. لدينا بيانات Yandex.Metrica. تتم كتابة JSON بسيط إلى قاعدة البيانات في أحد الأعمدة - معلمات المستخدم من رمز JavaScript للعداد.

أقوم بتقديم نوع من الطلب وتعطل خادم ClickHouse مع segfault. من تتبع المكدس ، أدركت ما هي المشكلة - التزام جديد من المساهمين الخارجيين من بلد آخر. اختفى التعدي الثابت ، اختفى. أجري

نفس الطلب: SELECTفي ClickHouse ، للحصول على JSON ، ولكن مرة أخرى ، كلام فارغ يعمل كل شيء ببطء. أحصل على JSON ، وهو 10 ميغابايت. أعرضه وأبدو أكثر انتباهاً: {"jserrs": cannot find property of object undefind...ثم سقطت ميغابايت من الشفرة الثنائية.



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

مسار فكرة


تم تسجيل نفس القمامة على مجموعتين ، بشكل مستقل عن بعضها البعض. البيانات غير مهمة ، لكنها صالحة UTF-8. يحتوي UTF-8 هذا على بعض عناوين URL الغريبة وأسماء الخطوط والعديد من الأحرف "I" على التوالي.

ما هو خاص السيريلية الصغيرة "أنا"؟ لا ، هذه ليست Yandex. والحقيقة هي أنه في ترميز Windows 1251 هو الحرف 255. وعلى خوادم Linux الخاصة بنا ، لا يستخدم أحد ترميز Windows 1251.

اتضح أن هذا تفريغ للمتصفح: كود JavaScript الخاص بالعداد المتري يجمع أخطاء JavaScript. كما اتضح ، فإن الجواب بسيط - كل ذلك جاء من المستخدم .

من هنا أيضا يمكن استخلاص النتائج.

البق من جميع أنحاء الإنترنت


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

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

لذلك ، من الصحيح ببساطة تصفية هذه القمامة قبل الكتابة إلى قاعدة البيانات. لا حاجة لكتابة القمامة إلى قاعدة البيانات - فهي لا تحبها.

HighLoad++ ( 133 ), - , , ++ PHP Russia 2020 Online.

Badoo, PHP Russia 2020 Online . PHP Russia 2020 Online 13 , .

, .

All Articles