SSD Nodes Learn 🎉 VPS $5.50/माह से
गाइड Matt Connorलेखक: Matt Connor · अपडेट किया गया: 2026-08-21

VPS पर PostgreSQL connection pooling क्यों जरूरी है

PostgreSQL का हर connection अलग process है जिससे 4 GB RAM जल्दी भर जाती है। जानें कैसे pooler का उपयोग करके OOM killer और server crash जैसी समस्याओं से बचा जा सकता है।

एक छोटे VPS में max_connections से पहले RAM क्यों खत्म हो जाती है

VPS पर PostgreSQL connection pooling केवल गति बढ़ाने का तरीका नहीं है। यह 4 GB वाले सर्वर को चालू रखने के लिए आवश्यक है, क्योंकि PostgreSQL का प्रत्येक connection एक अलग operating system process होता है जो अपनी निजी memory का उपयोग करता है। एक pooler बड़ी संख्या में सस्ते client connections के पीछे वास्तविक backend processes की एक छोटी और निश्चित संख्या रखता है।

डिफ़ॉल्ट max_connections 100 है। यह एक सीमा है, बजट नहीं। PostgreSQL कभी यह जांच नहीं करता कि आपकी मशीन वास्तव में 100 backends को वास्तविक queries चलाते हुए संभाल सकती है या नहीं, इसलिए मशीन पहले विफल हो जाती है। kernel का out of memory (OOM) killer एक process को चुनता है, और जब वह किसी backend को चुनता है, तो PostgreSQL shared memory को सुरक्षित करने के लिए पूरे cluster को restart कर देता है। लॉग में server process (PID 1234) was terminated by signal 9: Killed और फिर terminating any other active server processes दिखाई देता है। सभी खुले connections समाप्त हो जाते हैं, जिनमें स्वस्थ connections भी शामिल हैं।

सर्वर की memory इसलिए खत्म हो जाती है क्योंकि प्रत्येक connection एक process है, और क्योंकि work_mem प्रति connection के बजाय प्रति sort या hash operation के लिए आवंटित किया जाता है। ये दोनों कारक मिलकर memory की खपत को बढ़ा देते हैं।

हर कनेक्शन एक प्रोसेस है, और हर प्रोसेस मेमोरी खर्च करती है

PostgreSQL हर कनेक्शन के लिए एक प्रोसेस का उपयोग करता है। जब कोई क्लाइंट कनेक्ट होता है, तो postmaster एक backend को fork करता है, और वह backend तब तक जीवित रहता है जब तक क्लाइंट डिस्कनेक्ट नहीं हो जाता। यह एक thread नहीं है। इसके अपने page tables, अपने catalog caches और अपने cached query plans होते हैं। जैसे-जैसे कनेक्शन अधिक tables को छूता है और अलग-अलग queries चलाता है, ये caches बढ़ते जाते हैं, इसलिए एक व्यस्त ORM application में लंबे समय तक चलने वाला कनेक्शन एक नए कनेक्शन की तुलना में अधिक महंगा होता है।

Shared memory वास्तव में साझा होती है। shared_buffers पूरे cluster के लिए एक ही allocation है, जिसे हर backend में map किया जाता है। Private memory साझा नहीं होती है, यही कारण है कि top यहाँ आपको भ्रमित कर सकता है: एक backend का resident set size (RSS) उन shared pages को शामिल करता है जिन्हें उस backend ने छुआ है, इसलिए 50 backends के RSS को जोड़ने पर shared_buffers की गणना 50 बार हो जाती है।

इसके बजाय private हिस्से को मापें। PSS (proportional set size) प्रत्येक shared page को उसे map करने वाली processes की संख्या से विभाजित करता है, और USS (unique set size) केवल उन pages की गणना करता है जो विशेष रूप से उस प्रोसेस के हैं।

sudo apt update
sudo apt install -y smem
sudo smem -k -P '^postgres'

USS कॉलम वह मेमोरी है जो उस backend के बंद होने पर मुक्त हो जाएगी। यही आपका वास्तविक प्रति-कनेक्शन खर्च है। प्रकाशित आंकड़े आमतौर पर एक idle backend को सिंगल-डिजिट megabytes में रखते हैं, और एक backend जिसने व्यापक ORM queries चलाई हैं, उसे उससे कई गुना अधिक बताते हैं। इन्हें केवल सामान्य प्रकाशित आंकड़ों के रूप में देखें, न कि अपने आंकड़ों के रूप में। योजना बनाने के लिए केवल वही आंकड़ा विश्वसनीय है जो आपके workload के तहत आपके सिस्टम से प्राप्त होता है।

प्रति-सत्र (per-session) allocation को नजरअंदाज करना आसान है। temp_buffers का डिफ़ॉल्ट मान 8MB है और यह पहली बार temporary table को छूने पर प्रति-सत्र allocate किया जाता है। यह सत्र समाप्त होने तक वापस नहीं लिया जाता है।

work_mem प्रति ऑपरेशन आवंटित होता है, प्रति कनेक्शन नहीं

यहीं पर गणना में लोग गलती कर बैठते हैं। work_mem का डिफ़ॉल्ट मान 4MB है, और PostgreSQL का दस्तावेज़ीकरण स्पष्ट है कि इसका क्या अर्थ है: "एक जटिल क्वेरी एक ही समय में कई सॉर्ट और हैश ऑपरेशन कर सकती है, और प्रत्येक ऑपरेशन को आमतौर पर अस्थायी फ़ाइलों में डेटा लिखना शुरू करने से पहले इस मान के बराबर मेमोरी उपयोग करने की अनुमति होती है।" तीन सॉर्ट नोड्स वाली एक योजना एक ही बैकएंड के भीतर, एक ही समय में तीन गुना work_mem का उपयोग कर सकती है।

हैश ऑपरेशंस को अधिक मेमोरी मिलती है। hash_mem_multiplier का डिफ़ॉल्ट मान 2.0 है, इसलिए एक हैश जॉइन या हैश एग्रीगेट work_mem गुना दो का उपयोग कर सकता है, जो स्टॉक सेटिंग्स पर 8MB होता है। पैरेलल क्वेरी इसे और बढ़ा देती है, क्योंकि प्रत्येक पैरेलल वर्कर एक अलग प्रोसेस है जिसकी अपनी अलग सीमा होती है।

4 GB RAM वाले VPS के लिए गणना करें। shared_buffers को 1 GB पर सेट करें, work_mem को 4MB पर रहने दें, और मान लें कि 100 कनेक्शन प्रत्येक दो हैश नोड्स के साथ एक क्वेरी चलाते हैं। यह 100 गुना 16MB है, यानी 1 GB शेयर्ड बफ़र्स के ऊपर 1.6 GB प्राइवेट मेमोरी, पेज कैश और सर्वर पर मौजूद अन्य चीजों से पहले। अब work_mem को 64MB तक बढ़ाएं क्योंकि सर्वर पर अतिरिक्त RAM उपलब्ध है, तो वही 100 कनेक्शन 100 गुना 256MB का उपयोग करेंगे। आपको कोई चेतावनी नहीं मिलेगी। आपको इसका पता तब चलेगा जब OOM killer सक्रिय होगा।

आप अनुमान लगाने के बजाय यह जाँच सकते हैं कि क्या work_mem बहुत छोटा है। postgresql.conf में log_temp_files = 0 सेट करें और कॉन्फ़िगरेशन को रिलोड करें। डिस्क पर होने वाला प्रत्येक स्पिल (spill) एक लाइन लिखेगा जिसमें फ़ाइल का नाम और उसका आकार होगा, जैसे temporary file: path "base/pgsql_tmp/pgsql_tmp1234.0", size 20971520। बार-बार होने वाले स्पिल का मतलब है कि work_mem को बढ़ाना मददगार होगा। यदि कोई स्पिल नहीं है, तो मान बढ़ाने से कोई लाभ नहीं होगा और यह उस मेमोरी की खपत करेगा जो आपके पास उपलब्ध नहीं है।

पूल अरिथमेटिक जो वास्तव में समस्या पैदा करता है

कोई भी जानबूझकर 240 connections नहीं खोलना चाहता। वे 20 का एक pool configure करते हैं, और फिर application को एक से अधिक स्थानों पर चलाते हैं।

ChartBackends requested per app topology, pool size 20 per worker (arithmetic)
The data behind this chart
[
  {
    "config": "1 worker",
    "backends": 20
  },
  {
    "config": "4 web workers",
    "backends": 80
  },
  {
    "config": "4 web + 2 background",
    "backends": 120
  },
  {
    "config": "2 hosts x 4 workers",
    "backends": 160
  },
  {
    "config": "3 hosts x 4 workers",
    "backends": 240
  }
]

चार Gunicorn workers, जिनमें से प्रत्येक 20 का pool रखता है, 80 backends की मांग करते हैं। दो background job workers जोड़ें और यह 120 हो जाता है। 3 hosts x 4 workers तक बढ़ें और application 100 के max_connections के मुकाबले 240 backends की मांग कर रही होती है। इनमें से कोई भी 5 सेटअप किसी एक स्थान पर गलत तरीके से configure नहीं है। pool प्रति process होता है, और application का कोई भी हिस्सा कुल संख्या को नहीं देख सकता।

Library defaults भी इसी दिशा में दबाव डालते हैं। SQLAlchemy का QueuePool डिफ़ॉल्ट रूप से max_overflow=10 के साथ pool_size=5 पर सेट होता है, यानी प्रति process 15 connections। HikariCP डिफ़ॉल्ट रूप से 10 पर सेट होता है। Django में 5.1 से पहले कोई इन-बिल्ट pool नहीं था, प्रति worker process केवल एक connection होता था, यही कारण है कि Django applications को यह समस्या बाद में आती है और तब एक साथ आती है जब कोई CONN_MAX_AGE सेट करता है या नया "pool": True विकल्प चालू करता है। यदि आप Gunicorn और nginx के पीछे एक Django app चलाते हैं, तो जिस संख्या से गुणा करना है वह आपके Gunicorn worker की संख्या है, न कि आपके server की संख्या।

VPS पर Postgres connection pooling वास्तव में क्या बदलता है

एक pooler एक ऐसी प्रक्रिया है जो एक तरफ आपके application के साथ PostgreSQL wire protocol में बात करती है और दूसरी तरफ वास्तविक server connections का एक छोटा समूह बनाए रखती है। यह किसी भी query को तेज़ नहीं करती है। यह केवल यह बदलती है कि connection की लागत कौन उठाता है और कितने वास्तविक backends मौजूद रहते हैं।

दो चीजें बेहतर होती हैं। Connection बनाने में अब fork और catalog lookups की लागत नहीं लगती जो खाली backend cache को भरते हैं, क्योंकि pooler स्वयं client के connect request का उत्तर देता है। इससे भी महत्वपूर्ण बात यह है कि वास्तविक backends की संख्या अब application connections की संख्या पर निर्भर नहीं रहती, इसलिए 500 clients 20 backends को साझा कर सकते हैं।

इंतज़ार करना ही इसकी मुख्य विशेषता है, और यही वह हिस्सा है जिसका लोग विरोध करते हैं। बिना pooler के, 500 concurrent queries एक साथ backend प्राप्त करती हैं और दो CPU cores पर एक साथ चलती हैं, जिससे हर एक query धीमी हो जाती है और सारी memory एक ही समय में खर्च हो जाती है। Pooler के साथ, 20 queries चलती हैं और बाकी कुछ milliseconds इंतज़ार करती हैं, इसलिए प्रत्येक चल रही query को CPU का वास्तविक हिस्सा मिलता है और वह जल्दी पूरी हो जाती है। एक छोटे pool के सामने कतार (queue) होना, एक बड़े pool के सामने बिना कतार होने से बेहतर है।

Pooler जो नहीं करता, वह है machine पर किसी अन्य चीज़ को सीमित करना। यदि Postgres उसी VPS पर किसी app server या उसी VPS पर मौजूद vector database के साथ साझा किया जाता है, तो pooler केवल Postgres को आपके application से बचाता है और कुछ नहीं। पड़ोसियों पर भी एक सख्त सीमा लगाएँ: आप systemd के साथ किसी service द्वारा उपयोग की जाने वाली memory और CPU को सीमित कर सकते हैं ताकि एक अनियंत्रित प्रक्रिया database को नीचे न गिरा सके। Database स्वयं कहाँ स्थित है, यह इस बात को बदल देता है कि आप उन सीमाओं को कैसे निर्धारित करते हैं, जो Docker में Postgres चलाने या सीधे host पर चलाने के बीच का व्यावहारिक अंतर है।

Session pooling और transaction pooling की तुलना

एक सेटिंग बाकी सब कुछ निर्धारित करती है, और वह है pool_mode

Session pooling में, एक सर्वर कनेक्शन क्लाइंट को उसके पूरे कनेक्शन के दौरान असाइन किया जाता है और क्लाइंट के डिस्कनेक्ट होने पर ही रिलीज किया जाता है। सब कुछ काम करता है, क्योंकि pooler एक साधारण proxy की तरह व्यवहार करता है। आप केवल कनेक्शन बनाने की लागत बचाते हैं, और कुछ नहीं। यदि एप्लिकेशन 200 कनेक्शन खोलता है, तो आपको अभी भी 200 backends की आवश्यकता होगी।

Transaction pooling में, एक सर्वर कनेक्शन क्लाइंट को केवल एक transaction की अवधि के लिए असाइन किया जाता है। COMMIT या ROLLBACK पर यह pool में वापस आ जाता है और अगला प्रतीक्षा कर रहा क्लाइंट इसे प्राप्त कर लेता है। यही वह तरीका है जो 500 क्लाइंट्स को 20 backends में बदल देता है। यही वह तरीका है जो चीजों को तोड़ता भी है, और यह डिजाइन के अनुसार ही होता है: आपका अगला statement पिछले statement की तुलना में किसी अलग backend पर चल सकता है।

PgBouncer का डिफ़ॉल्ट pool_mode = session है। इसे इंस्टॉल करें, कुछ भी न बदलें, और आपको बिना किसी लाभ के केवल सस्ता वाला हिस्सा मिलेगा। एक तीसरा मोड, statement, हर एक statement के बाद कनेक्शन वापस कर देता है और multi-statement transactions को अस्वीकार कर देता है। इसे तब तक न छुएं जब तक आप यह न जानते हों कि आपको इसकी आवश्यकता क्यों है।

कौन सा ट्रांजेक्शन मोड विफल होता है, और क्यों

नीचे दी गई हर चीज़ एक ही कारण से विफल होती है। यह वह स्टेट (state) है जो एक सिंगल बैकएंड के भीतर रहती है, और ट्रांजेक्शन पूलिंग आपको दो बार एक ही बैकएंड मिलने का वादा नहीं करती है।

  • सेशन लेवल पर SET और RESETSET search_path, SET statement_timeout, SET TIME ZONE और SET ROLE उस बैकएंड पर जाते हैं जिसने उस स्टेटमेंट को सर्व किया था, और आपके अगले ट्रांजेक्शन तक वे समाप्त हो जाते हैं। एक स्पष्ट ट्रांजेक्शन के भीतर SET LOCAL का उपयोग करें, जो उस ट्रांजेक्शन तक ही सीमित होता है और इसलिए सुरक्षित है।
  • LISTEN। नोटिफिकेशन डिलीवरी उस बैकएंड से संबंधित है जिसने LISTEN चलाया था, और जैसे ही ट्रांजेक्शन समाप्त होता है, वह बैकएंड किसी अन्य क्लाइंट को सौंप दिया जाता है। NOTIFY ट्रांजेक्शन मोड में अभी भी काम करता है, जो इसे एक भ्रमित करने वाली विफलता बनाता है: भेजना सफल होता है, लेकिन प्राप्त करना कभी नहीं होता। यदि आपको LISTEN की आवश्यकता है, तो सीधे port 5432 पर एक अतिरिक्त कनेक्शन खोलें जो पूलर को छोड़ देता है।
  • सेशन-लेवल एडवाइजरी लॉक्स। pg_advisory_lock() सेशन द्वारा रखा जाता है और सेशन समाप्त होने पर रिलीज होता है। ट्रांजेक्शन पूलिंग के तहत आपका अनलॉक कॉल एक अलग बैकएंड पर चलता है, इसलिए लॉक तब तक बना रहता है जब तक PgBouncer उस सर्वर कनेक्शन को रिटायर नहीं कर देता, जो डिफ़ॉल्ट रूप से server_lifetime, यानी एक घंटे के बाद होता है। pg_advisory_xact_lock() का उपयोग करें, जिसे उसी बैकएंड द्वारा ट्रांजेक्शन के अंत में रिलीज किया जाता है जिसने इसे लिया था।
  • PREPARE और DEALLOCATE, SQL स्टेटमेंट्स। ट्रांजेक्शन मोड में कभी उपलब्ध नहीं होते।
  • WITH HOLD कर्सर, और कोई भी सर्वर-साइड कर्सर जिसके अपने ट्रांजेक्शन से अधिक समय तक जीवित रहने की उम्मीद हो।
  • कमिट (commit) के बाद भी जीवित रहने के लिए बनाई गई अस्थायी टेबल। CREATE TEMP TABLE ... ON COMMIT PRESERVE ROWS टेबल को एक बैकएंड के अस्थायी स्कीमा में डालता है, और आपका अगला ट्रांजेक्शन उस बैकएंड में नहीं हो सकता है।
  • LOAD

प्रोटोकॉल-लेवल प्रिपर्ड स्टेटमेंट्स एकमात्र ऐसी चीज़ है जो बदल गई है। PgBouncer 1.21.0 ने ट्रांजेक्शन मोड में उनके लिए सपोर्ट जोड़ा, और 1.24.0 ने max_prepared_statements को 200 पर सेट करके इसे डिफ़ॉल्ट रूप से सक्षम कर दिया। पुराने बिल्ड इसे 0 पर छोड़ देते हैं, जिसका अर्थ है बंद। Ubuntu 24.04 में PgBouncer 1.22.0 आता है, इसलिए फीचर मौजूद है लेकिन आपको max_prepared_statements को स्वयं सेट करना होगा। यदि आप सुनिश्चित नहीं हैं कि आपका बिल्ड क्या कर रहा है, तो क्लाइंट पर सुरक्षित सेटिंग यह है: जब आप prepare_threshold को None पर सेट करते हैं तो psycopg 3 सर्वर-साइड प्रिपर्ड स्टेटमेंट्स का उपयोग करना बंद कर देता है।

Django इसका अपना संस्करण नाम देता है। डॉक्यूमेंटेशन में कहा गया है कि "ट्रांजेक्शन पूलिंग मोड (जैसे PgBouncer) में कनेक्शन पूलर का उपयोग करने के लिए उस कनेक्शन के लिए सर्वर-साइड कर्सर को अक्षम करना आवश्यक है", क्योंकि "सर्वर-साइड कर्सर केवल उसी कनेक्शन में सुलभ होते हैं जिसमें वे बनाए गए थे"। उस डेटाबेस की एंट्री में DISABLE_SERVER_SIDE_CURSORS को True पर सेट करें, अन्यथा हर .iterator() कॉल एक रुक-रुक कर होने वाली विफलता बन जाती है जो केवल लोड के तहत दिखाई देती है।

ट्रांजेक्शन मोड अपनाने योग्य है, और यह एक अनुबंध (contract) है। सूची पढ़ें, अपने ORM और बैकग्राउंड जॉब लाइब्रेरी की इसके विरुद्ध जाँच करें, और फिर स्विच करें।

PgBouncer इंस्टॉल करें और एप्लिकेशन को इसकी ओर निर्देशित करें

नीचे दिया गया कॉन्फ़िगरेशन आपके अपने सर्वर पर चलाने के लिए है: Ubuntu 24.04, जिसमें PostgreSQL पहले से ही 127.0.0.1 पोर्ट 5432 पर लिसन कर रहा है।

sudo apt update
sudo apt install -y pgbouncer
pgbouncer --version

Ubuntu 24.04 में PgBouncer 1.22.0 पैकेज उपलब्ध है। अगस्त 2026 तक अपस्ट्रीम वर्ज़न 1.25.2 है। जांचें कि आपके पास कौन सा वर्ज़न है, क्योंकि ऊपर बताया गया prepared statement का व्यवहार इसी पर निर्भर करता है।

एक ऐसा रोल बनाएं जिसका एकमात्र काम PgBouncer एडमिन कंसोल में लॉग इन करना हो, फिर पासवर्ड फ़ाइल बनाएं। PgBouncer को pg_authid से SCRAM (salted challenge response authentication mechanism) सीक्रेट्स की आवश्यकता होती है, और केवल एक सुपरयूज़र ही उस टेबल को पढ़ सकता है।

sudo -u postgres psql -c "CREATE ROLE pgb_admin LOGIN PASSWORD 'change-this'"
sudo -u postgres psql -At -c \
  'SELECT format($$"%s" "%s"$$, rolname, rolpassword) FROM pg_authid WHERE rolpassword IS NOT NULL' \
  > /tmp/userlist.txt
sudo install -o postgres -g postgres -m 640 /tmp/userlist.txt /etc/pgbouncer/userlist.txt
rm /tmp/userlist.txt

पासवर्ड को दोबारा टाइप करने के बजाय सीक्रेट्स को कॉपी करना ही इस प्रक्रिया को सफल बनाता है। PgBouncer केवल तभी PostgreSQL में लॉग इन करने के लिए SCRAM सीक्रेट का पुन: उपयोग कर सकता है जब क्लाइंट ने भी SCRAM के साथ ऑथेंटिकेट किया हो, जब फ़ाइल में मौजूद सीक्रेट pg_authid में मौजूद सीक्रेट के बिल्कुल समान (byte for byte) हो (समान सॉल्ट और इटरेशन काउंट, न कि केवल समान पासवर्ड), और जब [databases] लाइन किसी user= को पिन न करती हो। उस लाइन में user=appuser जोड़ें, तो PgBouncer को प्लेनटेक्स्ट पासवर्ड की आवश्यकता होगी। पुष्टि करें कि फ़ाइल का ओनर वही अकाउंट है जिसके तहत सर्विस चलती है, इसके लिए systemctl show pgbouncer -p User का उपयोग करें। PostgreSQL में पासवर्ड रोटेट करने का अर्थ है इस फ़ाइल को फिर से जनरेट करना, अन्यथा अगला कनेक्शन password authentication failed एरर देगा।

अब /etc/pgbouncer/pgbouncer.ini लिखें।

[databases]
appdb = host=127.0.0.1 port=5432 dbname=appdb

[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 6432
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
admin_users = pgb_admin
pool_mode = transaction
max_client_conn = 500
default_pool_size = 20
min_pool_size = 5
max_db_connections = 80
max_prepared_statements = 200
ignore_startup_parameters = extra_float_digits

listen_addr = 127.0.0.1 पूलर को पब्लिक इंटरनेट से दूर रखता है, जो महत्वपूर्ण है क्योंकि बाहर से एक्सेस किया जा सकने वाला पूलर एक ऐसा ऑथेंटिकेशन एंडपॉइंट बन जाता है जिसे आप पब्लिश नहीं करना चाहते थे। max_client_conn वह संख्या है कि PgBouncer कितने एप्लिकेशन कनेक्शन स्वीकार करेगा, और यह एक सस्ता ऑपरेशन है, इसलिए इसे बड़ा रखा जा सकता है। default_pool_size वह संख्या है कि एक डेटाबेस और यूज़र पेयर कितने वास्तविक बैकएंड रख सकता है, और यह एक महंगा नंबर है। max_db_connections पूरे डेटाबेस को 80 पर सीमित करता है, जिससे max_connections के तहत psql, बैकअप और मॉनिटरिंग के लिए जगह बची रहती है। ignore_startup_parameters = extra_float_digits PgBouncer को उन ड्राइवर्स (जिनमें JDBC ड्राइवर भी शामिल है) को रिजेक्ट करने से रोकता है, जो कनेक्ट होते समय वह पैरामीटर भेजते हैं।

sudo systemctl restart pgbouncer
sudo systemctl status pgbouncer --no-pager
sudo journalctl -u pgbouncer -n 20 --no-pager

एक सफल स्टार्ट लॉग में एक लाइन दर्ज करता है जो बताती है कि PgBouncer 127.0.0.1:6432 पर लिसन कर रहा है। यदि पासवर्ड फ़ाइल के कारण स्टार्ट विफल होता है, तो लॉग में वह पाथ दिखाई देता है जिसे वह पढ़ नहीं सका, जो लगभग हमेशा सिंटैक्स की समस्या के बजाय मोड या ओनरशिप की समस्या होती है। इसके बाद एप्लिकेशन की कनेक्शन स्ट्रिंग को पोर्ट 5432 से बदलकर पोर्ट 6432 कर दें और इसे रीस्टार्ट करें। एप्लिकेशन में और कुछ भी बदलने की आवश्यकता नहीं है।

यह कैसे जाँचें कि pool अपना काम कर रहा है या नहीं

PgBouncer का एक admin console होता है, जिसे pgbouncer नामक एक virtual database के माध्यम से एक्सेस किया जाता है।

psql -h 127.0.0.1 -p 6432 -U pgb_admin pgbouncer
SHOW POOLS;
SHOW STATS;
SHOW CLIENTS;

SHOW POOLS वह कमांड है जिसे आपको देखना चाहिए। cl_active उन clients की संख्या है जो वर्तमान में server connection से जुड़े हैं, cl_waiting उन clients की संख्या है जो connection के लिए कतार (queue) में हैं, sv_active और sv_idle उपयोग में आने वाले और खाली वास्तविक backends हैं, और maxwait यह दर्शाता है कि कतार में सबसे आगे खड़ा client कितने सेकंड से प्रतीक्षा कर रहा है। सामान्य लोड के तहत स्वस्थ स्थिति का अर्थ है कि cl_waiting का मान 0 हो और maxwait का मान 0 हो। यदि maxwait बढ़कर एक या दो सेकंड से अधिक हो जाता है, तो इसका मतलब है कि pool बहुत छोटा है या queries बहुत धीमी हैं, और इनके लिए अलग-अलग समाधानों की आवश्यकता होती है।

default_pool_size बढ़ाने से पहले जाँच लें कि समस्या क्या है।

SELECT state, count(*) FROM pg_stat_activity
  WHERE backend_type = 'client backend' GROUP BY state;

यदि अधिकांश backends idle in transaction स्थिति में रहते हैं, तो pool का आकार आपकी समस्या नहीं है। application एक transaction खोलकर उसके भीतर कुछ धीमा कार्य कर रही है, जैसे कि कोई HTTP call, इसलिए प्रत्येक backend बिना कोई query चलाए व्यस्त रहता है। idle_in_transaction_session_timeout ऐसे connections को काट देगा, लेकिन वास्तविक समाधान application code में ही है। यदि इसके विपरीत सभी backends active स्थिति में हैं, तो pool वास्तव में भर चुका है और अधिक connections देने से पहले queries को EXPLAIN (ANALYZE, BUFFERS) की आवश्यकता है।

आकार निर्धारित करने के लिए, सबसे अधिक उद्धृत शुरुआती बिंदु HikariCP का heuristic है, जो लगभग core count का दोगुना जमा एक होता है। उदाहरण के लिए, 2 core वाले VPS पर यह 5 है। इसे एक प्रकाशित शुरुआती आंकड़े के रूप में मानें, default_pool_size को इसके आसपास सेट करें, और maxwait के आधार पर इसमें बदलाव करें। छोटे pools गलत लग सकते हैं लेकिन आमतौर पर उनका प्रदर्शन बेहतर होता है, क्योंकि कतार में खड़े backend की कोई लागत नहीं होती, जबकि चल रहे backend के कारण CPU, memory और lock contention की खपत बढ़ती है।

PgBouncer, PgDog और Pgpool-II के बीच चयन

सामान्य स्थितियों के लिए PgBouncer सबसे उपयुक्त है: जब आपके पास एक PostgreSQL सर्वर, एक VPS हो और आपका application सर्वर की क्षमता से अधिक connections खोल रहा हो। यह केवल एक काम करता है, इसकी configuration एक single ini file में होती है, और यह Debian तथा Ubuntu के repositories में उपलब्ध है। यह connection handling को एक single thread में चलाता है, जो VPS-आकार के workload के लिए पर्याप्त है और केवल बहुत बड़े machines पर ही एक सीमा बनता है।

जब routing का निर्णय pooling के समान network hop पर लेना हो, तो PgDog पर विचार करना उचित है। यह खुद को PostgreSQL scaling के लिए एक proxy बताता है, Rust में लिखा गया है, और यह query parsing के माध्यम से transaction और session pooling के साथ-साथ read/write splitting भी करता है। इसमें multi-shard routing और two-phase commit की सुविधा भी है। इसे तब चुनें जब आपके पास एक primary और एक या अधिक replicas हों, और आप चाहते हों कि reads replicas पर भेजे जाएँ, बिना application को यह बताए कि replicas मौजूद हैं। दो सावधानियाँ बरतें। यह AGPLv3 है, इसलिए production में उपयोग करने से पहले अपनी कंपनी की नीति के अनुसार इसके network-use clause पर स्पष्टीकरण लें; project का अपना पक्ष यह है कि आंतरिक उपयोग और निजी बदलाव source code साझा करने की बाध्यता पैदा नहीं करते। यह एक नया project है, जिसमें साप्ताहिक releases और 0.x version numbers हैं, इसलिए main को follow करने के बजाय एक विशिष्ट release tag को pin करें।

git clone https://github.com/pgdogdev/pgdog
cd pgdog
cargo build --release
./target/release/pgdog --config pgdog.toml --users users.toml

Source से build करने के लिए एक current stable Rust toolchain, CMake और एक C/C++ compiler की आवश्यकता होती है। releases page पर prebuilt Linux binaries और Debian packages उपलब्ध हैं, और ghcr.io/pgdogdev/pgdog पर एक container image भी मौजूद है। Configuration दो files में विभाजित है। पहली file में सामान्य settings और प्रति database एक entry होती है, जिसे यहाँ inline tables के TOML array के रूप में लिखा गया है ताकि दोनों forms को अलग पहचानना आसान रहे।

databases = [
  { name = "appdb", host = "127.0.0.1" },
]

[general]
port = 6432
default_pool_size = 10

दूसरी file में प्रति user एक entry होती है, जो उसी array form में होती है।

users = [
  { name = "appuser", database = "appdb", password = "change-this" },
]

PgDog default रूप से 6432 port पर listen करता है, जो PgBouncer के समान है, इसलिए एक ही host पर दोनों एक साथ default port का उपयोग नहीं कर सकते।

Pgpool-II, जो जून 2026 तक 4.7.2 version पर है, pooling के साथ load balancing और automatic failover के लिए watchdog प्रदान करता है। अतिरिक्त features के साथ अतिरिक्त failure modes भी आते हैं, और इसे चुनने से पहले इसके pooling model को समझना आवश्यक है। Pgpool-II num_init_children child processes को pre-fork करता है, और प्रत्येक child max_pool server connections तक cache करता है, इसलिए backends की अधिकतम सीमा num_init_children को max_pool से गुणा करने पर प्राप्त होती है। प्रत्येक child एक समय में एक ही client को serve करता है, इसलिए आप जितने clients स्वीकार कर सकते हैं, उनकी संख्या num_init_children के बराबर होती है और यह startup के समय तय हो जाती है; एक idle client भी एक child को occupy करके रखता है। यदि आप num_init_children को 100 और max_pool को 4 पर set करते हैं, तो आपने 400 backends authorize कर दिए हैं, जो कि ठीक वही समस्या है जिसे हल करने के लिए आपने pooler install किया था। Pgpool-II को तब चुनें जब आपको इसके failover और query routing की आवश्यकता हो, और फिर उस गुणा (multiplication) को सावधानीपूर्वक करें। यदि आप केवल backends की संख्या कम करना चाहते हैं, तो यह आवश्यकता से अधिक जटिल प्रणाली है।

Managed proxy का प्रश्न, और self-hosted विकल्प

Managed platforms इसे एक अलग उत्पाद के रूप में बेचते हैं। AWS, RDS के सामने RDS Proxy लगाता है, और Supabase अपने स्वयं के pooler, Supavisor को Supabase Postgres के सामने रखता है। दोनों ही यहाँ वर्णित कार्य करते हैं: client connections को कम लागत पर बनाए रखना, और वास्तविक backends की एक छोटी संख्या को संभालना। Supavisor open source है और इसे self-hosted किया जा सकता है, इसलिए चुनाव proprietary बनाम free का नहीं है।

Managed proxy का self-hosted विकल्प कोई अलग विचार नहीं है। यह वही विचार है जिसमें config file आपके हाथों में होती है: PgBouncer, transaction mode में, database वाले ही VPS पर, 127.0.0.1 पर listen करते हुए। दो अंतर वास्तविक हैं। Managed proxy एक network hop की दूरी पर होता है, इसलिए यह latency बढ़ाता है और database के restart होने के दौरान भी client connections को बनाए रखता है। Database host पर स्थित PgBouncer एक loopback hop जोड़ता है, जो लगभग मुफ्त है, और host के बंद होने पर यह भी बंद हो जाता है। यदि आप restart के दौरान भी सेवा जारी रखना चाहते हैं, तो आपको failover तंत्र की आवश्यकता होगी, जहाँ Pgpool-II का watchdog या PgDog के health checks अपनी जटिलता को सार्थक करते हैं।

इस सूची में एक और विकल्प शामिल है। यदि connection count ही वह मुख्य कारण है जो आपके deployment को जटिल बना रहा है, तो एक embedded database में pool करने के लिए कोई connection model नहीं होता, क्योंकि यह किसी port पर स्थित server के बजाय आपकी process के भीतर एक library होती है। मध्यम write volume वाले एक single application server के लिए, VPS पर production में SQLite चलाना इस समस्या को प्रबंधित करने के बजाय इसे पूरी तरह समाप्त कर देता है। जब आपको वास्तव में एक server की आवश्यकता हो, तो machine का आकार तय करने से पहले pool का आकार तय करें।

FAQ

क्या मुझे अभी भी PgBouncer की आवश्यकता है यदि मेरे application में पहले से ही connection pool है?

आमतौर पर हाँ, क्योंकि application pool प्रति process होता है और दूसरे processes को नहीं देख सकता। चार Gunicorn workers, जिनमें से प्रत्येक 20 request का pool रखता है, 80 backends बनाते हैं, और दो background workers जोड़ने पर यह 120 हो जाता है। PgBouncer एकमात्र ऐसा component है जो कुल संख्या को देख सकता है और उसे सीमित कर सकता है। सबसे अच्छी व्यवस्था दोनों का उपयोग करना है: प्रत्येक worker के अंदर एक छोटा pool ताकि requests को TCP connect के लिए प्रतीक्षा न करनी पड़े, और transaction mode में PgBouncer जो उनके पीछे वास्तविक backends को नियंत्रित करे।

transaction mode में PgBouncer स्विच करने पर वास्तव में क्या खराब होता है?

कोई भी ऐसी चीज जो transactions के दौरान एक backend में state बनाए रखती है। Session-level SET और RESET, LISTEN, WITH HOLD cursors, SQL PREPARE और DEALLOCATE statements, session-level advisory locks, temporary tables जिन्हें commit के बाद भी जीवित रहना आवश्यक है, और LOADNOTIFY काम करना जारी रखता है, जिससे टूटे हुए LISTEN pooling की समस्या के बजाय delivery bug जैसे दिखते हैं। Django पर, DISABLE_SERVER_SIDE_CURSORS को True पर सेट करें। Psycopg 3 पर, या तो prepare_threshold को None पर सेट करें या PgBouncer 1.22 या उससे नए version को max_prepared_statements के साथ 0 से ऊपर चलाएं। pg_advisory_lock() को pg_advisory_xact_lock() से बदलें।

2 core वाले VPS पर default_pool_size कितनी बड़ी होनी चाहिए?

जितना आपको सही लगता है, उससे छोटा। व्यापक रूप से प्रकाशित HikariCP heuristic कोर संख्या का लगभग दोगुना जमा एक है, इसलिए दो कोर पर लगभग 5, और यह एक उत्तर के बजाय एक शुरुआती बिंदु है। इसे सेट करें, फिर वास्तविक load के तहत SHOW POOLS में maxwait और cl_waiting को पढ़ें। दोनों पर शून्य का अर्थ है कि pool पर्याप्त बड़ा है। बढ़ता हुआ maxwait मतलब है कि clients queue में हैं, और संख्या बढ़ाने से पहले pg_stat_activity की जाँच करें: idle in transaction में अटके हुए backends एक application bug हैं जिसे अधिक connections केवल छिपाएंगे।

PgBouncer या PgDog?

एक VPS पर एक PostgreSQL server के लिए PgBouncer, जो अधिकांश deployments के लिए सही है। यह Ubuntu में packaged है, इसका व्यवहार अच्छी तरह से documented है, और इसका पूरा configuration एक ini file है। PgDog तब उपयोग करें जब replicas पर read/write splitting या sharding की आवश्यकता हो, क्योंकि यह pooling के समान hop में होता है, जिससे application को topology जानने की आवश्यकता नहीं होती। PgDog के प्रति प्रतिबद्ध होने से पहले, अपने कार्यस्थल पर लाइसेंसिंग के मालिक के साथ AGPLv3 प्रश्न को सुलझाएं, और एक विशिष्ट release को pin करें, क्योंकि project अभी भी 0.x version numbers पर है और साप्ताहिक रूप से ships होता है।

#postgres#pgbouncer#pgdog#connections#परफ़ॉर्मेंस