2 पॉइंट द्वारा GN⁺ 2023-08-12 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • एकल SaaS application में Postgres bottleneck गंभीर हो गया था, और CPU उपयोग 60~80% पर अटका हुआ था; एक बार यह 100% तक पहुंच गया, जिससे थोड़े समय की outage हुई
  • बड़े DB instance में बदलकर की जाने वाली vertical scaling अपनी सीमा पर पहुंच चुकी थी, इसलिए उसी तरीके से बढ़ते load को संभालना अब मुश्किल था
  • write sharding और microservices क्षमता और operational resilience बढ़ा सकते हैं, लेकिन backup, monitoring, migration, ORM और network topology तक complexity cost को लगातार साथ लाते हैं
  • व्यवहार में, 3 महीनों तक heavy queries हटाने, Rails code optimize करने, Postgres tuning करने, और कुछ महंगी read-only queries को replica DB पर अलग करने का काम किया गया
  • इसके परिणामस्वरूप साप्ताहिक अधिकतम DB CPU उपयोग 90% से घटकर 30% हो गया, जिससे अगली पीढ़ी की architecture पर जाने से पहले मौजूदा सिस्टम को अधिक समय तक उपयोग करने की गुंजाइश बनी

Postgres bottleneck और vertical scaling का अंत

  • जैसे-जैसे एकल SaaS application पर load बढ़ा, Postgres performance मुख्य bottleneck बन गया
    • CPU उपयोग 60~80% के दायरे में बना हुआ था
    • कम से कम एक बार यह 100% तक उछला और छोटी outage हुई
  • पहले, जब भी DB व्यस्त होता था, समय निकालने के लिए उसे बड़े instance से बदल दिया जाता था
    • इस तरीके से feature development जैसे दूसरे कामों पर ध्यान देना संभव था
    • उस समय तक सबसे बड़ा instance पहले ही उपयोग में था, इसलिए आगे vertical scaling संभव नहीं थी

आकर्षक लेकिन महंगी next-generation architecture

  • जिन प्रमुख विकल्पों पर चर्चा हुई, वे थे write sharding और microservices
  • write sharding में स्वतंत्र DB cluster रखे जाते हैं, और partitioning strategy के अनुसार data को किसी विशेष DB में लिखा जाता है
    • संभावित रूप से क्षमता को 2~3 अंकों तक बढ़ाया जा सकता है
  • microservices में monolith को कई services में बांटा जाता है, और हर service का अपना data store होता है
    • हर service के workload के अनुसार data store चुना जा सकता है
  • दोनों तरीके fault tolerance और operational resilience के लिहाज से विकल्प बढ़ाते हैं, लेकिन तत्काल लक्ष्य DB performance को फिर से नियंत्रण योग्य स्थिति में लाना था

एक बार लाई गई complexity लगातार लागत बनाती है

  • complexity में वृद्धि केवल नई संरचना को लागू करने की लागत तक सीमित नहीं रहती, बल्कि बाद में attention cost के रूप में जारी रहती है
  • अगर DB sharding चुना जाए, तो आगे की हर technical decision के साथ नई complexity भी संभालनी होगी
    • backup
    • monitoring
    • migration
    • ORM
    • network topology
  • microservices भी ऐसा ही बोझ बनाते हैं, और अतिरिक्त architecture को बनाए रखने के कारण feature delivery धीमी हो सकती है या छोड़नी पड़ सकती है

पहले मौजूदा सिस्टम में ही गुंजाइश खोजें

  • जब बड़ा architectural transition जरूरी लगता है, तब भी मौजूदा सिस्टम में अक्सर अतिरिक्त headroom बचा होता है
  • workload adjustment, performance tuning, और auxiliary systems जोड़कर कई महीनों या वर्षों का समय निकाला जा सकता है
  • अगर ये विकल्प व्यावहारिक हों, तो next-generation system नया बनाने से पहले इन्हें आजमाना चाहिए

वास्तव में किए गए optimization

  • पहले काम में दो engineers ने लगभग 3 महीने तक मुख्य रूप से DB performance समस्याओं पर काम किया
    • कोई एक जादुई उपाय नहीं था
    • telemetry से heavy queries खोजी गईं
    • Rails codebase में उन जगहों की पहचान की गई जहां queries बन रही थीं
    • queries को optimize किया गया या हटाया गया
    • कई Postgres settings को tune किया गया
  • दूसरे काम में दो अन्य engineers ने codebase बदलकर कुछ महंगी read-only queries को replica DB पर चलने लायक बनाया
    • web client की polling से बनने वाली सबसे अधिक बार चलने वाली SELECT query को primary DB से अलग किया गया

परिणाम और operational principles

  • दोनों कामों को मिलाकर DB का साप्ताहिक अधिकतम CPU उपयोग 90% से 30% तक घट गया
    • साप्ताहिक अधिकतम CPU उपयोग: {l:90,30}
  • CPU headroom काफी बढ़ गया, और primary DB से load हटाने की क्षमता भी बढ़ी
  • codebase के कई हिस्सों को छूने और कई developers के सहयोग से मौजूदा सिस्टम के बारे में distributed knowledge भी जमा हुई
  • complexity हमेशा बुरी नहीं होती, और कभी न कभी DB architecture की मूल सीमाओं तक पहुंचने से पहले अधिक complex संरचना की ओर जाना पड़ेगा
  • लेकिन उससे पहले, अगर पहले मौजूदा सिस्टम को पूरी तरह निचोड़ा जाए, तो जितना संभव हो उतने लंबे समय तक उबाऊ और सरल सिस्टम के साथ काम किया जा सकता है, और यह लागत तथा व्यावहारिकता दोनों के लिहाज से फायदेमंद है

1 टिप्पणियां

 
GN⁺ 2023-08-12
Hacker News की टिप्पणियां
  • Database performance समस्याओं को देखते हुए, नए project को लेकर मेरा सबसे strong विचार यह है कि application का hot path इस तरह design किया जाए कि उसमें joins बिल्कुल न हों
    Storage सस्ता है, इसलिए सब कुछ denormalize कर दें और transaction के अंदर सब update कर दें. Joins हटाने से कितनी speed बढ़ती है, यह सच में हैरान करने वाला है. Ad-hoc analytical queries के लिए data को analytics वाली दूसरी database में replicate किया जा सकता है
    Amazon के DynamoDB को लेकर मेरी mixed feelings हैं, लेकिन इसे सही से इस्तेमाल करने के लिए पहले usage patterns plan करने और schema बाद में तय करने वाली बात relational databases में भी अपनाने लायक है. आजकल तो मैं यह भी मानता हूं कि अगर मकसद analytics नहीं है, तो joins गैर-ज़रूरी हैं. Main databases में ACID properties होती हैं और storage बेहद सस्ता है, इसलिए बस denormalize कर दें
    Hot partitions रोकने के लिए integers की जगह UUID के करीब कुछ इस्तेमाल करना बेहतर है. यह कोई silver bullet नहीं है और इसके drawbacks भी हैं, लेकिन कभी न कभी टूटने वाली integers की शानदार performance के बजाय horizontally scalable “हमेशा ठीक-ठाक” performance की आदत डाली जा सकती है
    एक और ज्यादा hot take यह है कि हर column पर index लगा दें, लेकिन वह किसी और दिन की चर्चा है

    • सच कहूं तो मैं बिल्कुल सहमत नहीं हो सकता. Startup बनाते समय साल 1–5 में हमने performance पर लगभग ध्यान नहीं दिया, और साल 6 में किसी specific table की performance problem सामने आई, जिसे engineers के कई महीनों के काम से optimize किया गया
      Technical debt के नजरिए से, अगर हमने शुरू से ही सब कुछ तेज बनाने की कोशिश की होती, तो वह कहीं ज्यादा महंगा पड़ता. Development speed काफी धीमी हो जाती और शायद कुछ crises में हम fail हो जाते
      इसके बजाय हमने actual जरूरत से ज्यादा machine cost के तौर पर हर महीने कुछ हजार dollars ज्यादा दिए, और उस समय कई महीनों का engineering effort बचाया जब हम पर्याप्त engineers hire नहीं कर पा रहे थे और feature development की opportunity cost बड़ी थी. पहले से पता नहीं हो सकता था कि bottleneck कहां होगा, इसलिए सब कुछ शुरुआत से fast बनाने के लिए 10–20 गुना ज्यादा काम चाहिए होता. कुछ bottlenecks तो unexpected थे
      Joins बड़े scale पर नुकसानदेह हो सकते हैं, लेकिन ज्यादातर startups के पास कम से कम शुरुआत में scale problem नहीं होती. Denormalization अच्छा optimization हो सकता है, लेकिन हर change पर सभी copies को sync करने की speed cost चुकानी पड़ती है. कोई न कोई bug बना देगा जिसमें denormalized non-authoritative field update नहीं होगा और users को पुराना data दिखेगा. आम तौर पर joins इस्तेमाल करना और बाद में read-aside cache वगैरह से optimize करना, schema को तोड़-मरोड़ देने की तुलना में total cost के लिहाज से सस्ता पड़ता है
    • अगर joins पर सही indexes लगे हों और वे उसी server के अंदर हो रहे हों, तो performance पर बड़ा असर नहीं पड़ना चाहिए. “Joins हटाने से बहुत ज्यादा speed मिलती है” यह बात joins का सही इस्तेमाल करने पर सच नहीं है
      इसके उलट, एक data item को 1 जगह के बजाय 20 जगह लिखना performance के लिहाज से कहीं ज्यादा धीमा है, और queries भी बेहद complex और bug-prone हो जाती हैं. 18 जगह update हो जाती हैं लेकिन 2 जगह छूट जाती हैं
      Denormalization का फायदा बताते समय सस्ते storage की बात की जाती है, लेकिन यहां storage सबसे छोटी समस्या है. असली समस्या कहीं बड़ा bug surface area और खराब write performance है, और वह write performance आसानी से read performance को भी खा सकती है
    • Applications दो तरह की होती हैं: “tall” applications और “wide” applications. Database design और optimization पर लगभग सारी सलाह tall applications के लिए होती है
      Tall application मूल रूप से एक ही काम करती है, और बाकी सब उस काम को support करता है. ज्यादातर Big Tech जिनकी आप कल्पना कर सकते हैं, इसी में आती हैं. Data model में सच में महत्वपूर्ण driving concepts बहुत कम होते हैं
      Facebook में असल में लोग, posts और ads ही हैं. Netflix में accounts और shows जैसे concepts हैं. Amazon product में sellers, buyers और products core हैं, और logistics के लिए कुछ चीजें पीछे हो सकती हैं
      Tall applications ज्यादा इसलिए हैं क्योंकि वे आसान हैं. वे उन wide applications से कहीं आसान हैं जिन्हें आम तौर पर “enterprise” कहा जाता है. Enterprise software खराब इसलिए है क्योंकि वह मुश्किल है, और यहीं सबसे कम explore किया गया क्षेत्र और भारी opportunity है. Oracle जैसे incumbents यहां बहुत खराब कर रहे हैं, और अगर आप tall-application वाली सोच लेकर इसमें उतरेंगे, तो आप भी वैसा ही करेंगे
      “Joins कभी मत इस्तेमाल करो”, “single table के इर्द-गिर्द design करो” जैसी सलाह tall applications के लिए समझ में आती है, लेकिन wide applications के लिए भयानक सलाह है. अक्सर दिखता है कि बहुत tall application कंपनियां अपनी core competency के बाहर कुछ करने की कोशिश में बुरी तरह fail होती हैं, क्योंकि वे ऐसे लोगों से भरी होती हैं जो इस सलाह को पवित्र मानते हैं
      यह सलाह उन कंपनियों के लिए है जो पहले ही सफल हो चुकी हैं, आसान काम कर रही हैं और low-hanging fruit सब तोड़ चुकी हैं. जो tall applications अभी सफलता की शिकार नहीं बनी हैं, उन्हें भी performance के लिए data model को काटने-छांटने की जरूरत नहीं है. यह चिंता तो वे कंपनियां करती हैं जो पहले ही बहुत बड़ी सफलता पा चुकी हैं और performance की आखिरी बूंद निचोड़ रही हैं, और ऐसी कंपनियों को ही सलाह की सबसे कम जरूरत होती है. “FAANG करती है, इसलिए तुम्हें भी करना चाहिए”, “अगर users 1 अरब हो गए तो क्या करोगे?” जैसी tall-application-केंद्रित सलाह उन लोगों के दिमाग को दूषित करती है जो अरबों लोगों को ads दिखाने से ज्यादा दिलचस्प काम करना चाहते हैं
    • पिछले कुछ वर्षों में मैंने बड़ी tables की join performance problems की तुलना में developers के denormalization पर निर्भर होने से पैदा हुई तकलीफ ज्यादा देखी है
      बड़े joins की performance बाद में materialized views में धकेलकर या column store में ETL करके ठीक की जा सकती है. लेकिन अगर कोई subtotal_cents column को Order, Invoice, Payment, NotificationEmail, UserProfileRecentOrders models में copy कर दे, और 296 जगहों से उसे reference या update कराया जाए, तो normal state में लौटने का रास्ता लंबा हो जाता है
    • मैंने शायद ही कभी देखा है कि join अपने आप में performance bottleneck हो. अगर कोई एक principle मैंने observe किया है, तो वह है “tables को बहुत बड़ा मत बनने दो
      आम तौर पर समस्या history-record जैसे tables में पैदा होती है. Daily operations के लिए जरूरी data actual table के बहुत छोटे हिस्से में होता है, लेकिन indexes कुछ भी हों, विशाल table पर operations धीमे ही होते हैं. और ज्यादा indexes जोड़ना अपने आप में भी समस्या बन जाता है
      कम से कम traditional relational databases में, हर column पर index लगाना काफी नहीं है; सही column-combination indexes बनाने होंगे जिनका इस्तेमाल किया जा सके. DynamoDB अलग हो सकता है
  • आपके पास जो है उससे अधिकतम निचोड़ें, और कुछ समय तक निचोड़ने के बाद समस्या को देखने का तरीका बदलें—यहाँ, यहाँ और यहाँ तक निचोड़ें—तो अचानक एहसास होगा कि अभी भी बहुत कुछ बचा है
    एक विशाल monolith को करीब दो महीने optimize किया। PM और टीम जिस स्थिति को “अब और कुछ नहीं निकलेगा” मान रहे थे, वहाँ 2,000 RPS से कम से hardware बदलकर 3,200 RPS से कम तक पहुँचे; कुछ दिन सुधार कर 4,000 RPS, थोड़ी और मेहनत से 10,000 RPS, और लगभग एक हफ्ते बाद 40,000 RPS तक पहुँचे
    फिर कहा गया, “इतना काफी है, आगे जाने की जरूरत नहीं,” लेकिन काफी कुछ बदलने के बाद यह एक ही मशीन पर 20 लाख RPS से अधिक तक उछल गया, और एक महीने बाद कम latency के साथ 4 करोड़ RPS से अधिक को स्थिर रूप से संभालने लगा। अभी भी थोड़ा और push करने की गुंजाइश है
    अभी हम निकाली जा सकने वाली क्षमता का 5% भी इस्तेमाल नहीं कर रहे। सिर्फ समस्या के बारे में सोचने का तरीका बदलने से इतना बदलाव आया। पुराने server से नए server पर जाने भर से 1,800 RPS से थोड़ा ऊपर 3,000 RPS तक ही पहुँचे थे। hardware जोड़ने से मूल समस्या ठीक नहीं हुई, और complexity जोड़ना बस समस्या को आगे टालना था। समस्या के बारे में सोचने का तरीका बदला तो समस्या खुद और उसका जवाब बदल गया

    • सच में जानना चाहूँगा कि monolith आखिर क्या इतना खराब कर रहा था कि उससे इतनी performance निकाली जा सकी। क्या queries खराब थीं, queries बहुत ज्यादा थीं, कोई cache नहीं था, या जिन चीजों को concurrent किया जा सकता था उन्हें synchronous तरीके से किया जा रहा था?
    • यह भी जानना चाहूँगा कि इन requests की सामग्री या protocol क्या था, और request व response दोनों का औसत payload size कितना था—क्या आप साझा कर सकते हैं?
  • “monolith को कई interconnected services में तोड़ना, और हर service को अपनी जरूरतों के हिसाब से scale हो सकने वाला अपना data store देना” वाली बात में जोड़ूँ तो, इस point पर हर संभावित microservice को अलग करना जरूरी नहीं है। बस पूछना होता है, “कौन-सा separation सबसे बड़ा असर डालेगा?”
    मेरे मामले में मैंने कुछ time-series data को Mongo से Cassandra में अलग किया। Cassandra की table structure कहीं बेहतर fit थी। उस dataset का schema अच्छी तरह defined था, और Cassandra data को कहीं अधिक efficient तरीके से pack कर सकता था। उस हिस्से में JSON documents की flexibility की जरूरत नहीं थी
    क्योंकि वह data कुल data का बड़ा हिस्सा था, उसके बाद Mongo काफी संतोषजनक स्थिति में आ गया। जरूरी separation बस एक ही था। technically पहले और बाद में दोनों ही monolith थे; बस वही service दो databases में लिखने लगी थी
    विडंबना यह कि बाद में एक armchair architect टाइप व्यक्ति सारा data JSON document store में मिलाना चाहता था, और कई बार ऐसी चर्चा हुई कि “हम यह रास्ता पहले चल चुके हैं और जानते हैं कि इसका अंत कहाँ होता है”

    • दिलचस्प है कि microservices का idea बहुत obvious “solution” की तरह उछाल दिया जाता है। ऐसा नहीं है
      Vertical scaling obvious पहला समाधान होना चाहिए। बहुत लोग यह बात miss कर देते हैं, और लेख में भी इसका कुछ हिस्सा है, लेकिन vertical scaling लगभग ऐसी horizontal scaling जैसी है जो database consistency को नहीं तोड़ती
      निचोड़ने की काफी गुंजाइश होती है, और performance problem आते ही joins या data validation को ignore करने जैसे anti-patterns आसानी से फेंक देने की जरूरत शायद ही कभी होती है
    • लेख में भी एक मायने में separation किया गया था। क्योंकि कुछ भारी SELECT queries को replica पर भेजा गया था
    • मजेदार बात यह है कि मुझे अक्सर उलटी समस्या दिखती है। अपेक्षाकृत short-lived और अक्सर update होने वाले data के लिए Cassandra क्यों fit नहीं है, यह बार-बार समझाना पड़ता है। वजह tombstone है
    • यह भी अहम है कि एक server दो data stores से पढ़ सकता है। sibling comment में बताए read replica की तरह, उसी process के अंदर Postgres और Redis को साथ में पढ़ने से रोकने वाली कोई चीज नहीं है
  • अगर developers EXPLAIN/ANALYZE के results पढ़ना और सही indexing व query optimization करना जानते हों, तो कई over-engineering decisions से बचा जा सकता है
    queries को log करें, जो बहुत बार चलती हैं या जिनका runtime लंबा है उन्हें filter करें, frequently run होने वाली चीजों को cache करें, और heavy चीजों को optimize करें। यह व्यवस्थित तरीके से करने पर system ज्यादा healthy हो जाता है
    मेरे अनुभव में बहुत मददगार चीजें हैं APM, slow query logs, database read/write replicas, partitioning और sharding

    • explain output पढ़ना अपने-आप में भी काफी बड़ा काम हो सकता है। database पूरी तरह अलग दुनिया है, खासकर अगर आप direct SQL बहुत कम लिखते हैं
      https://explainmysql.com जैसे tools सच में यह ज्यादा साफ दिखाते हैं कि किस चीज को optimize करना है, इसलिए यह उन developers के लिए आसान framework बन जाता है जिनके पास database setup करने जितना ज्ञान तो है, लेकिन उसके internal usage को समझने जितना नहीं
      लगता है कोई न कोई पहले से ऐसा AI system बना रहा होगा जिसमें schema और logs डालो और वह जादू की तरह improvement वाला SQL चला दे। उस पर भरोसा करूँगा या नहीं, पता नहीं, लेकिन कई कंपनियाँ dedicated DBA रखने के बजाय ऐसी चीज इस्तेमाल करना चाहेंगी
    • SQL execution plans समझने के लिए कोई अच्छा resource है क्या? current project में MS SQL Server query performance की बहुत समस्याएँ आ रही हैं
      समझ नहीं आता कि हर query के लिए हमेशा index hint specify करना चाहिए या नहीं। कभी-कभी index मौजूद होने के बावजूद query उस index को use करती नहीं दिखती। SQL execution plans इस्तेमाल करने से शायद इस issue को बेहतर समझ सकूँ
    • “next-gen architecture” sessions मैंने कितनी बार देखे हैं, गिन नहीं सकता, जबकि उनमें से ज्यादातर को current implementation की due diligence से replace किया जा सकता था
      खराब code को नई architecture से ठीक नहीं किया जा सकता। आप बस problem को थोड़ी देर के लिए आगे टालते हैं
  • मेरी पसंदीदा बातों में से एक याद आती है। “आप उस सेना के साथ युद्ध में जाते हैं जो आपके पास अभी है, न कि उस सेना के साथ जो आप चाहते हैं या बाद में पाना चाहते हैं”
    आप शायद इस तथ्य को नज़रअंदाज़ करना चाहें कि यह बात Donald Rumsfeld ने कही थी। फिर भी “unknown unknowns” जैसी कुछ बेहतरीन बातें तो हैं ही
    टीम में काम करते समय मैं अक्सर यह बात याद करता हूँ। हर कोई पूरी तरह सहमत नहीं होता, या सबकी समझ और साझा लक्ष्य समान नहीं होते। कोई व्यक्ति अक्षम तरीके से या ऐसे तरीके से काम कर सकता है जो मुझे पसंद नहीं। लेकिन टीम न होने से टीम होना बेहतर है, इसलिए जो टीम आपके पास है उसी के साथ लक्ष्य हासिल करने का सबसे अच्छा तरीका ढूँढना पड़ता है
    यह सिस्टम पर भी अच्छी तरह लागू होता है

    • Rumsfeld के नाम कई अच्छे quotes हैं। उनमें से ज़्यादातर इस संदर्भ में आए कि Iraq युद्ध इतना गड़बड़ कैसे हुआ, और वही स्थिति ऐसी थी जहाँ, सिर्फ़ उनके quotes देखें तो, उनके द्वारा दी जा सकने वाली leadership की सख़्त ज़रूरत थी
    • जब भी मैं Rumsfeld का नाम सुनता हूँ, मुझे वह दृश्य याद आता है जहाँ वे 10 मिनट से ज़्यादा समय तक यह कहने से इनकार करते रहे कि वे छिपकली नहीं हैं
      https://www.youtube.com/watch?v=XH_34tqxAjA
    • “कोई भी battle plan दुश्मन से संपर्क के बाद टिक नहीं पाता”
      https://www.google.com/search?q=no+battle+plan+survives
    • Mattis की “दुश्मन के पास भी एक वोट होता है” भी वास्तविकता याद दिलाने वाली अच्छी बात है। लोग बहुत नाराज़ होते हैं, लेकिन security, privacy, DRM जैसे संदर्भों में यह उपयोगी है
    • Steven Pressfield की मिलती-जुलती पंक्ति भी मुझे पसंद है। “एथलीट जानता है कि ऐसा कोई दिन कभी नहीं आएगा जब वह बिना दर्द के जागे। उसे चोटिल होकर ही खेलना पड़ता है”
      यह सिस्टम से ज़्यादा हम पर लागू होता है
  • मेरे अनुभव में ORM के ऊपर बने web apps में, जब database load समस्या बनता है, तो query optimization से मिलने वाले low-hanging fruit बहुत ज़्यादा होते हैं
    “क्या N+1 problem है” जैसी बुनियादी बातों से आगे भी, ORM कभी-कभी optimal query नहीं बना पाता। मैं जटिल production web app ORM के बिना बनाना नहीं चाहूँगा, लेकिन कभी-कभी ORM से बाहर निकल पाने की क्षमता होनी चाहिए
    production में सचमुच चल रही और सबसे ज़्यादा resources खर्च करने वाली queries को profile करना चाहिए। उन queries को देखकर target tables की संरचना समझनी चाहिए। ORM join इस्तेमाल कर रहा होता है, लेकिन असल में subquery चाहिए होती है, और उल्टा भी हो सकता है। कभी-कभी results के कुछ हिस्से पहले से aggregate करने पड़ते हैं या complex join में WHERE conditions adjust करनी पड़ती हैं
    मैंने ऐसे मामले भी देखे हैं जहाँ आधे-अधूरे नियमित रूप से चलने वाली ORM-generated query database को मार रही थी, और जो query 20 सेकंड से ज़्यादा लेती थी वह कुछ छोटे बदलावों से 1 सेकंड से कम में आ गई

    • अभी मैं Python ORM SQLAlchemy पर काम कर रहा हूँ, और INSERT में RETURNING का उपयोग करवाना मामूली काम नहीं है। इसके लिए गैर- सहज expire_on_commit=False option सेट करना पड़ता है
      यह option भी RETURNING के उपयोग की गारंटी नहीं देता; अगर database driver और database support करते हों और ORM उस driver/database combination में support करता हो, तभी यह इस्तेमाल होता है। Generated SQL logs में दिखता है, लेकिन उसे वास्तव में inspect करने के लिए API नहीं है, इसलिए अपने logs capture करके scrape किए बिना test suite में RETURNING के उपयोग को enforce करने का तरीका नहीं है। शुक्र है कि Pytest framework के अंदर यह काफ़ी आसान है
      मुझे ORM पसंद हैं, लेकिन ऐसी चीज़ें कई layers पर झुंझलाहट भरी जटिल होती हैं। मैं यह भी समझता हूँ कि SQLAlchemy बहुत बड़ी library है, इसलिए सब कुछ आसान नहीं हो सकता। फिर भी यह example ORM इस्तेमाल करने के tradeoff को अच्छी तरह दिखाता है
      मुझे पता है कि Core में सीधे insert() इस्तेमाल करें तो मनचाहा हो जाता है। यहाँ बात ORM object को AsyncSession में .add() करने की है
    • ORM की बड़ी समस्या यह है कि object-oriented तरीका ज़्यादातर domains में software या data को organize करने का अच्छा तरीका नहीं है
      अधिकांश business logic को OOP की तुलना में relational algebra की भाषा और कुछ extensions से व्यक्त करना बेहतर होता है
  • “Complexity बढ़ने की असली कीमत, और अक्सर कहीं ज़्यादा बड़ी कीमत, attention है” वाली बात दरअसल cognitive load भी है
    मैं ऐसे microservices system पर काम करते-करते थक गया हूँ जिसमें अब भी downtime होता है और किसी को नहीं पता कि पूरा सिस्टम कैसे काम करता है। ज़्यादातर असल में distributed monoliths होते हैं, जहाँ बदलाव कई services में फैलते हैं और उन्हें क्रम से deploy करना पड़ता है। Data replicate करना पड़ता है, jobs synchronize करनी पड़ती हैं, और state share करनी पड़ती है
    https://www.youtube.com/watch?v=y8OnoxKotPQ

    • “किसी को नहीं पता कि पूरा सिस्टम कैसे काम करता है” वाले microservices बहुत आम architecture smell हैं। मूल बात यह थी कि किसी को पूरा सिस्टम जानना न पड़े और जानना चाहिए भी नहीं
      लेकिन अगर system को fix या modify करने के लिए किसी को पूरा समझना ज़रूरी है, तो यह strong signal है कि single responsibility या API के ज़रिए सही abstraction जैसे नियम तोड़े गए हैं। लेकिन मेरे अनुभव में यह बहुत आम है। N microservices वाली pipeline debug करने के लिए अक्सर सभी N services को locally run और build करना पड़ता है
      सख़्ती से कहें तो यह monolith में network partitions और build/deploy variability की अनंत मात्रा जोड़ने जैसा है। अंत में, मैं इसे किसी भी इंसानी programmer की क्षमता से परे बेहद कठिन working environment मानता हूँ
  • यह लेख अच्छा है। पिछले कुछ महीनों से मैं अपने manager को यही संदेश देने की कोशिश कर रहा था, लेकिन ख़ास सफलता नहीं मिली
    overloaded Redshift cluster कई बार गिर चुका है, और हम पहले ही RA3 nodes पर maximum तक scale कर चुके हैं, इसलिए अब “next-generation infrastructure” की ओर बड़े migration की शुरुआत करने वाले हैं। यहाँ next-generation infrastructure से मतलब CDK से managed तीन Redshift clusters हैं
    नया infrastructure मौजूदा setup से कहीं ज़्यादा complex है, और मुझे यकीन नहीं कि यह वह silver bullet बनेगा जिसकी सब उम्मीद कर रहे हैं

  • यह boring solution है। अगर आप life-and-limb से सीधे जुड़े extremely critical systems नहीं बना रहे हैं, तो यही default answer होना चाहिए। Total cost of ownership निश्चित रूप से बहुत कम हो जाती है
    अगर बड़े redundant system चलाने के resources नहीं हैं, तो मैंने बहुत बार देखा है कि redundant system से जुड़ी added complexity खुद समस्या बन जाती है। Simplicity पर ध्यान देना बेहतर है
    complexity को support करने के लिए बहुत लोगों को जोड़ना पड़े, लेकिन पैसे और risk assessment के हिसाब से उसकी ज़रूरत न हो, तो ज़्यादा सरल विकल्प कहीं बेहतर है। ऐसा नहीं कि मैंने कभी नहीं देखा कि आगे बढ़ने के लिए अंततः किसी विशाल project की ज़रूरत पड़ी हो, लेकिन कभी-कभी मुझे लगता है कि वह भी उस समय तक complexity को लगातार ढोते रहने की कुल लागत से कम ही होता है। यह बहुत हद तक इस पर निर्भर करता है कि आप बना क्या रहे हैं

  • सिस्टम में performance optimization ढूंढकर बची हुई performance निचोड़ लेने वाले solutions सच में मज़ेदार होते हैं
    Richard L. Sites की किताब Understanding_Software_Dynamics याद आती है। यह किताब latency समस्याओं को measure और fix करना, और बड़े scale पर latency कम करने से कितनी बड़ी savings हो सकती है, यह सिखाती है
    ऐसी समस्याओं को measure करना और उन पर reasoning करना मुश्किल है, लेकिन solutions अक्सर सरल होते हैं। उदाहरण के लिए, पेज 9 पर वे कहते हैं, “[a] simple change paid for 10 years of my salary.”
    कभी मैं भी इतना impact वाला optimization करना चाहूंगा

    • Google में मैंने ऐसा काम एक से ज़्यादा बार किया है। Google बहुत बड़ी मात्रा में machine resources इस्तेमाल करता है और उसके पास बेहतरीन performance tools भी हैं, इसलिए यह एक उपजाऊ environment है
      https://research.google/pubs/pub36575/
      हालांकि आसपास बहुत सारे smart लोग भी होते हैं, इसलिए अगर आपने कोई बड़ा opportunity ढूंढ लिया है, तो आम तौर पर कोई और उसमें क्यों नहीं कूदा, इसकी वजह होती है। वजह technical भी हो सकती है और organizational भी
      बाद वाले का उदाहरण दें तो, Google आम तौर पर resource pressure न हो तो इस तरह के काम को बहुत बड़ा reward नहीं देता। किसी optimization के लिए colleague bonus के तौर पर शायद लगभग 100 डॉलर मिले हों, लेकिन 10% commission या promotion, या 10 साल तक ऑफिस आए बिना salary पाने का अधिकार—ऐसा कुछ भी नहीं था। आम तौर पर कंपनी चाहती है कि engineers cost cutting की बजाय revenue growth पर काम करें। यह policy सही है या नहीं, यह मेरे pay grade से ऊपर की बात है
    • अंतिम solution में जो बात खटकती है, वह यह है कि query को optimize तभी किया गया जब instance को सबसे बड़े possible configuration पर उठा दिया गया
      अब management console में कुछ clicks करके configuration और ऊपर नहीं बढ़ाया जा सकता था, इसलिए लगता है capacity problem हल करने के लिए सचमुच दिमाग लगाना पड़ा। अगर code के किसी खास हिस्से को पहले ही optimize कर लिया गया होता, तो शायद इतना बड़ा instance configuration शुरू से ही ज़रूरी नहीं पड़ता