One self-hosted console to run your entire business — commerce, ERP, HRM, CRM & manufacturing

भाग 3: 26 मिनट में छोटा, सस्ता, Highly Available Database, और AI को अपना PostgreSQL क्यों मिला

एक बड़े MySQL node को 26-minute window में cheaper Standard cluster में standby के साथ move किया, standby को read के लिए split किया और AI को pgvector के साथ अपना PostgreSQL दिया। Real exam evening ने दिखाया database कभी bottleneck नहीं था।

Author

Anichur Rahaman

2 दिन पहले12 min read5 views
भाग 3: 26 मिनट में छोटा, सस्ता, Highly Available Database, और AI को अपना PostgreSQL क्यों मिला

5 अक्टूबर की रात 22:33 पर, हमने दोनों load balancer को एक empty tag पर point किया। जिसने site खोला maintenance page देखा। इसके पीछे, हर विद्यार्थी, answer और result एक नए database को copy किए जा रहे थे, और window जो हमने business के साथ agree किया वह 26 मिनट थी।

किसी को complain नहीं किया कि database slow था। Problem इसकी shape थी। यह एक large MySQL node था कोई standby नहीं के साथ, तो दिन जब machine fail होगा वह दिन होगा जब पूरा platform fail होगा। यह भी बड़ा था, और काम से ज़्यादा महंगा।

यह article NovaCommerce के data layer के बारे में है, platform जो course बेचता है और timed online exam चलाता है। यह 26-minute move को cover करता है, क्लाइंट जिसे हम भूल गए, कैसे हमने read और write को split किया, क्यों AI को अपना PostgreSQL मिला, और 7 अक्टूबर की real exam evening ने इसके बारे में क्या कहा। यह Part 2 को follow करता है, जहाँ application layer scale करना सीखा।

यह "From One Server to Exam-Day Ready" five-part case study का हिस्सा 3 है। NovaCommerce एक काल्पनिक नाम है; architecture, संख्याएँ और गलतियाँ वास्तविक हैं।

ग़लत तरह का बड़ा

पुराना database एक Managed MySQL "Advanced" node था 8 vCPU और 32 GB RAM के साथ। यह बड़ा था जैसे एक single enormous bridge बड़ा है। Impressive, और एक पार का एक मात्र तरीका।

Project के शुरुआत में हमने लिख दिया कि High Availability हमारे लिए क्या मतलब होना चाहिए। एक line कहता है: database एक node failure से survive करता है। एक node किसी भी size पर उस line को meet नहीं कर सकता। Size और availability अलग सवाल हैं, और पुराना setup ने सिर्फ़ पहले को answer दिया था।

Data लगभग 11 GB था। तो हमने Managed MySQL "Standard" 8.4 में move किए 4 vCPU और 16 GB per node के साथ, और दो node: एक primary, और एक standby जो take over करता है अगर primary fail करे। नए cluster में buffer pool 7 GB है, max connection 1,601 है और storage autoscale on है। Status: Implemented, 5 अक्टूबर।

पहलेअब
PlanAdvanced, single nodeStandard 8.4, दो node (primary + standby)
Size8 vCPU / 32 GB4 vCPU / 16 GB per node
अगर एक node fail करेDatabase down हैStandby take over करता है
Monthly costज़्यादाकम; primary + standby लगभग $389 list price पर है

सस्ता और highly available एक ही समय पर rare है, तो हमने इसे ले लिया। यहाँ पूरा data layer है जैसा यह आज दिखता है। Article का बाकी यह walk through करता है।

Data layer map: backend node MySQL primary को write करते हैं और standby और primary में पढ़ते हैं spread करते हैं, worker सिर्फ़ primary इस्तेमाल करता है, और PostgreSQL pgvector के साथ, Valkey और Spaces अलग service हैं
Write primary को जाते हैं, read primary और standby के बीच share होते हैं, और AI, cache और file हर एक अपने service पर रहते हैं।

26-minute cutover, step by step

Window planned था और business के साथ agree किया। यह order है जो हमने 5 अक्टूबर को follow किया।

  1. 22:33। दोनों load balancer एक empty tag पर point किए। विद्यार्थी maintenance page देखते थे।
  2. Queue drain होते थे और scheduler stop करता था, तो कोई job copy के बीच write नहीं कर सकता था।
  3. पुराने database पर 30 second के लिए zero write। वह gate था: copy सिर्फ़ शुरू होता था पुरानी side quiet हो जाने के बाद।
  4. 4 parallel stream में mysqldump के साथ copy। 10 मिनट लगे।
  5. Count check: 341 table और 18,727,244 row old और new के बीच exactly match करते थे।
  6. Application नए database के लिए test किया गया। फिर एक नया golden snapshot, दोनों pool का rollout और एक fresh worker।
  7. 22:59। Load balancer back, हमने शुरू करने के 26 मिनट बाद।
5 अक्टूबर को cutover का timeline: 22:33 पर load balancer एक empty tag को, queue drain, 30-second quiet gate, 10-minute copy, matching count, test, 22:59 पर back live, और 23:24 पर एक forgotten legacy panel पाया
Window के अंदर सात planned step, और window के 25 मिनट बाद एक surprise।

कैसे हमने prove किया copy सही था

Row count matching एक weak test है, क्योंकि दो table same number row hold कर सकते हैं different content के साथ। तो हमने count से ज़्यादा check किया, और हमने site back होने के बाद check करते रहे।

CheckResult
Table और row, old vs new341 table, 18,727,244 row, exact match
हर table का contentसब table पर full-content checksum, window के बाद
सबसे बड़ा result tableColumn by column compare: 2.76 मिलियन row identical
App user का rightsCREATE TABLE refused, जैसा होना चाहिए
पहले 4 मिनट liveलगभग 2,900 request, 0 5xx, 110 notification job, 0 failed

क्लाइंट जिसे हम भूल गए

23:24 पर, site वापस आने के 25 मिनट बाद, हमें कुछ मिला जो हमने plan नहीं किया था। एक legacy admin panel, एक अलग पुराने server पर चल रहा, अब भी पुराने database को write कर रहा था।

हमने हर चीज़ switch off किया जो हमें याद था: web node, worker, scheduler। हमने कुछ switch off नहीं किया जिसे हम भूल गए कि exist करता है। House move करने की सोचो। Post office आपका mail forward करता है, लेकिन एक दोस्त जिसके पास अब भी पुरानी address है वहाँ letter post करते रहते हैं।

तब तक 48 row पुराने database में land हो गए थे। हमने उन्हें नए में merge किया एक conditional upsert के साथ: row insert करो, या सिर्फ़ update करो जब पुरानी copy newer हो। 44 row merge किए गए। 4 row के लिए नया database पहले से ही एक newer version hold कर रहा था, और हमने उन्हें keep किए। फिर हमने legacy panel को नए database पर point किया और पुराने cluster का app password change किया, तो कुछ भी इसे फिर से write नहीं कर सके। Status: Implemented।

Password change matter करता है जितना merge करता है। एक forgotten client फिर loudly fail करता है silently एक database fill करने की जगह जिसे कोई read नहीं करता। पुराना cluster 48 घंटे freeze रहा हमारे rollback तरीके के रूप में, और उसके बाद delete किया गया।

Lesson एक checklist item है, story नहीं:

  • Cutover से पहले, database के हर external client को list करो। पुराने cluster के trusted source read करो, और हर entry के लिए पूछो: यह किसका है?
  • हर client को application के साथ same window में switch करो।
  • Switch के बाद, पुरानी password change करो, तो एक client जिसे तुम miss किया fail करे loudly।
  • पुराने cluster को एक while के लिए frozen रखो rollback के रूप में।

Standby से Read: read/write split

क्यों read replica नहीं

"Read heavy हैं" का textbook जवाब एक read-only replica node है। हमने इसे consider किया। इस platform पर एक read-only node कम से कम primary जितना बड़ा होना चाहिए, तो यह लगभग same cost करता। इसी बीच हमने पहले से ही standby के लिए pay किया है जो idle बैठा है failure का wait करते।

तो हमने implement किया कुछ सस्ता: standby भी एक reader है, replica hostname के through reach किया जाता है। Read primary और standby के बीच share होते हैं, और हर एक दूसरे को cover करता है। Same hardware, bill पर कोई नई line नहीं।

OptionExtra costStatusNote
हर चीज़ primary परकोई नहींBaselineसबसे simple, लेकिन primary हर read करता है
Dedicated read-only replica nodeलगभग primary जितना फिरConsideredकम से कम primary जितना बड़ा होना चाहिए
Standby primary के साथ read share करता हैकोई नहींImplemented, 6 अक्टूबरRead एक थोड़ा lag कर सकते हैं; sticky read उसे cover करते हैं

Rule, सादी शब्दों में

हमने 6 अक्टूबर को 00:13 पर split on करा। Sketch form में, Laravel database configuration कहता है:

read   hosts = [standby, primary]
write  host  = primary
sticky = true
connect timeout = 2 s
  • Write primary को जाते हैं। हमेशा।
  • Read standby को जाते हैं, और primary दूसरे read host के रूप में listed है। अगर एक answer नहीं दे, दूसरा देता है।
  • Sticky। एक write के बाद, same request primary से read करते रहते हैं, तो एक विद्यार्थी अपना answer देखता है। एक छोटा middleware अगले request को भी primary पर रखता है। एक whiteboard पर नोट लिखने जैसे सोचो: आप वापस whiteboard पर जाते read करने के लिए, photocopy पर नहीं जो अभी print हो रहा है।
  • 2-second connect timeout। एक slow standby quickly primary में fall back जाता है, और कोई विद्यार्थी इस पर wait नहीं करता।
  • Worker कभी standby इस्तेमाल नहीं करता। यह primary पर रहता है, क्योंकि job को fresh data की ज़रूरत है।

क्या यह सच में split होता है?

हमने एक live node पर check किया config को trust करने की जगह। हमने 8 fresh connection खोले, दो बार। पहली run दोनों server के बीच 6 और 2 split किया, दूसरी 4 और 4 split किया। Primary report किया read_only=0 और standby read_only=1, तो दोनों use किए जा रहे थे, और हमने जानते थे कौन सा कौन था।

एक gotcha

Production एक authoritative class map से class load करता है। एक brand-new class, जैसे एक नया middleware, जो map में नहीं है application के लिए exist नहीं करता, और request एक 500 return करता है। Deploy के हिस्से के रूप में class map को add करो।

Fixed size, narrow right, access by tag

Weekly resizing: considered। एक idea exam day से पहले database को up resize करना और बाद में down था। Business ने एक fixed 4 vCPU / 16 GB चुना। नीचे exam evening के नंबर वह choice back करते हैं।

Least privilege: implemented। App के database user को SELECT, INSERT, UPDATE और DELETE है, DDL नहीं। यह row change कर सकता है, जो इसे चाहिए, लेकिन यह table create, alter या drop नहीं कर सकता। हमने cutover के दौरान test किया: CREATE TABLE refused।

Access by tag: implemented। हर database backend tag, worker और office और jump address को admit करता है। एक नया autoscale backend node manual step के साथ connect करता है।

वह एक piece arithmetic छोड़ता है। हर backend node 80 php-fpm worker चलाता है, और हर एक एक MySQL connection hold कर सकता है। Total 1,601 allowed के अंतर्गत रहना चाहिए।

Backend nodephp-fpm worker per nodeWorst case connectionMySQL limit
2801601,601
4803201,601
6804801,601
10 (pool maximum)808001,601

Pool maximum पर भी हम लगभग आधी limit use करते हैं, जो worker और admin session के लिए room छोड़ता है।

Data layer में Valkey

एक managed Valkey, एक standby के साथ, cache, session और queue hold करता है, और यह noeviction policy के साथ run करता है। Part 4 गहरा जाता है। यहाँ सिर्फ़ वह जो data layer को belong करता है।

6 अक्टूबर को 02:28 पर, एक backend load test 2,854 application 500 दे गया। हर एक "Operation timed out" था जबकि Valkey से connect करता, 5-second connect timeout के साथ। हर request को एक fresh TLS connection खोलता, जो लगभग 5.4 ms CPU per request Valkey के लिए, MySQL के लिए लगभग 3.3 ms के विपरीत। 4 GB पर, Valkey connection को fast enough accept नहीं कर सकता।

Implemented: हमने इसे in place resize किया 8 GB को दो node के साथ। Data kept, host unchanged। Retest 25 से 500 request/s तक एक gradual climb के साथ run किया, 2 backend node पर शुरू करते हुए pool scale out: 0 application error, 0 backend 5xx। Exam evening पर यह अपनी memory का लगभग 12.5% use करता है। यह ज़्यादा पैसे cost करता है, और यह एक real failure remove किया।

क्यों AI को अपना PostgreSQL मिला

NovaCommerce के पास AI study feature हैं: adaptive learning, weakness analysis और predicted question। वे embedding पर काम करते हैं, जो संख्याओं की लंबी list हैं जो एक question के meaning को describe करते हैं। सबसे similar को find करना को vector similarity search कहते हैं।

हम उन embedding को एक अलग Managed PostgreSQL 17 में pgvector extension के साथ keep करते हैं, 2 vCPU और 4 GB पर। MySQL में नहीं। तीन reason:

  • एक अलग query shape। Vector search का मतलब big vector, approximate-nearest-neighbour index और CPU-heavy query। Exam traffic short read और write है।
  • Exam write के साथ कोई competition नहीं। एक heavy similarity query को कभी नहीं एक विद्यार्थी से CPU लेना चाहिए answer save करते समय। दो server मतलब दो budget।
  • सही tool। MySQL के पास इस काम के लिए कोई first-class vector index नहीं है।
Side by side comparison MySQL और PostgreSQL pgvector के साथ workload, query shape, scaling lever और क्या होता है जब हर एक struggle करता है
दो workload अलग shape के साथ और अलग failure cost, तो दो database अलग size के साथ।

Downsize जिसे हमने revert किया

PostgreSQL node छोटा है, और हमने इसे और छोटा बनाना try किया: 1 vCPU और 2 GB। Tested, फिर reverted। हमने इसे 2 vCPU और 4 GB में वापस रखा exam के दौरान आने वाले write की वजह से। एक छोटी saving उस घंटे के लिए एक node जो struggle करे का worth नहीं है जो matter करता है। Quiet day के लिए नहीं, exam hour के लिए size करो।

Generic version के लिए इस advice का, जैसे एक index type चुनना और filter vector search को accurate रखना, हमारा field guide देखें The Data Tier Under Load। यह case study सिर्फ़ उससे सटिक रहता है जो हमने किया।

Backup और recovery

एक standby एक dead machine से protect करता है। यह एक mistake से protect नहीं करता: अगर कोई एक table delete करता है, standby एक moment बाद delete करता है। यही backup के लिए है। हमने दोनों use करते हैं, और वे अलग सवाल answer करते हैं।

क्या wrong होता हैक्या हमें protect करता हैStatus
एक MySQL node fail करता हैStandby take over करता है (provider failover)Implemented
Data खो जाता है या damaged होता हैManaged database के daily backup (provider)Implemented
Cutover wrong जाता हैपुराना cluster 48 घंटे frozen रखा, फिर deletedImplemented
एक web node lost होता है या rollout fail करता हैWeb node के golden snapshot; last 3 अच्छे image रखे हैंImplemented
Worker lost होता हैWorker का एक snapshot; यह अभी भी एक single point of failure हैSnapshot implemented, standby worker planned

Database bottleneck नहीं था

हमने database move नहीं किया क्योंकि यह slow था। हमने इसे availability और cost के लिए move किया। Measurement ने decide किया क्या *नहीं* करना: 7 अक्टूबर की real exam evening पर, दो exam back to back के साथ, database system का quiet part था।

Measure, 7 अक्टूबर exam eveningValue
MySQL CPU, max / average24.5% / 13.4%
MySQL running query, max5
MySQL lock wait0
Valkey memory12.5%
Backend peakलगभग 69 requests/s
Backend node CPU, 2-minute average, max47% (एक node एक मिनट के लिए 86% को touched)

Bottleneck backend CPU per node था, और database के पास लगभग 6x headroom था। हमारा capacity model कहता है MySQL limit near 450 requests/s के पास आते है, against 69 पीक जो शाम को था। वह model एक multiple-choice exam से आता है। Written exam PDF upload के साथ worker को बहुत ज़्यादा load करते हैं और must be measured separately।

Money term में, तीन data store लगभग $689 एक महीने में cost करते हैं list price पर: MySQL लगभग $389, Valkey लगभग $240 और PostgreSQL $60। यह लगभग 70% है roughly $990 monthly bill का backend pool 2 node के साथ, यही वजह है कि हम एक store grow करते हैं सिर्फ़ measure करने के बाद।

हमने क्या सीखा

  • एक big node high availability नहीं है। Availability को एक दूसरे node की जरूरत है जो take over कर सके, और एक छोटी pair एक giant machine पर price में beat कर सकते हैं।
  • एक cutover को gate की जरूरत है copy से पहले (zero write), दौरान count, और बाद में checksum।
  • Cutover से पहले पुराने database के हर client को list करो, फिर बाद में पुरानी password change करो तो एक missed client loudly fail करे।
  • Standby को एक reader के रूप में use करो replica buy करने से पहले। Sticky read add करो और short connect timeout, और job को primary पर रखो।
  • App user को right दो जिसकी उसे जरूरत है कुछ नहीं। कोई DDL नहीं।
  • AI को अपना PostgreSQL दो, और exam hour के लिए size करो।
  • Spend करने से पहले measure करो। Database के पास 6x headroom था; काम backend में था।

Database अब calm हैं, तो अगले सवाल हर चीज़ के बारे में हैं जो background में चलता है: single worker, queue, log और monitoring। Part 4 उन्हें cover करता है: Valkey, worker, logging और reliability।

About the Author

Anichur Rahaman

Continue Reading