← All posts

हमारी सर्विस डिस्कवरी ने अपनी ही विफलता पकड़ ली और खुद को बंद कर दिया

Authagonal·July 1, 2026
authdistributed-systemsclusteringleader-electionazureaks

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

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

क्लस्टर असल में किसलिए है

एक auth सर्वर उपलब्धता के लिए एक से ज़्यादा रेप्लिका के रूप में चलता है। ये रेप्लिका ज़्यादातर स्वतंत्र होते हैं: इनमें से कोई भी किसी token को मान्य कर सकता है या किसी पासवर्ड की जाँच कर सकता है। लेकिन कुछ जॉब को एक बार चलना चाहिए, हर रेप्लिका पर एक-एक बार नहीं। वह डेटा-रिटेंशन स्वीप जो समय-समाप्त रिकॉर्ड हटाती है। वह पास जो ग्राहकों के webhooks फ़ायर करती है। ऐसा कुछ भी जो बाहर की ओर पहुँच बनाता है और जिसका कोई साइड-इफ़ेक्ट होता है। इनके लिए आपको दो चीज़ों की ज़रूरत है जिन पर क्लस्टर को सहमत होना ही पड़ता है: सदस्य कौन हैं, और उनमें से कौन-सा वह leader है जो singleton काम चलाता है।

हमारा मूल जवाब वही क्लासिक जवाब था: gossip। हर node अपने पीयरों से बातचीत करता है, मेंबरशिप इस बात से उभरने वाला गुण है कि कौन पहुँच-योग्य है, और समूह सहमत समुच्चय में से एक leader चुन लेता है। यह एक खूबसूरत डिज़ाइन है। यह यह भी मान लेता है कि nodes एक-दूसरे को ढूँढ सकते हैं, और पुराना कोड उन्हें multicast से ढूँढता था: स्थानीय नेटवर्क सेगमेंट पर हाँक लगाओ और देखो कौन जवाब देता है।

यह चुपचाप क्यों टूट गया

मैनेज्ड Kubernetes multicast नहीं ढोता। और जो क्लाउड नेटवर्क आप खुद नहीं बनाते, उनमें से भी लगभग कोई नहीं ढोता। हाँक निकलती और कुछ भी वापस नहीं आता, हर बार। तो डिस्कवरी "विफल" हो जाती, catch ब्लॉक फ़ायर हो जाता, और हर node यह नतीजा निकाल लेता कि वह दुनिया में अकेला है।

एक रेप्लिका पर यह हानिरहित है। तीन पर यह बनावट से ही split-brain है: तीन nodes, हर एक को पक्का यकीन कि वही इकलौता सदस्य है, हर एक खुद को leader चुनता हुआ, हर एक singleton काम चलाता हुआ। रिटेंशन स्वीप तीनों पर चली। webhook पास ने हर ग्राहक का webhook तीन-तीन बार फ़ायर किया। इनमें से किसी ने भी कोई एरर नहीं फेंका, क्योंकि हर अकेले node की नज़र से सब कुछ ठीक था। बग कोई क्रैश नहीं था; वे तीन प्रोग्राम थे, हर एक एक ऐसी दुनिया में बिलकुल सही बर्ताव कर रहा था जिसे उसने गलत समझ रखा था।

हल यह था कि पीयरों को ढूँढना ही पूरी तरह बंद कर दिया जाए

यह रहा वह नया नज़रिया जिसने पूरी चीज़ को चंद पंक्तियों में समेट दिया: हम एक बातूनी प्रोटोकॉल के ज़रिए वह तथ्य फिर से बनाने की कोशिश कर रहे थे जिसे प्लेटफ़ॉर्म पहले ही हमारे लिए मज़बूत संगति (strong consistency) के साथ संजोकर रखता है। हम एक ऐसे क्लाउड पर चलते हैं जो हमें एक मज़बूती से संगत स्टोर सौंपता है। अगर एक ऐसी जगह पहले से मौजूद है जिस पर सब सहमत हो सकते हैं, तो leader चुनाव के लिए पीयरों के बीच आम सहमति की कोई ज़रूरत ही नहीं।

तो leadership एक blob lease बन गई। एक blob, एक lease। जो भी lease धारण करता है वही leader है। lease में एक टाइमआउट होता है, सो जो leader मर जाता है वह नवीनीकरण बंद कर देता है और lease अगले लेने वाले के लिए खुद को मुक्त कर देता है। न कोई पीयर-डिस्कवरी है, न quorum का गणित, न multicast, और न ही कोई catch ब्लॉक जो उसे बंद करने की ताक में बैठा हो। समन्वय और इवेंट एक छोटी-सी टेबल पर सवार होकर चलते हैं जो nodes के बीच एक बस की तरह काम करती है।

वह गुण जिससे प्यार होने की मुझे उम्मीद नहीं थी: आप Storage Explorer खोलकर क्लस्टर के मन को देख सकते हैं। इस वक़्त lease किसके पास है। बस पर क्या-क्या कतार में लगा है। मेंबरशिप अब किसी प्रोटोकॉल के व्यवहार से अनुमान लगाने वाली चीज़ नहीं रही, वह एक ऐसी पंक्ति बन गई जिसे आप पढ़ सकते हैं। जब यह तय करने वाली चीज़ कि आपके विनाशकारी जॉब कौन चलाएगा, एक ऐसा मान हो जिसे आप देख सकते हैं, तो डीबगिंग पुरातत्व-खुदाई होना बंद कर देती है।

वह हिस्सा जहाँ हमने जान-बूझकर एक चतुर चीज़ हटा दी

जब हाथ डाल ही दिया था, तो हमने एक conflict-free replicated data type पर बना एक डिस्ट्रिब्यूटेड रेट-लिमिटर हटा दिया, एक ऐसा CRDT जो बिना किसी समन्वय के हर node के काउंटरों को एक वैश्विक गिनती में मिला देता था। वह सचमुच सुंदर था। पर वह एक ऐसी समस्या भी हल कर रहा था जिसे हम कहीं और सरका चुके थे। वैश्विक रेट लिमिट का ठिकाना किनारे (edge) पर है, जहाँ रिक्वेस्ट आती हैं, न कि क्लस्टर के भीतर एक ऐसी डेटा-संरचना से दोबारा बनाई जाए जिसे समझाने के लिए एक पूरा पैराग्राफ़ चाहिए। बाहर।

चलते हुए कोड को हटाना तब तक एक नुकसान-सा लगता है, जब तक आप यह न गिन लें कि अब आपको क्या-क्या बनाए नहीं रखना पड़ेगा। हमने gossip परत, multicast वाली धारणा, खुद को बंद कर देने वाला catch ब्लॉक, और CRDT, सब हटा दिए, और इन सबकी जगह एक lease और एक टेबल रख दी। पंक्तियों की गिनती घट गई, और सिस्टम जिन अवस्थाओं में हो सकता है उनकी संख्या भी उसके साथ घट गई।

निचोड़ में, सबक़

gossip और multicast वाली मेंबरशिप bare metal पर या ऐसे फ़्लैट नेटवर्क पर सही औज़ार हैं जिसे आप नियंत्रित करते हैं। वे एक ऐसे प्लेटफ़ॉर्म पर गलत औज़ार हैं जिसका नेटवर्क उसी तंत्र को नहीं ढोता जिस पर वे निर्भर हैं, और इसका विफलता-रूप कोई शोरगुल वाला क्रैश नहीं है; वह एक खामोश फ़ॉलबैक है जो देखने में ऐसा लगता है मानो काम कर रहा हो। किसी डिस्ट्रिब्यूटेड-सिस्टम पैटर्न को मैनेज्ड इंफ़्रास्ट्रक्चर पर ले जाने से पहले पूछिए कि वह नेटवर्क के बारे में क्या मान बैठता है, और पूछिए कि प्लेटफ़ॉर्म आपको पहले से मुफ़्त में क्या दे रहा है। हमारा प्लेटफ़ॉर्म पहले से ही एक मज़बूती से संगत स्टोर चला रहा था। उस स्टोर में एक lease, उस प्रोटोकॉल के मुकाबले जिसे हम ढोते आ रहे थे, एक ज़्यादा सरल और ज़्यादा सही leader चुनाव था, और यह एक ऐसा चुनाव है जिसे हम देख सकते हैं।

अगर आप auth चलाते हैं, तो वे हिस्से जो आपकी कुंजियाँ संभालते हैं और तय करते हैं कि आपके विनाशकारी जॉब कौन चलाएगा, आपके पास मौजूद सबसे उबाऊ और सबसे आसानी से जाँचे जा सकने वाले हिस्से होने चाहिए। हमने अपने हिस्सों को उबाऊ बना दिया। यह एक अपग्रेड था।