2 पॉइंट द्वारा GN⁺ 2023-12-14 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • Knock ने अपने notification workflow engine के मुख्य स्टोरेज Postgres को AWS RDS Aurora 11.9 से 15.3 तक अपग्रेड करते हुए ग्राहकों पर असर डाले बिना स्विचओवर की प्रक्रिया तैयार की
  • Amazon RDS में Postgres 11.9 की रिटायरमेंट तारीख 29 फ़रवरी 2024 से पहले कदम न उठाने पर forced upgrade और downtime झेलना पड़ता
  • in-place upgrade और pg_dump/pg_restore में लंबे interruption की ज़रूरत थी, इसलिए इन्हें छोड़कर नए DB पर PUBLICATION/SUBSCRIPTION आधारित logical replication चुना गया
  • टेबल के आकार और write pattern के अनुसार replication strategy अलग रखी गई; छोटे टेबल सीधे replicate किए गए, जबकि बड़े append-only टेबल के लिए copy_data = false और snapshot backfill का संयोजन इस्तेमाल हुआ
  • अंतिम switchover में दोनों DB connections बनाए रखते हुए flag बदला गया, running queries को 500ms दिया गया, फिर 1 सेकंड तक नए DB requests रोके गए ताकि stale read का जोखिम घटे, और यह सब कुछ कुछ सेकंड में पूरा हुआ

अपग्रेड के लक्ष्य और सीमाएँ

  • Knock अपने notification workflow engine के लिए Postgres पर निर्भर है, और workflow configuration, message templates, लाखों logs का ingestion, तथा background job queuing के लिए Postgres का उपयोग करता है
  • relational database होने के कारण Postgres upgrade के समय कम से कम reboot की ज़रूरत होती है, और major version upgrade में disk पर data और index storage format बदलने की वजह से कई मिनट या उससे अधिक का complete shutdown लग सकता है
  • कंपनी की शुरुआत से इस्तेमाल किया जा रहा Postgres 11.9 Amazon RDS से रिटायर होने वाला था, और अलग से कार्रवाई न करने पर forced upgrade और forced downtime की आशंका थी
  • अपग्रेड की शर्तें operational risk कम करने पर केंद्रित थीं
    • जहाँ तक संभव हो, सीधे Aurora के Postgres 15.3 तक जाना
    • 60 सेकंड से अधिक downtime स्वीकार्य नहीं, और आदर्श रूप से system downtime 0
    • Amazon की फ़रवरी 2024 की deadline से पहले पूरा करना
    • ग्राहकों पर न्यूनतम असर, जैसे API error responses 0
    • अगली upgrade में दोबारा उपयोग के लिए प्रक्रिया को runbook के रूप में दर्ज करना
  • 11.9 से 15.3 तक जाना 4 major versions का upgrade था, इसलिए in-place upgrade को 4 बार दोहराना विकल्प से बाहर था

पूर्व तैयारी: जोखिम कम करना और observability

  • Postgres upgrade के लिए पहले जोखिमों की सूची बनाई गई, फिर उन जोखिमों को घटाने पर काम हुआ जिनका प्रभाव बड़ा था और जिन्हें पहले से हटाना आसान था
    • लंबा downtime
    • data loss
    • application workload पर DB performance में बदलाव
    • VACUUM की frequency या behavior में बदलाव
    • replication slots को migrate करने की ज़रूरत
  • Postgres release notes देखकर versions के बीच बदलावों की जाँच की गई, और VACUUM behavior change या किसी खास upgrade में reindexing की ज़रूरत जैसे जोखिम पहचाने गए
  • upgrade के दौरान system और database metrics लगातार देखना ज़रूरी था
    • transaction wraparound रोकने के लिए Max TXN ID
    • DB CPU usage
    • writer instance की waiting sessions
    • query latency
    • application API response latency
  • Knock ने application-specific metrics भी monitor किए, जैसे API request के notification में बदलने में लगने वाला समय
  • समय पर दिखने वाले metrics न हों तो upgrade प्रक्रिया के दौरान आप आँखों पर पट्टी बाँधकर काम कर रहे होते हैं

जिन तरीकों को छोड़ा गया: in-place upgrade और dump/restore

  • AWS RDS का in-place upgrade AWS console से चलाया जाता है, जहाँ AWS DB को बंद करके upgrade scripts चलाता है और फिर उसे वापस online लाता है
  • यह प्रक्रिया data size और versions के बीच बदलाव की मात्रा के अनुसार कुछ मिनट से लेकर कई घंटों तक चल सकती है
  • DB के फिर से online आने के बाद भी VACUUM या REINDEX जैसे maintenance tasks की ज़रूरत पड़ सकती है, इसलिए यह तुरंत पूरी तरह usable स्थिति में नहीं होता
  • pg_dump और pg_restore approach में reliable backup पाने के लिए सभी applications को पुराने DB से अलग करना पड़ता है, और बड़े DB में dump व restore खुद ही बहुत समय लेते हैं
  • Knock की downtime limit से बहुत ऊपर जाने की संभावना के कारण दोनों approaches को छोड़ दिया गया

चुना गया तरीका: logical replication आधारित upgrade

  • अंतिम चुनाव Postgres के PUBLICATION और SUBSCRIPTION का उपयोग करने वाली logical replication approach थी
  • इसकी बुनियादी flow इस प्रकार थी
    • target Postgres version का नया DB खड़ा करना
    • settings, extensions, table structure, users आदि को migrate करना
    • पुराने DB में publication बनाना और नए DB में subscription configure करना
    • tables को publication में जोड़ना
    • replication पूरा होने पर बचे हुए जोखिमों की जाँच के लिए tests चलाना
    • नए DB configuration के पर्याप्त रूप से verify हो जाने पर application को नए DB पर switch करना
    • पुराने DB को हटाना
  • इससे एक ही बार में बड़ा upgrade चलाने के बजाय क्रमिक चरणों में आगे बढ़ना संभव हुआ, और नए DB को वास्तविक data तथा वास्तविक workload के साथ test किया जा सका
  • नया DB तैयार हो जाने के बाद actual switchover कुछ सेकंड में हो सकता था, जिससे switch के समय और तरीके पर बेहतर नियंत्रण मिला

replication configuration के मुख्य बिंदु

  • Postgres logical replication replication slot settings से जुड़े parameters का उपयोग करती है, और साधारण applications में wal_level को logical पर सेट करना मुख्य बदलाव हो सकता है
  • यदि पहले से read replicas, DB failover, data warehouse sync आदि के लिए replication slots उपयोग हो रहे हों, तो max_replication_slots जैसे संबंधित parameters को documentation के अनुसार समायोजित करना होगा
  • नए DB की table structure पुराने DB के समान होनी चाहिए, लेकिन खाली होनी चाहिए
  • schema snapshot pg_dumpall में --schema-only, --no-role-passwords options देकर बनाया जा सकता है, और नए DB के SQL से तुलना करके अंतर सुधारे जा सकते हैं
  • पुराने DB में publication और नए DB में subscription बनाते समय मुख्य options सेट किए गए
    • enabled = false: शुरुआत से synchronization शुरू न हो
    • create_slot = true: Postgres replication slot को खुद manage करे
    • copy_data = true: डिफ़ॉल्ट रूप से table contents कॉपी हों
    • disable_on_error = true: अनपेक्षित error पर subscription रुक जाए ताकि समस्या ठीक करके फिर resume किया जा सके
  • FOR ALL TABLES से सभी tables को एक साथ publication में डालने पर बड़े DB में performance problem हो सकती है, इसलिए Knock ने ALTER PUBLICATION ... ADD TABLE से tables को एक-एक करके जोड़ा

tables का वर्गीकरण और replication strategy

  • Knock ने tables को disk size और tuple count के आधार पर बाँटा
    • छोटे tables जिन्हें कुछ मिनटों में sync किया जा सके
    • बड़े लेकिन append-only के करीब tables
    • बड़े tables जिनकी अधिकांश rows अक्सर update होती हैं
  • Knock के हिसाब से “छोटे” tables वे थे जो 50GB से कम और 1 करोड़ tuples से कम हों
  • Postgres में tuple वह unit है जिसमें insert या update store होता है; row count कम होने पर भी अगर uncleared tuples बहुत हों तो replication time बढ़ सकता है
  • replication से पहले VACUUM चलाना source DB से target DB में कॉपी होने वाले tuples की संख्या घटाने में मदद कर सकता है
  • table synchronization time सीधे disk size और tuple count से जुड़ा होता है, और बहुत लंबा sync primary DB के VACUUM को बाधित कर सकता है, जिससे performance गिर सकती है और transaction wraparound का जोखिम बढ़ सकता है

छोटे tables की replication

  • छोटे tables के लिए पुराने DB के publication में table जोड़ा गया और नए DB में subscription refresh किया गया
  • table copy, synchronization और बाद के changes लागू करने का काम Postgres ने संभाला
  • बहुत छोटे tables 1 सेकंड से कम समय में sync हो सकते थे

बड़े append-only tables की replication

  • जिन बड़े tables में updates नहीं होते, या केवल हाल की rows में updates होते हैं, उनके लिए copy_data = false के साथ अलग publication/subscription बनाया जा सकता है
  • Knock ने सामान्य replication से अलग पहचान के लिए नाम में _nocopy suffix लगाया
  • पहले केवल नए changes replicate किए गए, और historical data को backup या snapshot से अलग से backfill किया गया
  • AWS RDS Aurora पर इस्तेमाल की गई प्रक्रिया यह थी
    • production DB snapshot बनाना
    • snapshot को नए DB instance में restore करना
    • replicate किए जाने वाले snapshot DB tables के नाम में _snapshot जैसा suffix जोड़ना
    • target DB में भी उसी schema के snapshot tables बनाना
    • snapshot DB से target DB तक publication/subscription configure करना
    • replication progress monitor करना
    • replication catch up होने पर INSERT ... ON CONFLICT DO NOTHING से actual target tables में merge करना
  • बहुत बड़े tables में इस प्रक्रिया को कई दिन लग सकते हैं, लेकिन यह background में चलती है इसलिए production environment पर असर नहीं होना चाहिए
  • merge के बाद row counts की तुलना करके consistency verify की गई, फिर target DB के snapshot tables, snapshot subscription, और snapshot DB instance हटा दिए गए

बड़े और बार-बार update होने वाले tables

  • बड़े tables जिनकी अधिकांश rows अक्सर update होती हैं, सबसे कठिन थे, और लंबी replication AUTOVACUUM के चलने में बाधा बन सकती है
  • जिन उपायों पर विचार किया जा सकता है, वे हैं
    • housekeeping से table size घटाई जा सकती है या नहीं, यह देखना
    • हाल में VACUUM चला है या नहीं, जाँचना
    • table को छोटे हिस्सों में partition किया जा सकता है या नहीं, देखना
    • यह जाँचना कि कुछ समय बाद row updates रुक जाते हैं या नहीं, ताकि उसे append-only की तरह माना जा सके
  • यदि source DB PG 15 से कम है, तो विकल्प सीमित रहते हैं; छोटे table वाली पद्धति से replicate करना होगा और monitoring से देखना होगा कि service degradation हो रही है या नहीं
  • ज़रूरत पड़ने पर publication से table हटाकर और subscription refresh करके rollback किया जा सकता है
  • बहुत बड़े tables के लिए कम ट्रैफ़िक वाले समय में replication शुरू की जा सकती है ताकि load और write activity का असर कम हो

PG 15 या उससे ऊपर में बड़े tables की विभाजित replication

  • यदि source DB PG 15 या उससे ऊपर हो, तो कई publications में replication बाँटकर बड़े tables को छोटे हिस्सों में migrate किया जा सकता है
  • यह approach partitioning या sharding जैसी लगती है, लेकिन इसकी कीमत ज़्यादा replication slots के उपयोग के रूप में चुकानी पड़ती है
  • Knock 11.9 से 15.3 पर जा रहा था, इसलिए यह तरीका इस्तेमाल नहीं कर सका और इसे खुद test भी नहीं किया
  • उदाहरण में primary key hash और WHERE clause के माध्यम से rows को कई publications में बाँटा जाता है
  • Knock के हिसाब से manage करने लायक chunk size, indexes को छोड़कर data के आधार पर लगभग 100GB थी

replication state की जाँच और रोकना

  • subscription में table जुड़ने पर target DB के pg_subscription_rel.srsubstate से उसकी स्थिति देखी जा सकती है
    • i: initialization
    • d: table contents copy
    • f: copy complete, final sync का इंतज़ार
    • s: initial synchronization finalizing
    • r: normal replication running
  • d चरण में पुराने Postgres transaction IDs को बनाए रखना पड़ता है, इसलिए यह VACUUM को प्रभावी रूप से रोक सकता है और performance issues या transaction ID wraparound तक ले जा सकता है
  • अगर wraparound करीब आ रहा हो, तो migration रोककर उसे छोटे chunks में बाँटना बेहतर है
  • किसी खास table की replication रोकने के लिए पुराने DB के publication से उस table को हटाया जा सकता है और नए DB की subscription refresh की जा सकती है
  • केवल subscription disable कर देने से source DB पुराने transaction IDs पकड़े रख सकता है, जिससे performance problem हल न हो
  • आपात स्थिति में publication और subscription दोनों हटाकर शुरुआत से दोबारा शुरू किया जा सकता है, और Postgres संबंधित replication slots साफ कर देता है

replication slots migration की सीमाएँ

  • Postgres replication slots DB activity logs को संभालकर रखते हैं जिन्हें कोई दूसरा DB या application consume कर सकता है
  • slot progress को Log Sequence Number यानी LSN से track किया जाता है, और LSN primary Postgres DB के लिए unique होता है
  • पुराने DB के replication slot LSN को नए DB में ज्यों का त्यों कॉपी नहीं किया जा सकता
  • data warehouse tools जैसे applications जो replication slots consume करते हैं, उनके लिए हर tool के documentation के अनुसार migration strategy तय करनी होगी
  • यदि आपकी अपनी application replication slots का उपयोग करती है, तो पुराने और नए DB के duplicate transactions हटाने के लिए idempotency mechanism मददगार होगा

अंतिम सत्यापन

  • सभी tables को publication में जोड़ने और subscription के catch up हो जाने के बाद, यह verify करना ज़रूरी है कि tables मेल खा रहे हैं
  • logical replication lag के कारण पुराने और नए DB का एक ही क्षण में पूरी तरह समान होना कठिन है, लेकिन row counts की तुलना से पर्याप्त निकटता जाँची जा सकती है
  • Knock ने हर table के लिए पुराने DB और नए DB की row count निकालने वाला script बनाया
  • जिन tables में inserted_at column था, उनमें केवल 10 सेकंड से पुरानी rows की तुलना की गई, यह मानकर कि हाल के 10 सेकंड का data जल्द replicate हो जाएगा
  • कुछ tables में random row samples की तुलना करके contents matching की अतिरिक्त जाँच भी की गई

application switchover का तरीका

  • अंतिम cutover के लिए application को दोनों DBs से connect होने लायक बदला जा सकता है
  • कम ट्रैफ़िक वाले DB के लिए config को नए DB पर बदलकर application restart करना एक सरल migration तरीका हो सकता है
  • बहुत अधिक concurrent activity वाले applications में पुराने और नए DB के बीच conflicting writes से बचना ज़रूरी था
  • Knock का cutover script इस क्रम में चला
    • सभी application instances को नए queries नए DB पर भेजने का निर्देश देना
    • चल रही DB queries को पूरा होने के लिए 500ms देना, उसके बाद force cancel करना
    • flag switch के बाद पहले 1 सेकंड तक नए DB requests को कृत्रिम रूप से pause करना ताकि pending transactions को नए DB में replicate होने का समय मिल सके
    • उसके बाद DB activity को सामान्य करना, लेकिन नए DB की ओर मोड़कर
    • कुछ विशेष DB workloads को रोककर नए DB से दोबारा connect कराने के लिए restart करना
  • Knock ने पुष्टि की कि 500ms उसके अधिकांश DB queries से काफी अधिक था, और forced connection teardown से कोई errors नहीं हुए

sequences का प्रबंधन

  • Postgres logical replication sequence को synchronize नहीं करती
  • पुराने DB में sequence values उपयोग होने पर भी नए DB की sequence values अपने-आप नहीं बढ़तीं
  • Knock ने feature flag switch से ठीक पहले दोनों DBs से connect होने वाला script चलाया
    • पुराने DB के हर sequence के लिए SELECT nextval('sequence_name') से अगला value लिया
    • नए DB में SELECT setval('sequence_name', value::int4 + 100000) से sequence को आगे बढ़ाया
  • इस approach से sequence में gap बनता है, लेकिन Knock की sequences bigint थीं, इसलिए 1 लाख values skip करना उपलब्ध sequence space का व्यावहारिक रूप से लगभग 0% था
  • वास्तविक cutover के दौरान उपयोग होने वाली sequence values की मात्रा के अनुसार gap size समायोजित करनी चाहिए

cutover से पहले जाँचने वाली बातें

  • अंतिम switchover से पहले checklist ने operational readiness को व्यापक रूप से कवर किया
    • क्या सभी tables की row counts अपेक्षा के अनुसार मेल खा रही हैं
    • क्या सभी subscriptions enabled हैं और बिना errors के चल रही हैं
    • क्या schema मेल खाता है, और क्या migration releases को freeze किया जा सकता है
    • क्या नया DB workload के अनुसार सही sized है
    • क्या पुराने DB और नए DB की cluster topology मिलाने के लिए read replicas की ज़रूरत है
    • क्या नए DB पर REINDEX और बुनियादी VACUUM maintenance चलाया गया है
    • क्या Postgres release notes से application regressions की संभावना फिर से जाँची गई है
    • क्या नए version के staging DB में automated और manual tests किए गए हैं
    • क्या सबसे भारी queries को pg_bench से load test किया गया है
    • क्या अभी भी कोई ऐसा risk बचा है जिसे और कम किया जा सकता है
    • क्या staging या test environment में cutover procedure कई बार अभ्यास की गई है
    • क्या cutover से ठीक पहले DB backup लिया गया है

वास्तविक switchover का परिणाम

  • Knock ने कई हफ़्तों में tables को एक-एक करके replicate किया, मुख्यतः business hours के बाद और सबसे कम ट्रैफ़िक वाले समय में
  • staging environment में cutover का कई बार अभ्यास करके प्रक्रिया को इतना निखारा गया कि कम operator intervention में भी काम करे
  • PG 15 replica और application switchover code तैयार होने के बाद अंतिम checks किए गए और flag switch किया गया
  • वास्तविक cutover कुछ सेकंड में पूरा हो गया, और replication का इंतज़ार करने के लिए जानबूझकर दिए गए छोटे latency blip के अलावा application चलती रही
  • इसके बाद temporary application changes rollback किए गए, सभी connections स्थायी रूप से नए DB पर शिफ्ट किए गए, और नए DB की subscription तथा पुराने DB को हटा दिया गया
  • Knock ने Postgres 11.9 से 15.3 तक बिना downtime के migration पूरा कर लिया

निष्कर्ष

  • Postgres के 4 major versions को एक साथ skip करना कठिन है, लेकिन संभव है
  • logical replication approach वास्तविक cutover से पहले कई बार अभ्यास, test और rework की सुविधा देती है, इसलिए यह scheduled downtime से अधिक सुरक्षित हो सकती है
  • प्रक्रिया के दौरान समस्या आने पर पुराने DB की publication हटाकर फिर से शुरुआत की जा सकती थी, जिससे service degradation के बिना procedure rollback करना संभव हुआ
  • तकनीकी रूप से 100% availability पूरी तरह संभव नहीं हो सकती, लेकिन बिना downtime वाली migration बड़ी service interruption के बिना system को चलाते रहने में मदद करती है

1 टिप्पणियां

 
GN⁺ 2023-12-14
Hacker News की राय
  • टेबल की सामग्री को एक-एक करके पूरी तरह कॉपी करने वाला तरीका I/O load बहुत ज्यादा पैदा करता है, और बहुत बड़ी tables पर काम नहीं आता
    बेहतर तरीका है replication slot बनाना, snapshot लेना, उसे नए instance में restore करना, LSN को आगे बढ़ाना, और फिर वहीं से replication करना। इससे सभी डेटा वाली logical replica बन जाती है, और फिर उस replica को upgrade किया जा सकता है
    तरीका Instacart के लेख में दिया है: https://archive.ph/K5ZuJ
    अगर मुझे सही याद है तो लेख में कुछ छोटी गलतियां थीं, लेकिन कुल मिलाकर प्रक्रिया काम करती थी, और TB-स्तर के instances को मैंने कई बार इसी तरह upgrade किया है

    • यह तरीका अच्छी recipe है, लेकिन pg_upgrade को बीच में डालने के क्रम में एक छोटा मगर अहम सुधार चाहिए
      पहले logical replication शुरू करके फिर pg_upgrade चलाने से corruption का जोखिम है। इससे जुड़ी चर्चा pgsql-hackers पर है: https://www.postgresql.org/message-id/flat/20230217075433.u5...
      समाधान के लिए पहले logical slot बनाएं, नए cluster को slot की LSN position तक आगे बढ़ाएं लेकिन logical replication अभी शुरू न करें, फिर pg_upgrade चलाएं, और नए PostgreSQL version पर cluster ऊपर आने के बाद logical replication शुरू करें
      Postgres.ai ने हाल ही में GitLab के कई multi-TiB clusters को भारी load के दौरान zero-downtime upgrade करने के लिए बिल्कुल यही तरीका इस्तेमाल किया, और साथ में PgBouncer के PAUSE/RESUME का भी उपयोग किया। इस हफ्ते के आखिर में Alexander Sosna का talk तय है: https://www.postgresql.eu/events/pgconfeu2023/schedule/sessi...
    • OP के तौर पर मैंने यह तरीका भी देखा था, लेकिन सुझाए गए तरीके की तरह LSN को manually आगे बढ़ाने को लेकर भरोसा नहीं था, और replication छूट जाने पर inconsistency detect कर पाऊंगा—इस पर भी भरोसा नहीं था
      प्रति-table progress काफी ज्यादा झंझट वाला था, लेकिन ज्यादा भरोसेमंद लगा
    • लेख update हो गया है: https://tech.instacart.com/zero-downtime-postgresql-cutovers...
    • वह लेख Instacart की upgrade approach की बुनियाद बताता है, लेकिन काफी पुराना है; नीचे वाला लेख मौजूदा प्रक्रिया को बेहतर दिखाता है
      इस तरीके से बहुत बड़े और सक्रिय databases को काफी सफलतापूर्वक upgrade किया गया है
      https://www.instacart.com/company/how-its-made/zero-downtime...
  • approach दिलचस्प है और documentation भी अच्छी है, लेकिन “आज के customer 100% availability की उम्मीद करते हैं” वाला वाक्य अटकता है
    customer के तौर पर यह मेरी preference नहीं है, और provider के तौर पर मेरा अनुभव भी नहीं है। कई workloads में availability से कहीं ज्यादा consistency महत्वपूर्ण होती है
    अगर provider downtime window announce करे, तो कई बार यह देखकर भरोसा होता है कि वह मेरे data को सावधानी से handle कर रहा है

    • OP के तौर पर यह अच्छा feedback है
      मैं product की reliability और workload की consistency—दोनों पर भरोसा बनाना चाहता था। बेशक, consistency का दिखावा करते हुए unstable रहने से कहीं बेहतर है customer expectations manage करना और लंबे समय में बेहतर uptime के लिए जानबूझकर downtime लेना
      periodic maintenance window की पहले से expectation बनाना कुल मिलाकर अधिक मजबूत architecture की ओर भी ले जा सकता है। जब customers downtime सहने के safeguards बनाते हैं तो resilience बढ़ती है, और teams को भी, जब वे customers पर इस तरह भरोसा कर पाती हैं, बेहतर product में investment करने का समय मिलता है
      अगले major version upgrade के बाद शायद मैं “downtime की expectations set करना बहुत high uptime की राह है” पर लेख लिखूं
    • यह इस पर निर्भर करता है कि customer कौन है
      AWS का customer होने के नाते मैं 100% availability की उम्मीद करता हूं। क्योंकि मेरे customers दुनिया भर में हैं और downtime रखने का कोई समय नहीं है
  • AWS अब blue/green deployments support करता है: https://aws.amazon.com/about-aws/whats-new/2023/10/amazon-rd...

    • कुछ हफ्ते पहले खुद करके देखा, लेकिन PostgreSQL के लिए अभी इस पर भरोसा न करना बेहतर है
      AWS के साथ कुछ rounds की बातचीत के बाद experiment कई घंटों तक अटका रहा, और बाद में ही AWS UI ने माना कि switchover apply नहीं हुआ था। शुक्र है कि यह safely fail हुआ, लेकिन GB से बड़े datasets पर असली switchover timing मैच कर पाएंगे—इस पर भरोसा नहीं है
    • सही बात है। OP के तौर पर उस समय हम Aurora 11.9 पर थे और blue/green deployments support के दायरे में नहीं थे
      अगली बार शायद संभव हो
  • यह शानदार है
    आपके अनुभव की ज्यादातर चीजों को automate करने वाला tool मैंने बनाया है, और अगर यह उपयोगी लगे या feedback/ideas से इसे आगे बढ़ाना चाहें तो हमेशा स्वागत है: https://github.com/shayonj/pg_easy_replicate

    • बढ़िया tool है
      बड़ी tables से मिली insights ऐसे tool के लिए दिलचस्प हो सकती हैं। अगर यह प्रति-table सही strategy लागू करना आसान बना दे, तो आगे ऐसी migrations करने वाली teams के लिए यह must-have tool बन सकता है
  • “Knock जैसी सेवा में, scheduled हो या न हो, किसी भी downtime की अनुमति नहीं है” — यह बात संदिग्ध लगती है
    अगर सिस्टम complex है, तो failures भी होंगे और downtime भी होगा। पहले से घोषित 15 मिनट का downtime लगभग हर SaaS business में ठीक माना जाता है। यह न कोई hospital है, न power plant
    जब लोग सेवा को उसकी वास्तविक अहमियत से ज्यादा महत्वपूर्ण समझते हैं, तो बहुत-सा बनावटी काम पैदा हो जाता है। अगर इसमें लगी engineering time को product या development team की productivity सुधारने में लगाया जाता, तो users के ज्यादा खुश होने की संभावना थी। खासकर अगर notifications को queue में डालकर downtime के बाद catch up किया जा सकता हो
    अगर 15 मिनट downtime पर compensation clauses वाला enterprise SLA है, तो इसे justify किया जा सकता है, लेकिन ज्यादातर मामलों में ऐसा नहीं होता। असल में संभव है कि पहले ही ऐसे या इससे लंबे outages कुछ बार हो चुके हों
    database migration में “छोटा downtime” और “zero-downtime” के बीच effort का फर्क आम तौर पर काफी बड़ा होता है, इसलिए यह और महत्वपूर्ण हो जाता है। इस बार जैसा one-off मामला हो, और RDS का नया PostgreSQL version default रूप से support करता हो, तो इसे justify करना खास तौर पर मुश्किल लगता है

    • OP के तौर पर, यह सही है कि हर service में किसी न किसी वजह से downtime होता है
      maintenance window तय करने पर भी चर्चा हुई थी, लेकिन लगातार चिंता यह थी कि production data के साथ upgrade की rehearsal कैसे की जाए। production data से synced PG 15 replica workload के expected तरीके से काम करने की validation के लिए बहुत महत्वपूर्ण था
      live replica का उपयोग करके production environment पर असर न्यूनतम रखते हुए rehearsal की जा सकती है
      इस migration से सबसे बड़ी सीख यह रही कि ऐसे project में सोचे जा सकने वाले हर risk को track और mitigate करना कितना उपयोगी होता है। आखिरकार in-place upgrade का risk चुने गए रास्ते के risk से ज्यादा दिखा, और यह निर्णय maintenance window हो या न हो, उससे अलग था
      bonus के तौर पर, आगे अगर इस approach की जरूरत पड़े, तो यह blog post starting point बनकर कई हफ्ते बचा देगी। उम्मीद है कि समान स्थिति वाली दूसरी teams के लिए भी यह मददगार होगी
    • एक doctor के नज़रिए से, “यह कोई hospital भी नहीं है” को ऐसे system के उदाहरण के रूप में देखना मजेदार है जो downtime सहन नहीं कर सकता
      अमेरिका के सबसे बड़े electronic medical record providers में से एक Epic भी upgrades के लिए कम से कम महीने में 1 बार, हर बार करीब 30–60 मिनट का scheduled downtime रखता है
    • समस्या यह है कि RDS में PostgreSQL instance को scheduled 15-minute downtime के साथ upgrade करने का कोई तरीका नहीं है
      reboot कब होगा, इसे control नहीं किया जा सकता। process शुरू करने के बाद switchover एक घंटे, दो घंटे, तीन घंटे बाद शुरू हो सकता है, और कब reboot होगा यह न पता होता है, न control किया जा सकता है
      अगर replicas हैं, तो वे parallel में upgrade होते हैं और किसी भी random समय reboot हो सकते हैं, जिससे मामला और उलझ जाता है
      इसलिए database size के आधार पर कई घंटों तक चल सकने वाली window में arbitrary unavailability झेलना संभव नहीं है, तो RDS upgrade के लिए logical replication approach practically एकमात्र रास्ता है
      instance जितना बड़ा होगा, समस्या उतनी कठिन हो जाती है
    • downtime की असली समस्या तब होती है जब सारे systems एक साथ down हो जाएं
      अगर Jira दिन में 15 मिनट down रहता है, तो आम तौर पर बड़ा impact नहीं होता। work queue में और काम होते हैं, और worst case में कई failures overlap भी हों, तो किसी को promised documentation work करना होता है
      लेकिन अगर पूरा Atlassian product suite एक साथ मर जाए, तो काम जारी रखने के लिए buffer tasks बनाए रखना कहीं ज्यादा मुश्किल हो जाता है। अगर enterprise की सारी apps को वही storage array use करने पर मजबूर कर दिया जाए, तो productivity loss 5% से 95% तक उछल सकता है
    • “पहले से घोषित 15 मिनट का downtime लगभग हर SaaS business में ठीक है” — इसके उलट, हर महीने downtime न रखने वाला competitor मौजूद हो सकता है
      ऐसा competitor मेरी जरूरतों को अपनी सुविधा से ऊपर रखता है
      आपका outage मेरा outage भी है
  • hava.io में हम अभी इसी process से गुजर रहे हैं
    AWS RDS PostgreSQL 11.13 से 15.5 पर upgrade कर रहे हैं
    आखिरकार हमने pglogical का उपयोग करते हुए one-way replication वाला अपेक्षाकृत simple approach चुना। Google Cloud SQL से AWS RDS में zero-downtime migration इसी तरीके से कर चुके थे, इसलिए भरोसा था कि यह customers को visible impact दिए बिना काम करेगा
    pglogical इस तरह के migrations को काफी simple बना देता है। यह हमेशा तेज नहीं होता, लेकिन अगर आप पूरे database के नए instance में gradually replicate होने के लिए कुछ दिन इंतजार कर सकते हैं, तो ठीक है
    इस तरीके ने storage type और size बदलने की ज्यादा freedom भी दी। IOPS पाने के लिए storage जरूरत से ज्यादा provision कर रखा था, इसलिए storage type बदलना और size भी घटाना चाहते थे। इसलिए simple snapshot restore से काम नहीं चल सकता था

  • शायद आप उस feature की बात कर रहे हैं जिसका AWS ने “sales engineering” stage में वादा किया था
    असल में जब major version upgrade जबरन करना पड़ा, तो वे इसे provide नहीं कर पाए

  • यह हैरानी की बात है कि backup से replica initialize नहीं किया जा सकता
    अगर यह संभव होता, तो stable existing database contents को नए server पर stream करने की मशक्कत कम हो जाती
    और यह “zero-downtime” नहीं है; नए server पर service switch करने में कुछ सेकंड का downtime है
    लेख में यह छूट गया कि consistency कैसे preserve की गई। उदाहरण के लिए, आप application को कुछ समय के लिए बस दोनों servers से जोड़कर नहीं रख सकते। Reads शायद दोनों से serve हो सकती हैं, लेकिन वह भी perfect नहीं है, और writes अनिवार्य रूप से सिर्फ एक server पर ही जानी चाहिए
    आखिर में rollback option भी नहीं है। इतनी बड़ी मात्रा का data एक बार में उठाकर ले जाने जैसे कामों में देर रात चीजें बिगड़ सकती हैं। इसलिए हमेशा ऐसा plan चाहिए जिससे आप पिछले step पर लौट सकें और इस भरोसे के साथ सो सकें कि सुबह भी service चल रही होगी
    खासकर जब आपने नए server पर write transactions भेज दिए हों और फिर किसी भी वजह से पुराने server पर लौटना पड़े, तो यह मुश्किल होता है, और data पहले ही inconsistent हो चुका होता है

    • OP के तौर पर, backup से replica initialize तो किया जा सकता है, लेकिन backup के दौरान लगातार होने वाली writes नहीं मिलतीं
      अगर कोई replication mechanism न हो या इसे application layer तक न उठाया जाए, तो restored system में missing writes होंगी
      उदाहरण के लिए app में बदलाव करके dual writes लागू की जा सकती हैं। मुझे पता है कि जिन teams ने पूरे application को RDBMS से Apache Cassandra जैसे पूरी तरह अलग database पर replatform किया, उन्होंने भी ऐसा ही किया
      हमारे मामले में dual writes, PostgreSQL की built-in functionality से streaming replication setup करने की तुलना में ज्यादा risky लगीं। लेकिन कुछ teams के लिए यह बेहतर विकल्प हो सकता है
      “यह zero-downtime नहीं है” और “consistency preserve करने की details छूटी हैं” वाली बात पर, लेख में consistency बनाए रखने और API downtime से बचने का तरीका विस्तार से बताया गया था। सार यह है कि app दोनों databases से connected था, लेकिन नए database को primary के रूप में इस्तेमाल नहीं कर रहा था
      फिर हमने LaunchDarkly के जरिए सभी app instances को switch का signal भेजा, और LaunchDarkly सभी instances के साथ low-latency connections बनाए रखता है
      signal के बाद पहले 1 second तक servers ने database requests को queue में डाला ताकि replication catch up कर सके। इससे latency में छोटा spike आया, लेकिन यह जानबूझकर calculate की गई acceptable range के भीतर था। उस short pause के बाद requests हमेशा की तरह flow होने लगीं, बस target नया database था, और switch पूरा हो गया
      पुराने database पर बचे traffic के लिए हमने 500ms timeout के साथ forced disconnect भी लगाया। यह value p99 query time से काफी बड़ी थी, इसलिए running queries forcefully terminate नहीं हुईं। इससे पुराने database का traffic रुक गया, और replication को catch up करने के लिए पर्याप्त समय मिला
      rollback option blog post में छूट गया था, लेकिन हमने PG 11.9 का एक alternate database बनाने और 15.3 database को उस तीसरे database में replicate करने का तरीका भी विचार किया था। अगर हमें abort करना पड़ता, तो हम same version वाले इस database पर roll forward कर सकते थे
      staging में upgrade procedure को कई बार अभ्यास करके success की संभावना verify करने के बाद, हमने यह तरीका न अपनाने का फैसला किया। कई rehearsals के कारण actual switch के समय हमें confidence था। production में भी canary deployment के जरिए हमने कुछ read-only workloads को 15.3 instance पर validate किया, और उसे read replica जैसा treat किया
      देर रात की problems से बचने के लिए हमने जानबूझकर weekend की early evening में यह किया। switch को बारीकी से script और rehearse किया गया ताकि human error का risk कम हो
      catastrophic failure होने पर system पुराने database पर वापस लौटने के लिए भी ready था। इस case में नए database में गया कुछ data loss होता, और critical parts को reconcile करने के लिए हमने तैयारी रखी थी। data loss risk कम करने के लिए switch के दौरान हमने कुछ background jobs को थोड़ी देर के लिए pause किया ताकि writes की संख्या घटे
      ये details blog में इसलिए नहीं डालीं क्योंकि हम Knock-specific considerations से ज्यादा PostgreSQL-related details पर focus करना चाहते थे। इस playbook को अपनाने वाली teams को हमेशा अपने context में risks की list बनाकर उन्हें mitigate करना चाहिए
  • sequences वाला हिस्सा वाकई दिलचस्प है
    काफी समय से sequences का इस्तेमाल बहुत कम कर रहा हूं; मुख्यतः sequential UUID या UUID v7, या HiLo जैसे तरीके इस्तेमाल कर रहा हूं
    https://en.wikipedia.org/wiki/Hi/Lo_algorithm

    • जब तक PostgreSQL इसे natively support नहीं करता, database के अंदर UUID v7 generation की responsibility बनाए रखना चाहने वालों के लिए PL/pgSQL function मददगार हो सकता है
      IETF draft specification के आधार पर 12-bit sequence बनाकर, current UNIX epoch milliseconds और 62 bits of randomness को combine करके UUID compose करने का तरीका है
      core idea यह है कि uuidv7_seq हो और generate_uuidv7() function में clock_timestamp(), NEXTVAL, RANDOM() का इस्तेमाल करके UUID v7 form की value return की जाए
    • OP के तौर पर, dependencies की वजह से application की एक जगह को छोड़कर हम sequences से बचते हैं
      कई जगहों पर KSUID और UUID v4 इस्तेमाल करते हैं। यह “trap” सभी sequences पर लागू होता है, इसलिए ऐसी migration करते समय इसे general advice के रूप में mention करना worthwhile है
      [1]: https://segment.com/blog/a-brief-history-of-the-uuid/
  • सफलतापूर्वक किए गए इस जबरदस्त काम को कम करके दिखाने का इरादा नहीं है, लेकिन curious हूं कि नई versions आने पर छोटे-छोटे upgrades क्यों नहीं किए गए
    पढ़ने के लिए यह शानदार है, लेकिन यह उन sailors की कहानी जैसा लगता है जिन्होंने बड़े तूफान को avoid करने के बजाय, यह जानते हुए भी कि अंत tragic हो सकता है, सीधे उसके बीच से गुजरने का फैसला किया
    क्या इस case में छोटे upgrades option ही नहीं थे? क्या बात यह थी कि “एक छोटा upgrade भी बड़े upgrade जितना ही downtime cost करता है, इसलिए जितना हो सके टाल दिया”? introduction में ऐसा hint दिखता है, लेकिन हो सकता है मैं ज्यादा पढ़ रहा हूं

    • OP के तौर पर, minor upgrades के लिए भी हमने यही approach अपनाई होती
      यह “टालते-टालते कोने में फंस गए” से ज्यादा “अगर टूटा नहीं है तो ठीक मत करो” जैसा था, जबकि हमें पता था कि कभी न कभी jump करना ही होगा
    • N versions upgrade करना, availability threat के लिहाज से, N चाहे 1 हो या 3, लगभग समान है
    • हर upgrade में downtime लगता है
      भले ही actual answer 60 seconds से कम हो, 15 तक पहुंचने के रास्ते में हमें वह downtime कई बार झेलना पड़ता