← All posts

हमने साबित किया कि हर अलर्ट फ़ायर हो सकता है। ज़्यादातर नहीं हो सके।

Authagonal·July 31, 2026

मॉनिटरिंग में एक ऐसी विशेषता है जो इसे विशिष्ट रूप से ख़तरनाक बनाती है: यह वह कोड है जो केवल किसी आपदा के दौरान ही चलता है। आपके सिस्टम का हर दूसरा हिस्सा लगातार चलता रहता है, उपयोगकर्ताओं के सामने, और ऐसी त्रुटियाँ फेंकता है जिन्हें आप देख लेते हैं। आपका अलर्टिंग सिस्टम उस दिन चलता है जिस दिन डेटाबेस भर जाता है, उससे पहले नहीं, और अगर वह ख़राब है तो आपको इसका पता ठीक उसी क्षण चलता है जब आप उस पर भरोसा कर रहे थे, और ठीक उसी तरीक़े से जो यह सुनिश्चित करता है कि कोई देख नहीं रहा था। एक ख़ामोश अलर्ट, अलर्ट न होने से भी बदतर है, क्योंकि अलर्ट न होने पर आप जानते हैं कि वह आपके पास नहीं है, जबकि ख़ामोश अलर्ट के बारे में आप मानते हैं कि वह आपके पास है।

इसलिए हमने यह भरोसा करना बंद कर दिया कि हमारे अलर्ट सिर्फ़ इसलिए काम करते हैं क्योंकि हमने उन्हें लिखा है, और इसे साबित करने के लिए एक हार्नेस बनाया। हम जो भी अलर्ट नियम तैनात करते हैं, उसके लिए हार्नेस एक वास्तविक संकेत भेजता है जिसे उसे ट्रिगर करना चाहिए, और फिर वह वही करता है जो असल में मायने रखता है: वह तब तक प्रतीक्षा करता है जब तक सूचना भौतिक रूप से एक कैप्चर रिले में न पहुँच जाए, एक छोटी सेवा जो उस फ़ोन की जगह लेती है जो अन्यथा बजता। मॉनिटरिंग टूल के अपने इंटरफ़ेस में फ़ायर होना मायने नहीं रखता। डिलीवरी मायने रखती है। संकेत से लेकर हाथ में आई एक संदेश तक का पूरा रास्ता, वरना यह एक विफलता है।

पहली वास्तविक रन ने ज़्यादातर नियमों को विफल कर दिया। यहाँ बताया गया है कि हरे डैशबोर्ड के पीछे क्या छिपा हुआ था।

प्रमुख अलर्ट मर चुका था

हमारे पास सबसे महत्वपूर्ण नियम वह है जो त्रुटि दर बढ़ने पर फ़ायर होता है: बहुत ज़्यादा HTTP 500, किसी को पेज करो। हार्नेस ने उस पर 450 वास्तविक सर्वर त्रुटियाँ भेजीं, यह सत्यापित किया कि उनमें से कम से कम 90% ने सचमुच 500 लौटाया, और प्रतीक्षा की। कुछ नहीं आया। जिस एकमात्र अलर्ट पर हम सबसे ज़्यादा भरोसा करना चाहते, वह एक बार भी फ़ायर नहीं हुआ था, और किसी वास्तविक घटना में फ़ायर नहीं होता।

इसकी वजह एक नाम था। हमारे मेट्रिक्स OpenTelemetry से आते हैं, जहाँ नाम बिंदुओं वाले होते हैं, जैसे http.server.request.duration। एक ट्यूटोरियल पढ़ने और क्वेरी लिखने के बीच कहीं, हमने मान लिया था कि मॉनिटरिंग बैकएंड उन बिंदुओं को अंडरस्कोर में नॉर्मलाइज़ कर देगा, जैसा कि बहुत सारे Prometheus टूल करते हैं, और हमने अलर्ट क्वेरी को अंडरस्कोर वाले रूप के हिसाब से लिखा। बैकएंड नॉर्मलाइज़ नहीं करता। वह बिंदुओं वाले नामों को ठीक वैसे ही रखता है जैसे भेजे गए थे। तो अलर्ट ने एक ऐसे मेट्रिक की क्वेरी की जो अस्तित्व में ही नहीं था, किसी से मेल नहीं खाया, हमेशा के लिए शून्य त्रुटि दर की गणना की, और वहीं शांत और हरा बैठा रहा, जबकि असली मेट्रिक, अपने असली बिंदुओं वाले नाम के तहत, उन 500 में से हर एक को दर्ज कर रहा था। क्वेरी इस तरह से ग़लत नहीं थी कि वह त्रुटि दे। वह इस तरह से ग़लत थी कि वह ख़ामोशी से खाली सेट लौटाती है, जिसे फिर अंकगणित एक बिल्कुल विश्वसनीय शून्य में बदल देता है।

वह नियम जिसने हर लॉग अलर्ट को अपने साथ गिरा दिया

वह ख़राब वाला था। अगला उससे भी बुरा था, और हार्नेस ने उसे सिर्फ़ इसलिए पकड़ा क्योंकि वह नियम के सही दिखने के बजाय डिलीवरी पर ज़ोर देता है।

हमारे अलर्ट की एक पूरी श्रेणी लॉग-आधारित है: लॉग्स में एक पंक्ति से मेल खाओ, फ़ायर हो जाओ। उनमें से हर एक ख़ालीपन के विरुद्ध मूल्यांकन कर रहा था, और इसकी वजह का अलर्ट नियमों से कोई लेना-देना नहीं था। यह एक रिटेंशन सेटिंग थी।

हम अपने लॉग रिटेंशन को बैकएंड के सेटिंग्स API के ज़रिए समायोजित करते हैं। वह API एक नए रिक्वेस्ट आकार के साथ एक नए वर्शन पर चला गया था, और हम अब भी पुराना आकार भेज रहे थे। पुरानी बॉडी अस्वीकार नहीं हुई। दूसरे छोर पर मौजूद डिकोडर उन फ़ील्ड्स को ख़ामोशी से अनदेखा कर देता है जिन्हें वह नहीं पहचानता, इसलिए उसने हमारे अब-अनजान फ़ील्ड्स को अनुपस्थित मानकर पढ़ा, जिस रिटेंशन को वह ढूँढ नहीं पाया उसके लिए डिफ़ॉल्ट मान ले लिया, और वह डिफ़ॉल्ट शून्य था। कॉल ने एक ख़ुशनुमा 200 लौटाया। और शून्य दिन का रिटेंशन का मतलब है तुरंत समाप्ति: नए लिखे गए लॉग डेटा पर तुरंत समाप्त होने की मुहर लग रही थी, इसलिए जिस स्टोर पर लॉग अलर्ट क्वेरी करते हैं वह व्यावहारिक रूप से हमेशा ख़ाली रहता था। पूरे समय इंजेशन बिल्कुल स्वस्थ दिख रहा था, क्योंकि लॉग्स आ रहे थे। वे बस उसी क्षण पुराने होकर समाप्त हो रहे थे जिस क्षण वे पहुँचते थे।

दोनों सिस्टम में से हर एक ने कुछ ऐसा किया जिसका बचाव किया जा सकता है। सेटिंग्स API ने विफल होने के बजाय एक ऐसी रिक्वेस्ट स्वीकार कर ली जिसे वह आंशिक रूप से नहीं समझता था, जो एक आम और अक्सर उचित विकल्प है। हमारी तैनाती ने एक ऐसी बॉडी भेजी जो एक वर्शन बदलाव के मुक़ाबले पुरानी पड़ चुकी थी। इनमें से किसी ने कुछ भी नहीं उठाया। नतीजा यह हुआ कि हमारी लॉग पाइपलाइन का सबसे परिणामकारी अकेला आँकड़ा, यानी एक लॉग कितने समय जीता है, ख़ामोशी से शून्य पर सेट कर दिया गया था, और एकमात्र दिखाई देने वाला लक्षण यह था कि हर लॉग अलर्ट स्थायी रूप से, स्वस्थ दिखते हुए, ख़ामोश था। अगर हम यह जाँच रहे होते कि नियम सही दिखते हैं, तो वे बिल्कुल सही दिखते। केवल यह जाँचना कि एक वास्तविक लॉग की गई घटना एक डिलीवर हुआ पेज उत्पन्न करती है, इसे उजागर कर सकता था, क्योंकि केवल वही जाँच सचमुच उस स्टोर से पढ़कर वापस लाती है जिसे ख़ाली कर दिया गया था।

वे अलर्ट जो हमेशा के लिए फ़ायर होते रहे, जो कभी न होने के बराबर है

एक तीसरी विफलता विपरीत दिशा में चली, और यही सूक्ष्म वाली है। मुट्ठीभर ऑडिट अलर्ट लगातार फ़ायर हो रहे थे। यह एक ख़ामोश अलर्ट के विपरीत लगता है, लेकिन इसका प्रभाव बिल्कुल वैसा ही है, और यह समझना कि क्यों, इस भटकाव के लायक़ है।

जब इनमें से कोई एक अलर्ट फ़ायर होता, तो मॉनिटरिंग बैकएंड मूल्यांकन के बारे में एक लॉग पंक्ति लिखता, एक ब्रेडक्रम्ब, और उस पंक्ति में उस अलर्ट का अपना फ़िल्टर एक्सप्रेशन पाठ के रूप में होता। वह ब्रेडक्रम्ब ख़ुद भी किसी और चीज़ की तरह एक लॉग के रूप में ग्रहण कर लिया जाता। तो अगली बार जब नियम का मूल्यांकन होता, तो उसे एक मेल खाती पंक्ति मिल जाती: पिछली बार की उसकी अपनी पदचाप। नियम स्वयं को बनाए रखने वाला बन गया था। वह हर मूल्यांकन में, हमेशा के लिए, अपनी ही परछाईं से मेल खाता था।

जो अलर्ट हमेशा फ़ायर होता रहता है, वह कभी भी न-फ़ायर होने से फ़ायर होने की ओर संक्रमण नहीं करता, और वही संक्रमण वह चीज़ है जो सूचना भेजता है। इसलिए अलर्टिंग परत ने, एक ऐसी स्थिति देखकर जो पहले से ही सच थी और सच बनी रही, आगे की सूचनाएँ दबा दीं, ठीक जैसा डिज़ाइन किया गया था, ताकि आपको किसी ऐसी बात के बारे में हर मिनट एक पेज से बचाया जा सके जिसके बारे में आपको पहले ही बता दिया गया है। नतीजा यह है कि एक वास्तविक घटना, एक सच्ची ऑडिट कार्रवाई जो पेज किए जाने लायक़ थी, एक ऐसे नियम तक पहुँची जो पहले से ही चालू पर अटका हुआ था और इसलिए कुछ नहीं बोला। एक अलर्ट जो स्थायी रूप से चालू पर अटका है, उतना ही मूक है जितना एक जो स्थायी रूप से बंद पर अटका है। इसका समाधान यह था कि उन नियमों को हमारी अपनी सेवाओं द्वारा उत्सर्जित लॉग पंक्तियों तक सीमित कर दिया जाए, ताकि वे बैकएंड की अपने बारे में की गई बकबक से मेल खाना बंद कर दें, एक जानबूझकर रखे गए अपवाद के साथ जिसका वास्तविक संकेत सचमुच हमारी सेवाओं के बाहर से आता है।

वह नियम जिसने एक पॉड की पहली साँस को सामान्य समझा

आख़िरी वाला दर की गणना के बारे में एक छोटा, तीखा सबक़ है। किसी मेट्रिक के परिवर्तन की दर पर बना अलर्ट किसी शृंखला के सबसे पहले नमूने से दर की गणना नहीं कर सकता, क्योंकि उससे पहले तुलना करने के लिए कुछ होता ही नहीं। इसलिए जब एक नए पॉड ने त्रुटियों का अपना पहला झोंका छोड़ा, तो वह झोंका उछाल के बजाय आधार बन गया, और जिस नियम को उसे पकड़ना चाहिए था उसने सिर्फ़ एक शुरुआती बिंदु देखा। हमने इसे हार्नेस में ऐसे संकेतों को दो लहरों में भेजकर ठीक किया, एक छोटी लहर शृंखला को स्थापित करने के लिए और उसके बाद एक वास्तविक लहर, जो इस बात का भी एक उचित वर्णन है कि उत्पादन ट्रैफ़िक अपने आप क्या करता है और एक कृत्रिम परीक्षण को जानबूझकर किसका अनुकरण करना पड़ता है।

हर विफलता एक ऐसी सीवन में छिपी थी जिसे आप केवल चलाकर ही ढूँढ सकते हैं

इनमें से हर एक निरीक्षण के लिए अदृश्य था। क्वेरीज़ सही पढ़ी जाती थीं। नियम तैनात थे। डैशबोर्ड हरे थे। इंजेशन स्वस्थ था। हर विफलता दो चीज़ों के बीच की एक सीवन में रहती थी, जिनमें से हर एक अलग-अलग ठीक थी: एक मेट्रिक नाम और नॉर्मलाइज़ेशन के बारे में एक धारणा, एक रिक्वेस्ट बॉडी और एक वर्शन बदलाव, एक अलर्ट और वह लॉग जो उसका अपना फ़ायर होना उत्पन्न करता है, एक दर फ़ंक्शन और एक शृंखला जो अभी-अभी शुरू हुई है। आप किसी सीवन को पढ़ नहीं सकते। आपको उसे चलाकर परखना होता है।

इसलिए मॉनिटरिंग को वैसा ही समझें जैसा वह है, यानी कोड, और ख़ास तौर पर ऐसा कोड जिसका उत्पादन में एकमात्र रन आपातकाल है। जो कोड केवल आपातकाल में चलता है, उसके परीक्षणों को हर दूसरे दिन चलना ज़रूरी है, क्योंकि आपको चेताने के लिए कोई कोमल पहली विफलता नहीं होती। एक वास्तविक संकेत को छोर से छोर तक भेजें, और शृंखला के बिल्कुल अंत में मौजूद ठोस परिणाम पर ज़ोर दें, यानी डिलीवर हुई सूचना पर, न कि बीच में सही दिखने वाले नियम पर। इससे कम कुछ भी यह परीक्षण करना है कि आपने एक अलर्ट लिखा, जो आप पहले से जानते थे, बजाय इसके कि यह परीक्षण करें कि वह फ़ायर होता है, जो एकमात्र चीज़ है जो आप वास्तव में चाहते थे।

अगर आप चाहेंगे कि आपका आइडेंटिटी प्रोवाइडर पहले से ही ख़ुद पर इसी तरह नज़र रखे, तो Authagonal अपने ख़ुद के अलर्टिंग को ठीक इसी हार्नेस के ज़रिए चलाता है, ताकि जिस दिन कुछ टूटे, वह पेज जो फ़ायर होना चाहिए, सचमुच फ़ायर हो।