Steven
Steven6 मिनट पढ़ें

जिस दिन हमारे ऐप ने खुद पर ही DDoS कर दिया

लंबित अपलोड का एक ढेर, जो स्टार्टअप पर एक साथ छोड़ दिया गया, हर GeekBye क्लाइंट को हमारे अपने सर्वरों पर एक छोटे से डिनायल-ऑफ-सर्विस हमले में बदल गया। वह फिक्स — और जो connection-liveness सीढ़ी उसने हमसे बनवाई — v2 ने हमें जो सबसे उपयोगी चीज़ें सिखाईं, उनमें से एक है।

विश्वसनीयता
नेटवर्किंग
इंजीनियरिंग
GeekBye रिलीज़
जिस दिन हमारे ऐप ने खुद पर ही DDoS कर दिया

हर डिस्ट्रिब्यूटेड-सिस्टम्स इंजीनियर आखिरकार भीड़-हमले (thundering herd) से मिलता ही है: क्लाइंट्स का एक हुजूम एक ही पल में एक ही काम करता है, और उनके पीछे का साझा संसाधन ढह जाता है। आमतौर पर यह किसी और के क्लाइंट होते हैं। GeekBye v2.0.1 में यह हमारे थे, और हमलावर हमारा अपना ऐप था।

एक notetaker खुद पर कैसे हमला करता है

GeekBye रिकॉर्डिंग को आपके Google Drive पर अपलोड कर सकता है। अगर कोई रिकॉर्डिंग तुरंत अपलोड न हो पाए — आप ऑफलाइन थे, ऐप बंद हो गया, Drive ने अटकल दी — तो वह बाद में रीट्राई करने के लिए एक बैकलॉग में चली जाती है। समझदारी की बात है।

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

हमारे बैकएंड ने सही काम किया और उस बाढ़ को rate-limit कर दिया। और वहीं से दूसरे दर्जे का नुकसान शुरू हुआ। एक rate-limit रिस्पॉन्स एक HTTP 429 है, और 429, अगर आप सावधान न हों, बहुत हद तक दूसरे फेल्योर्स जैसा दिखता है:

  • स्टार्टअप पर होने वाले प्रोफाइल फ़ेच को एक 429 मिला और ऐप ने इसे auth फेल की तरह लिया — यूज़र्स को एक बिलकुल वैध सेशन से पल भर के लिए लॉग आउट कर दिया।
  • ट्रांसक्रिप्शन कनेक्शन को एक सामान्य 429 मिला और उसने एक ऑडियो-लिमिट-पूरी-हो-गई अपग्रेड प्रॉम्प्ट दिखाया — पैसे देने वाले यूज़र्स को बताया कि वे एक ऐसे quota से टकरा गए हैं जिससे वे टकराए ही नहीं थे।

तो एक मूल कारण — एक बिना-लय वाला बैकलॉग — ने तीन दिखने वाले लक्षण पैदा किए: सर्वर पर दबाव, झूठे लॉगआउट, और झूठे अपसेल। खुद-पर-DoS का यही क्लासिक रूप है: लोड बुरा है, पर यूज़र्स असल में जो महसूस करते हैं, वह उस लोड के लक्षणों की गलत व्याख्या है।

फिक्स, दो परतों में

भगदड़ रोको। अपलोड बैकलॉग अब लय में है — रिक्वेस्ट backoff के साथ फैलती हैं, और किसी भी 429 के बाद एक cooldown, ताकि क्लाइंट एक दबे हुए सर्वर पर और ज़ोर से टेक लगाने के बजाय उससे पीछे हट जाए। एक भीड़-हमला (thundering herd) एक सलीकेदार कतार बन जाता है।

गलत व्याख्या रोको। स्टार्टअप के दौरान एक क्षणिक 429 या 5xx अब आपको लॉग आउट नहीं करता — क्लाइंट "सर्वर बस पल भर के लिए व्यस्त है" को "आपका सेशन अमान्य है" से अलग करता है। और ट्रांसक्रिप्शन रास्ते पर एक सामान्य rate-limit 429 अब एक ऑडियो-quota एरर का भेस नहीं धरता; सिर्फ एक सच्चा quota रिस्पॉन्स ही अपग्रेड प्रॉम्प्ट दिखाता है। जो सबक टिका रहा: एक error code, एक error का मतलब नहीं है। 429 का मतलब है "धीमे हो जाओ", न कि "आप अनधिकृत हैं" और न ही "आपका quota खत्म है" — और क्लाइंट को यह फर्क पता होना चाहिए।

जो liveness सीढ़ी उसके बाद बनी

भीड़ को लय में लाने से एक ज़्यादा खामोश समस्या उजागर हुई: जब एक कनेक्शन लोड के नीचे सचमुच खराब हो गया, तो हमें कितनी जल्दी पता चला? हम जितनी चाहते थे, उससे धीरे। तो v2.0.4 ने रियल-टाइम ट्रांसक्रिप्शन के लिए एक connection-liveness सीढ़ी बनाई:

  • Heartbeat — ट्रांसक्रिप्शन सॉकेट को एक तय अंतराल पर ping किया जाता है, ताकि एक चुपचाप मरा कनेक्शन अगले ऑडियो चंक के फेल होने का इंतज़ार करने के बजाय कुछ सेकंडों में पकड़ा जाए।
  • नेटवर्क वापस आने पर एक reconnect किक — जब OS बताता है कि नेटवर्क लौट आया, तो ऐप उस खोज पर ठोकर खाने का इंतज़ार करने के बजाय सक्रिय रूप से कनेक्शन फिर से बनाता है।
  • Control ping/pong liveness — एक एप्लिकेशन-स्तर का आना-जाना जो सिर्फ "सॉकेट खुला है" नहीं, बल्कि "दूसरा सिरा सचमुच जवाब दे रहा है" की पुष्टि करता है।
  • एक service के मालिकाने वाला reconnect गेट — एक ही जगह तय करती है कि reconnect करना है या नहीं, बजाय इसके कि ऐप के कई हिस्से इसे करने के लिए दौड़ें और एक-दूसरे को कुचलें।

इनमें से कुछ भी चमकदार नहीं है। यह सब मिलकर ही वह वजह है कि एक v2 सेशन ऐसा महसूस होता है जैसे वह बस… जुड़ा रहता है।

यह इस हफ्ते फिर हुआ — एक स्तर नीचे

यही वह हिस्सा है जो इसे महज़ एक जंग-की-कहानी से ज़्यादा बनाता है। यही फेल्योर क्लास कुछ दिन पहले फिर उभरी, हमसे एक स्तर नीचे। एक अकेले क्लाइंट ने 400 मिलीसेकंड से कम में 21 ट्रांसक्रिप्शन-सेशन स्टार्ट दागे — और हमारे स्पीच प्रोवाइडर की अकाउंट-भर की 15 समवर्ती रिक्वेस्ट की लिमिट से टकरा गया। साझा इन्फ्रास्ट्रक्चर पर एक भगदड़, बिलकुल उस अपलोड बैकलॉग की तरह, बस अपने बैकएंड के बजाय एक dependency पर तानी हुई।

आकार एक जैसा है, और फिक्स भी: भगदड़ मचाते क्लाइंट को एक single-flight गार्ड चाहिए ताकि वह एक इरादे के लिए बीस स्टार्ट न दाग सके, और साझा संसाधन को एक प्रति-यूज़र सीमा चाहिए ताकि एक क्लाइंट पूरा पूल न खा जाए। हमने इसका क्लाइंट-साइड वर्ज़न पहले बनाया है — वह अपलोड pacer ही यह पैटर्न है। अब हम इसे सेशन स्टार्ट पर लगाते हैं। भीड़-हमला (thundering herd) एक ऐसा bug नहीं जिसे आप एक बार ठीक करते हैं; यह एक आकार है जिसे आप हर उस जगह पहचानना सीखते हैं जहां क्लाइंट एक साझा सीमा से मिलते हैं।

तीन बातें साथ ले जाने के लिए

  1. आपके अपने क्लाइंट एक ऐसा लोड टेस्ट हैं जो आपने शेड्यूल नहीं किया। जो भी चीज़ स्टार्टअप-पर-बैच करती है, लॉन्च-पर-रीट्राई करती है, या जागने-पर-रीकनेक्ट करती है, वह पर्याप्त यूज़र्स का इंतज़ार करता एक भीड़-हमला (thundering herd) है। यूज़र्स होने से पहले उसे लय में लाइए।
  2. कोड को मतलब से अलग करो। 429 अस्तित्व में सबसे ज़्यादा गलत पढ़ा जाने वाला स्टेटस है। "धीमे हो जाओ" न तो "लॉग आउट" है और न "हमें पैसे दो"। हर फेल्योर को उसी तक रूट करो जो वह असल में मतलब रखता है।
  3. liveness एक सीढ़ी है, एक flag नहीं। "क्या कनेक्शन ज़िंदा है?" के अलग-अलग परतों पर कई ईमानदार जवाब हैं — सॉकेट खुला है, बाइट बह रहे हैं, दूसरा सिरा जवाब दे रहा है, नेटवर्क मौजूद है। एक मज़बूत ऐप एक से ज़्यादा जांचता है।

यही GeekBye v2 की शांति के नीचे की बे-चमक मशीनरी है। जिन विश्वसनीयता फ़ीचर्स को इसने मुमकिन बनाया, उनके लिए देखें आपका AI notetaker खराब Wi-Fi पर क्यों रुक जाता है और जब फ़ायरवॉल WebSocket को ब्लॉक करे तब लाइव ट्रांसक्रिप्शन (v2.0.8)। इस सीरीज़ की पड़ोसी रिलीज़ों के लिए देखें आपका AI notetaker मीटिंग के बीच में रिकॉर्डिंग क्यों बंद कर देता है (v2.0.9)।

संबंधित लेख

ख़ामोशी भार-वहन कर रही थी
Steven
Steven9 मिनट पढ़ें

ख़ामोशी भार-वहन कर रही थी

GeekBye v1 की आख़िरी दो releases एक ही असहज सच्चाई के बारे में हैं: एक असली network पर real-time transcription लॉसलेस नहीं है, और ईमानदार चाल है यह दिखावा बंद करना कि वह है। v1.8.20 ने एक reconnect के दौरान हर audio chunk को गिराने से पहले उसकी एक प्रति disk पर रखी, और transcript के अंतरालों को ज़ोर से चिह्नित करना शुरू किया। v1.9.0 ने bandwidth बचाने के लिए silence भेजना बंद किया — और पाया कि वह silence ही वह ठीक-ठीक संकेत था जिससे transcriber जानता था कि एक वाक्य ख़त्म हो चुका है। चीज़ें फेंक देने की क़ीमत के बारे में दो releases।

इंजीनियरिंग
Audio
विश्वसनीयता
एक कनेक्शन टूटने से पूरा ऐप क्रैश नहीं होना चाहिए — पर हमारा हुआ
Steven
Steven7 मिनट पढ़ें

एक कनेक्शन टूटने से पूरा ऐप क्रैश नहीं होना चाहिए — पर हमारा हुआ

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

इंजीनियरिंग
विश्वसनीयता
Electron
वो सेफ्टी चेक जिसने हमारी ऐप को क्विट करना नामुमकिन बना दिया
Steven
Steven7 मिनट पढ़ें

वो सेफ्टी चेक जिसने हमारी ऐप को क्विट करना नामुमकिन बना दिया

ऑटो-अपडेट अब तक का सबसे मुश्किल फीचर था जो हमने शिप किया — चार दिन में छह रिलीज़ सिर्फ इसे ऐप को ब्रिक करने से रोकने के लिए। सबसे बुरा बग वो था जो हमने खुद सावधान रहने की कोशिश में पैदा किया: एक 500-मिलीसेकंड का "सेफ्टी" चेक जिसने एक फेल्ड अपडेट को ऐसे प्रोसेस में बदल दिया जिसे आप शाब्दिक रूप से क्विट ही नहीं कर सकते थे।

इंजीनियरिंग
Electron
विश्वसनीयता