← All posts

أثبتنا أن كل تنبيه يمكن أن ينطلق. لكن معظمها لم يكن كذلك.

Authagonal·July 31, 2026

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

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

أخفق أول تشغيل حقيقي في معظم القواعد. وإليك ما كان مختبئًا خلف اللوحات الخضراء.

التنبيه الرئيسي كان ميتًا

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

كان السبب اسمًا. تخرج مقاييسنا من OpenTelemetry، حيث تكون الأسماء منقوطة، مثل http.server.request.duration. في مكان ما بين قراءة درس تعليمي وكتابة الاستعلام، افترضنا أن الخادم الخلفي للمراقبة سيحوّل تلك النقاط إلى شرطات سفلية، كما تفعل الكثير من أدوات Prometheus، فكتبنا استعلام التنبيه بالصيغة ذات الشرطة السفلية. لكن الخادم الخلفي لا يُحوّل. إنه يحتفظ بالأسماء المنقوطة تمامًا كما أُرسلت. فاستعلم التنبيه عن مقياس غير موجود، ولم يطابق شيئًا، وحسب معدل أخطاء يساوي صفرًا إلى الأبد، وجلس هناك هادئًا وأخضر بينما كان المقياس الحقيقي، تحت اسمه المنقوط الحقيقي، يسجّل كل واحد من أخطاء الـ 500 تلك. لم يكن الاستعلام خاطئًا بطريقة تُنتج خطأً. كان خاطئًا بطريقة تُعيد المجموعة الفارغة بصمت، فتحوّلها الحسابات بعد ذلك إلى صفر معقول تمامًا.

القاعدة التي أسقطت معها كل تنبيه سجل

كان ذلك هو السيئ. أما التالي فكان أسوأ، ولم تلتقطه المنظومة إلا لأنها تُصرّ على التسليم لا على مظهر القاعدة الصحيح.

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

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

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

التنبيهات التي انطلقت إلى الأبد، وهو أمر يساوي ألا تنطلق أبدًا

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

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

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

القاعدة التي عاملت أول نَفَس لـ pod على أنه أمر طبيعي

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

كل إخفاق اختبأ في وصلة لا تجدها إلا بتشغيلها

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

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

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