उबाऊ कोड के साथ 1 अरब वेब रिक्वेस्ट सर्व करने का अनुभव
(notes.billmill.org)- अमेरिकी Medicare health insurance plan comparison system को फिर से बनाते समय, सिर्फ़ verified technologies (Postgres, golang, React आदि) से बनी एक सरल संरचना के साथ 1 अरब से अधिक वेब रिक्वेस्ट को स्थिरता से संभालने का अनुभव साझा किया गया है
- आर्किटेक्चर को सरलता और स्थिरता के लक्ष्य के साथ डिज़ाइन किया गया, जिससे औसत 10ms से कम response time और बहुत कम failure rate हासिल हुई
- innovation token को सिर्फ़ मुख्य structural separation (3 बड़े modules, gRPC communication) में न्यूनतम रूप से इस्तेमाल किया गया, और बाकी सब जगह उबाऊ लेकिन भरोसेमंद तरीकों को चुना गया
- DB schema management, ETL pipeline, testing, logging, documentation, CLI tools तक सभी operational elements को दोहराए जा सकने वाले और सरल तरीके से बनाया गया, जिससे पूरी टीम आसानी से समझ और maintain कर सके ऐसा system तैयार हुआ
- यह साफ़ तौर पर दिखाता है कि continuous quality control और मज़बूत teamwork बड़े सरकारी प्रोजेक्ट्स में भी काम करते हैं
Serving a billion web requests with boring code
High level
- ढाई साल तक अमेरिकी सरकार की Medicare plan comparison और purchase website के lead developer रहे
- औसतन प्रतिदिन 50 लाख API requests संभालीं, औसत response time 10ms से कम रहा, और 95% requests 100ms से नीचे रहीं
- outage rate बहुत कम थी, इतनी कि वास्तविक engineers को तड़के जगाकर बुलाने की घटनाएँ उँगलियों पर गिनी जा सकें
- Postgres, golang, React जैसी ऐसी verified technologies से ही सिस्टम बनाया गया जिन्हें कोई भी समझ सके, और लगातार स्थिर system तैयार किया गया
Boring über alles
- सर्वोच्च सिद्धांत था 'उबाऊ और verified technology' को प्राथमिकता देना (** Choose Boring Technology**)
- innovation की कोशिशें सिर्फ़ वहीं की गईं जहाँ वे बिल्कुल ज़रूरी थीं, और innovation token को बहुत बचाकर इस्तेमाल किया गया
- जटिल और चमकदार solutions की जगह स्थिर और स्पष्ट technology और process को पसंद किया गया
The boring bits
- Postgres: data storage का केंद्र, reliability और scalability दोनों को पूरा करता है. जटिल search (जैसे faceted search) भी Postgres से ही हल की गई
- golang: build और deploy तेज़, और binary artifacts स्पष्ट. error handling सीधी-सादी, इसलिए नए team members भी आसानी से ढल सकते हैं
- React: SPA frameworks में सबसे ज़्यादा verified, और team members पहले से इससे परिचित थे. accessibility और कई devices का support भी महत्वपूर्ण था
- लंबे समय में bundle size और speed degradation की समस्या आई, लेकिन उस समय सीमित समय में परिणाम देने के लिए यह सबसे अच्छा विकल्प था
The innovation tokens
- Modular backend: पूरे backend को न तो microservices बनाया गया, न monolith, बल्कि 3 बड़े modules (druginfo, planinfo, beneinfo) में बाँटा गया
- हर module अलग Postgres DB का उपयोग करता है, और data sharing सिर्फ़ gRPC के ज़रिए होती है
druginfo: pharmacy, insurance, packaging आदि के संयोजनों से ज्यामितीय रूप से बढ़ने वाली drug pricing information को बहुत बारीकी से index करता है, और जटिल preprocessing व performance optimization की ज़रूरत पड़ती हैplaninfo: रोज़ नया CMS data लेकर पूरे DB को फिर से बनाकर उपयोग करता है (immutability बनाए रखते हुए)beneinfo: वास्तविक subscriber information रखने वाला एकमात्र भाग, जहाँ sensitive PII (personal information) को न्यूनतम रखा गया. data leak risk कम करने के लिए design और operations पर विशेष ध्यान दिया गया
- gRPC: modules के बीच communication interface को code में स्पष्ट रूप से define करने का लाभ. automation tools के साथ integration भी बहुत अच्छा
- लेकिन build, tooling, debugging जटिल थे, और JSON API की तुलना में यह कम intuitive भी लगा
- grpc-gateway के ज़रिए web client support और बड़े traffic को बिना दिक्कत संभाला गया
Strict backwards compatibility
- API और database की backwards compatibility को सख़्ती से बनाए रखा गया
- public API के fields कभी delete नहीं किए गए, और जब तक security issue न हो वे हमेशा के लिए रखे गए
- DB columns जोड़ना आसान था, लेकिन हटाने के लिए कई चरणों वाली verification प्रक्रिया अपनाई गई (references हटाना → कुछ हफ़्ते इंतज़ार → वास्तविक deletion)
- यही अनुशासन तेज़ बदलाव की रफ़्तार और स्थिर deployment व operations की बुनियाद बना
Faceted search
- ElasticSearch की जगह सिर्फ़ Postgres से faceted search लागू की गई
- अच्छी तरह indexed plan table पर conditions को जोड़ने वाले लगभग 250 lines के एक function से पूरा search logic संभाला गया
- business requirements पर ध्यान रखते हुए, अनावश्यक जटिलता के बिना सरल समाधान चुना गया
Database
-
creation
- DB schema को नंबर लगे .sql files में manage किया गया, जिन्हें क्रम से load कर reliability सुनिश्चित की गई
planinfo/beneinfoDB रोज़ फिर से बनाए जाते थे, इसलिए migration की ज़रूरत नहीं थी. version mismatch जैसी configuration errors होने पर app को शुरू ही न होने देने वाला design रखा गया
-
ETL
- हर data source के लिए shell scripts से S3 पर load किया गया → cron के ज़रिए EC2 instance नया ETL code/data लाकर नया RDS DB बनाता था
- Postgres के COPY statement का सक्रिय उपयोग किया गया, ताकि INSERT की जगह bulk data loading कुशलतापूर्वक हो सके
- रोज़ 2~4 घंटे में सैकड़ों मिलियन rows को नए DB में बदला जा सकता था
-
models
- xo library से DB models auto-generate किए गए, और custom templates से team के मुताबिक code generation किया गया
-
testing
- सबसे बड़ी गलती यह थी कि sqlmock आधारित tests बहुत ज़्यादा बना दिए गए, जिससे बार-बार बदलते data के बीच maintenance बहुत मुश्किल हो गई
- अगर वास्तविक immutable DB पर test किया जाता, तो वह अधिक प्रभावी होता
-
Local database for development
- हर table के partial data को auto-generate करने वाली scripts से, हर developer छोटे local DB पर वास्तविक data आधारित testing और development कर सकता था
- DB बहुत बड़ा होने से पहले ऐसे tools बना लिए जाएँ, तो पूरी team की development efficiency बहुत बढ़ जाती है
Miscellaneous tooling
- विभिन्न operations और observability automation के लिए CLI tools को shell scripts में बनाया गया, और सभी utility features को एक जगह समेटकर manage किया गया
- Splunk logs को Slack command से तुरंत graph के रूप में visualize करने जैसे field-oriented tools सक्रिय रूप से बनाए और उपयोग किए गए
Logging
- request के entry point पर request id बनाई जाती थी, और वही id हर log context से जुड़कर कहीं से भी tracing संभव बनाती थी
- zerolog से सुरक्षित और व्यवस्थित logging design की गई
- system के entry/exit, exception cases जैसे महत्वपूर्ण moments पर आवश्यक logs छोड़े गए
Documentation
- GitHub markdown documents को sphinx-book-theme में बदलकर wikibook की तरह चलाया गया
- team के सभी members ने documentation में सक्रिय योगदान दिया, ताकि सिस्टम से जुड़े सभी documents एक ही जगह मिल सकें
- बेहतरीन documentation culture ने team growth, maintenance और नए लोगों की onboarding efficiency को बहुत बढ़ाया
Runtime integrations
- client की performance गिराने वाली requests (जैसे analytics scripts insertion) को यथासंभव समझाकर कम कराया गया
- queries को browser runtime की जगह build time पर process करने के लिए बदला गया, ताकि service performance बनी रहे
- व्यवहार में सभी customer requests को रोका नहीं जा सका, इसलिए कुछ मामलों में performance degradation हुआ
And more
- तकनीक के अलावा सकारात्मक और सहयोगी team culture तथा मज़बूत motivation को बड़े system की सफलता का असली इंजन बताया गया
- यह ऐसा उदाहरण था जिसने छोटे लेकिन महत्वपूर्ण practical decisions और लगातार quality management की ताकत को महसूस कराया
18 टिप्पणियां
इस उबाऊ चीज़ पर पूरे 2.5 साल?!
मैं यह जानने के लिए गया था कि Faceted Search क्या है, और वहाँ पढ़ने लायक और भी चीज़ें मिलीं.
https://www.cybertec-postgresql.com/en/faceting-large-result-sets/
https://roaringbitmap.org/about/
https://github.com/cybertec-postgresql/pgfaceting
"उबाऊ"पर राय दिलचस्प हैं, हाहा। अगर इसे किसी और शब्द से बदलें, तो क्या अच्छा रहेगा? घिसा-पिटा, आम?boringका अनुवाद "उबाऊ" करना मूल अर्थ को काफ़ी हद तक नहीं पहुँचा पाता।boringnessGo की design philosophy में से एक है।उबाऊ~ सा दिखावा…
कोरिया में तो हर चीज़ का नतीजा आखिर Java ही होता है, इसलिए यह थोड़ा अजीब लग रहा है lol
मुझे लगता है कि golang और React दोनों ही नए दौर की boring enterprise coding languages हैं।
क्योंकि
boringका지루한यानी "उबाऊ" के रूप में 100% सटीक अनुवाद नहीं होता, इसलिए लगता है कि कोरियाई पाठकों तक उसका सही nuance ठीक से नहीं पहुँच रहा है।Postgres, golang, React की उबाऊ दुनिया में जीना चाहता हूँ
हाँ, शीर्षक देखकर मुझे भी लगा कि यह कोई मज़ाक है।
विदेशों में उसे शायद एक उबाऊ stack माना जाता है.
असल में, go तो वेब सर्वर बनाने के लिए बस सबसे आसान विकल्प है..
लगता है कि rust या FP जैसी भाषाओं से डेवलपमेंट करना ही उबाऊ नहीं माना जाता है.
बहुत ही साधारण बातें.. इतनी साधारण कि हम उन महत्वपूर्ण बातों को नज़रअंदाज़ कर रहे हैं..
यह स्टैक इतना उबाऊ भी नहीं लगता। अगर सच में उबाऊ होता, तो शायद कम से कम Java 1.8 से नीचे का version या VB जैसा कुछ तो होना चाहिए था... ऐसा थोड़ा असभ्य-सा ख़याल आता है।
मूल लेख में
boringसे जुड़ा एक लिंक है, और उसका मतलब देखने पर लगता है कि यहाँ boring = बहुत परिचित जैसा अर्थ है।experienced,verified,skillfulजैसे ज़्यादा उपयुक्त शब्द मौजूद हैं, फिर भी ख़ास तौर परboringइस्तेमाल किया गया है, तो लगता है कि इसका इरादा बस ध्यान खींचने का था।क्या इसका मतलब यह नहीं होगा कि इसे लिखना उबाऊ नहीं था, बल्कि इतना ज़्यादा इस्तेमाल होने की वजह से इसे उबाऊ
gukbap stackकहा गया होगा?Linux kernel 2.6.29 के आसपास...
बस gRPC इस्तेमाल किया, यही अपने आप में... lol
मुझे भी पहले यही लगा कि golang उबाऊ है?
लेकिन अगर classic asp जैसी चीज़ हो, तो उसे उबाऊ कहना शायद ज़्यादा ठीक लगेगा।