बेंचमार्क का जाल
बेंचमार्क दिखाने वाले कभी भी आपको पूरी तस्वीर नहीं दिखाते। वे ऐसे हैंडलर को मापते हैं जो लगभग कुछ भी नहीं करता। एक JSON Hello World। एक स्टैटिक स्ट्रिंग। उस शून्य में, Fiber स्पष्ट रूप से जीत जाता है। यह Gin की तुलना में प्रति सेकंड अधिक रिक्वेस्ट सर्व करता है और कम मेमोरी का उपयोग करता है। यह वास्तविक इंजीनियरिंग है। लेकिन आपका एप्लिकेशन कोई शून्य नहीं है।
मैं केन्या में एक रियल-टाइम सुरक्षा ऐप बना रहा हूँ। यह नैरोबी और मोम्बासा के उपयोगकर्ताओं से GPS कोऑर्डिनेट्स लेता है और अपराध के हॉटस्पॉट या सड़क दुर्घटनाओं के बारे में अलर्ट भेजता है। इसका मतलब है PostgreSQL राइट्स, Firebase Cloud Messaging कॉल्स, और रिवर्स जियोकोडिंग लुक्सअप्स। जब आपके स्टैक में नेटवर्क हॉप्स और डेटाबेस राउंड-ट्रिप्स शामिल होते हैं, तो रूटिंग पर नैनोसेकंड बचाने वाला फ्रेमवर्क अदृश्य हो जाता है। बॉटलनेक (bottleneck) कभी राउटर नहीं होता। यह SMS गेटवे का टाइम आउट होना होता है। यह घटना रिपोर्टों की एक टेबल को स्कैन करने वाली PostGIS क्वेरी होती है। रॉ थ्रूपुट (Raw throughput) तब तक लुभावना लगता है जब तक कि वह प्रोडक्शन से नहीं मिलता।
स्टैंडर्ड लाइब्रेरी के प्रति वफादारी
Gin सीधे net/http पर बना है। यह सुनने में जितना लगता है, उससे कहीं अधिक महत्वपूर्ण है।
हर Go डेवलपर net/http को जानता है। डिबगिंग में परिचित स्टैक ट्रेसेस (stack traces) मिलते हैं। पाँच साल पहले लिखा गया मिडलवेयर भी बिना किसी तामझाम के काम कर जाता है। रिक्वेस्ट और रिस्पॉन्स ऑब्जेक्ट्स बिल्कुल वैसे ही व्यवहार करते हैं जैसा Go डॉक्यूमेंटेशन कहता है। Gin अनुमानित (predictable) रहता है क्योंकि यह स्वयं भाषा के करीब रहता है।
Fiber उस आधार को fasthttp से बदल देता है, जो ज़ीरो-एलोकेशन परफॉरमेंस के लिए ऑप्टिमाइज़ किया गया एक कस्टम HTTP इंजन है। इसे हासिल करने के लिए, यह Request/Response Pooling का उपयोग करता है। प्रत्येक रिक्वेस्ट के बाद गारबेज कलेक्टर को मेमोरी वापस लेने देने के बजाय, Fiber मेमोरी ब्लॉक्स को रीसायकल करता है। यह उन्हें साफ करता है और अगले आने वाले कनेक्शन को सौंप देता है। यही ट्रिक इसकी गति का स्रोत है। यह खतरे का स्रोत भी है।
पुन: उपयोग की गई मेमोरी का छिपा हुआ खतरा
थ्योरी में पूलिंग सुरक्षित लगती है। व्यवहार में, यह एक सूक्ष्म अनुबंध (contract) पेश करती है: आपको अपने हैंडलर के वापस लौटने के बाद रिक्वेस्ट डेटा का रेफरेंस कभी भी नहीं रखना चाहिए। यदि कोई goroutine रिक्वेस्ट से अधिक समय तक जीवित रहता है, या यदि आप बाद में प्रोसेसिंग के लिए रिक्वेस्ट बॉडी का एक स्लाइस कैप्चर करते हैं, तो Fiber उस मेमोरी को वापस ले लेगा और उसे अगले उपयोगकर्ता को सौंप देगा।
इसका परिणाम डेटा का दूषित होना (contamination) है। यह बग यूनिट टेस्ट में दिखाई नहीं देता। यह नैरोबी के एक ड्राइवर के GPS कोऑर्डिनेट के अचानक किसुमु (Kisumu) के मैप पर दिखाई देने के रूप में सामने आता है। यह एक उपयोगकर्ता के JWT टोकन के दूसरे उपयोगकर्ता के कॉन्टेक्स्ट में लीक होने के रूप में सामने आता है। ये समय-आधारित (temporal) विफलताएं हैं। जब आप लॉगिंग जोड़ते हैं तो वे गायब हो जाते हैं क्योंकि लॉगिंग करने से नई मेमोरी एलोकेट होती है और टाइमिंग बदल जाती है। आप अंततः भूतों का पीछा करते रह जाते हैं।
Gin के साथ, स्टैंडर्ड लाइब्रेरी एक फ्रेश रिक्वेस्ट ऑब्जेक्ट एलोकेट करती है। यहाँ दूषित करने के लिए कोई पूल नहीं है। वह सुरक्षा तब मायने रखती है जब आपका ऐप वास्तविक सुरक्षा से जुड़े वास्तविक स्थानों को संभालता है।
इकोसिस्टम का गुरुत्वाकर्षण
अनुकूलता (compatibility) का एक शांत टैक्स भी होता है।
अधिकांश Go मिडलवेयर net/http मानकर चलते हैं। JWT वैलिडेटर्स, OpenTelemetry एक्सपोर्टर्स, CORS हैंडलर्स और रेट लिमिटर्स सभी स्टैंडर्ड इंटरफ़ेस का उपयोग करते हैं। Gin उस इंटरफ़ेस को नेटिवली समझता है। बस एक मिडलवेयर डालें और यह काम करने लगता है।
Fiber का अपना कॉन्टेक्स्ट टाइप और अपना हैंडलर सिग्नेचर है। आपको एडेप्टर (adapters) की आवश्यकता होती है। कभी-कभी एडेप्टर आधिकारिक होता है। कभी-कभी यह अपस्ट्रीम लाइब्रेरी से एक साल पीछे रहता है। कभी-कभी लोड के तहत यह थोड़ा अलग व्यवहार करता है। जब आप एक छोटी टीम चला रहे होते हैं, तो आपके पास यह डिबग करने के लिए अतिरिक्त घंटे नहीं होते कि कोई ऑथेंटिकेशन मिडलवेयर आपके लैपटॉप पर क्यों काम करता है लेकिन सर्वर पर वैध टोकन को क्यों अस्वीकार कर देता है। आप चाहते हैं कि go get उबाऊ (boring) हो। Gin इसे उबाऊ रखता है, जो ठीक वही है जो आप रात के 3 बजे चाहते हैं।
ऐप को वास्तव में किस चीज़ की आवश्यकता है
मेरी सुरक्षा सेवा समवर्ती (concurrent) उपयोगकर्ताओं से हर कुछ सेकंड में लोकेशन पिंग प्रोसेस करती है। यह उच्च जोखिम वाली सड़कों के लिए जियोफेंसिंग (geofencing) करती है। यह नोटिफिकेशन ट्रिगर करती है। एक धीमा अलर्ट निराशाजनक होता है। एक गलत अलर्ट खतरनाक होता है। दूषित पूल्ड मेमोरी के कारण किसी को सक्रिय दुर्घटना स्थल की ओर भेजना अस्वीकार्य है।
यहाँ विश्वसनीयता (reliability), थ्रूपुट से बेहतर है। मुझे ऐसे स्टैक ट्रेसेस चाहिए जो pprof अटैच करने पर समझ में आ सकें। मुझे ऐसे मेमोरी प्रोफाइल चाहिए जिनमें मुझे कस्टम मेमोरी पूल के आंतरिक जीवनचक्र (internal lifecycle) को समझने की आवश्यकता न हो। मुझे चाहिए कि जो अगला डेवलपर इस प्रोजेक्ट को संभाले, वह fasthttp ऑब्जेक्ट मॉडल सीखे बिना एक दोपहर में काम शुरू कर सके। Gin मुझे वह देता है। ऑपरेशनल सैनिटी (operational sanity) बेंचमार्क में नहीं दिखती, लेकिन यही एक सर्विस को बनाए रखती है
