1 पॉइंट द्वारा GN⁺ 4 시간 전 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • ClickHouse Managed Postgres में single-threaded PgBouncer के एक CPU core तक सीमित हो जाने की सीमा से बचने के लिए, core की संख्या के अनुपात में multi-process fleet को default configuration के रूप में चलाया जाता है
  • सभी processes so_reuseport के जरिए उसी port से bind होते हैं और kernel नए connections को distribute करता है, इसलिए clients को यह single endpoint जैसा दिखता है
  • अलग connection के जरिए आने वाले Postgres cancel requests उस process तक पहुंच सकते हैं जिसके पास session नहीं है, इसलिए process peering के जरिए उन्हें वास्तविक session owner तक forward किया जाता है
  • 16-vCPU c7i.4xlarge पर single process लगभग 87,000 TPS पर peak हुआ, जबकि 16-process fleet लगभग 336,000 TPS तक पहुंची, यानी करीब 4 गुना throughput दर्ज किया
  • Connections कम हों तो single process समान या थोड़ा तेज हो सकता है, लेकिन high concurrency में एक core bottleneck बन जाता है, इसलिए connection budget को पूरी fleet में बांटकर Postgres में excessive connections के बिना throughput और connection limit को scale करना चाहिए

Single process से बनने वाला CPU bottleneck

  • PgBouncer single-threaded है, इसलिए system में CPU की संख्या चाहे जितनी हो, एक process केवल एक core ही इस्तेमाल करता है
  • 16-vCPU server पर भी connection pooling का काम एक core पर केंद्रित हो जाता है और बाकी 15 cores idle रहते हैं
    • इस वजह से Postgres की processing capacity खत्म होने से पहले ही pooler overall throughput को limit कर देता है
  • ClickHouse Managed Postgres उपलब्ध cores की संख्या के अनुपात में PgBouncer processes की संख्या तय करने वाली process fleet चलाता है

so_reuseport के जरिए connection distribution

  • Fleet के सभी processes so_reuseport enable करते हैं और same port पर bind होते हैं
  • Kernel incoming connections को हर process में distribute करता है, इसलिए client एक endpoint से connect करता है और उसे यह जानने की जरूरत नहीं होती कि पीछे कई PgBouncer चल रहे हैं
  • PgBouncer documentation भी single-threaded processes के कई instances से multiple cores का उपयोग करने के तरीके के रूप में so_reuseport बताती है

Query cancellation सुनिश्चित करने वाली process peering

  • Postgres के cancel requests running query connection से अलग एक नए connection के जरिए cancel key के साथ आते हैं
  • so_reuseport इस्तेमाल करने पर kernel नया cancel connection उस process के बजाय किसी दूसरे process को दे सकता है जिसके पास वह session है
    • अगर request पाने वाला process उस query को नहीं जानता, तो cancellation execute नहीं होती
  • PgBouncer processes के बीच peering करने पर गलत process पर पहुंचे request को वास्तविक session-owning process तक forward किया जा सकता है
  • इससे कोई भी process request पाए, पूरी fleet में query cancellation सही ढंग से काम करती है

Transaction pooling और connection budget

  • Pooling transaction mode में काम करती है, और transaction commit होते ही server connection pool में लौट जाता है
  • max_client_conn और max_db_connections को processes की संख्या से बांटकर हर process को allocate किया जाता है
  • Connection budget को split करने से fleet के कुल connections को Postgres की safe limit के भीतर रखते हुए aggregate connection limit बढ़ाई जा सकती है
  • Single process अपने max_client_conn से अधिक नए clients को नीचे दिए error के साथ reject करता है
    • FATAL: no more connections allowed (max_client_conn)

समान hardware benchmark

  • दोनों configurations को समान AWS EC2 environment में मापा गया
    • Pooler server: 16-vCPU c7i.4xlarge
    • Postgres: अलग server
    • Load generator: तीसरे server से pgbench का उपयोग
    • Workload: read-only(select-only), transaction pooling mode
  • तुलना PgBouncer single process और 16-process fleet के बीच है; instance type, Postgres और workload समान हैं
  • Client connections को 8 से 256 तक बढ़ाते हुए throughput और 16-core server का CPU usage मापा गया
Clients Single process TPS Single process server CPU Fleet TPS Fleet server CPU
8 8,910 0.8% 6,450 2.9%
32 54,203 5.2% 64,244 12.3%
64 86,570 8.3% 219,439 31.9%
128 83,463 8.1% 320,547 45.9%
256 76,893 7.7% 336,469 48.9%

Throughput और CPU उपयोग के नतीजे

  • Single process लगभग 87,000 TPS पर peak होने के बाद load बढ़ने पर performance घट गई, और 256 clients पर लगभग 77,000 TPS दर्ज किया
  • 16-process fleet ने ज्यादा cores का उपयोग करते हुए लगभग 336,000 TPS तक वृद्धि की, जो single process की तुलना में करीब 4 गुना throughput है
  • pidstat measurement में single PgBouncer process ने load के दौरान लगभग 97% CPU के साथ एक core पूरी तरह इस्तेमाल किया, लेकिन 16-vCPU server का overall usage 10% से कम रहा
  • Fleet ने काम को पूरे server में distribute करके लगभग 8 cores इस्तेमाल किए, और Postgres व load generator के limit पर पहुंचने के बाद भी अतिरिक्त headroom बची रही
  • 256 clients बनाए रखकर किए गए measurement में single-process server ने लगभग 9%, और fleet server ने लगभग 52% CPU लगातार इस्तेमाल किया
  • EC2 CloudWatch की external measurement में single-process instance का average CPUUtilization लगभग 16% और fleet का लगभग 60% था
    • CloudWatch numbers guest-internal measurements से थोड़े अधिक हैं, लेकिन यह अंतर समान रहता है कि single PgBouncer 16-vCPU के अधिकांश हिस्से का उपयोग नहीं कर पाता

Concurrency के अनुसार configuration चुनना

  • Connection count कम होने पर parallelize करने के लिए काम नहीं होता और fleet के connections कई processes में distribute हो जाते हैं, इसलिए single process पर्याप्त या थोड़ा तेज हो सकता है
    • 8 clients पर single process ने 8,910 TPS और fleet ने 6,450 TPS दर्ज किया
  • Concurrency बढ़ने पर single process द्वारा कब्जा किया गया एक core throughput की दीवार बन जाता है, और fleet के साथ performance gap बढ़ता है
  • जिन environments में PgBouncer से पहले Postgres limit पर पहुंचता है, वहां single process भी उपयुक्त default है
  • जब pooler throughput limit करना शुरू करे, तो bottleneck दूर करने के लिए core count के अनुरूप process fleet, so_reuseport आधारित port sharing, और process peering को साथ में इस्तेमाल करना चाहिए
  • सभी ClickHouse Managed Postgres servers में यह configuration default रूप से मिलता है

1 टिप्पणियां

 
GN⁺ 4 시간 전
Hacker News टिप्पणियाँ
  • बस https://github.com/yandex/odyssey इस्तेमाल करें। Odyssey एक scalable PgBouncer है

    • हमने सबसे पहले सबसे ज़्यादा परखा हुआ और PostgreSQL के साथ स्वाभाविक रूप से फिट होने वाला PgBouncer सही तरह से tune करके इस्तेमाल किया, और prepared statements support जैसी लंबे समय से चली आ रही समस्याएँ भी सुलझ चुकी हैं, इसलिए यह बहुत अच्छी तरह काम कर रहा है
      हमारे कई ग्राहक PostgreSQL कनेक्शनों को 10,000 से अधिक तक scale कर चुके हैं, और आगे चलकर Odyssey या pgdog जैसे विकल्पों पर भी विचार करने की योजना है। हालांकि व्यक्तिगत रूप से मुझे PostgreSQL में 10,000 से ज़्यादा कनेक्शन रखना पसंद नहीं है, और मेरा मानना है कि कुछ सौ कनेक्शनों से भी पर्याप्त scale किया जा सकता है
    • एक दिलचस्प संबंधित तथ्य यह है कि ClickHouse भी मूल रूप से Yandex ने ही विकसित किया था
    • Yandex एक रूसी कंपनी है, इसलिए मेरा मानना है कि इसका इस्तेमाल न करना ही बेहतर है
  • Kubernetes में PgBouncer चलाने पर एक मशीन पर कई processes चलाना भी आसान होता है, और कई मशीनों में distribute करना भी सरल होता है
    Azure VM maintenance के दौरान पूरे server fleet में क्रमिक failures पैदा कर देता है, इसलिए multi-machine setup खास तौर पर उपयोगी है

    • मैं जानना चाहता हूँ कि PgBouncer से PostgreSQL तक एक अतिरिक्त network hop जुड़ने पर throughput पर क्या असर पड़ता है। मेरा अनुमान है कि फर्क छोटा होगा, लेकिन जाँचने लायक है
  • मैं यहाँ peering की अवधारणा समझता हूँ, लेकिन PostgreSQL में इसे कभी इस्तेमाल नहीं किया
    A) क्या PostgreSQL में इसे आसानी से configure करने के लिए कोई mode या setting है? मैं ऐसी संरचना की कल्पना कर रहा हूँ जहाँ cancel requests को peers तक क्रमवार भेजा जाए जब तक error न मिले, या cancel request में ऐसा metadata हो जिससे गलत request पाने वाला process सही process की पहचान कर सके
    B) अगर सभी PostgreSQL processes so_reuseport के साथ client requests ले रहे हों, तो peers के बीच communication के लिए अलग inter-process communication (IPC) mechanism चाहिए होगा; व्यवहार में कौन-सा mechanism इस्तेमाल होता है?

    • मूल लेख में AI द्वारा लिखे जाने के संकेत साफ़ दिखते हैं। cancel handling PostgreSQL server process का नहीं बल्कि PgBouncer feature है, और peering भी PgBouncer processes के बीच होता है
      अगर PgBouncer process ID को cancel key में शामिल कर दिया जाए, तो इसे काफ़ी आसानी से implement किया जा सकता है
    • जानकारी token में encode की जाती है। यही PostgreSQL में token की 32-byte limit हटाने की एक वजह थी: https://www.postgresql.org/message-id/508d0505-8b7a-4864-a68...
      26वीं slide से देख सकते हैं: https://www.pgevents.ca/events/pgconfdev2024/sessions/sessio...
      PgBouncer maintainer Jelte की talk video भी है: https://www.youtube.com/watch?v=X-nCHcZ6vQU
  • मैं pgdog इस्तेमाल कर रहा हूँ, और यह हमारी ज़रूरत के हिसाब से बहुत अच्छी तरह फिट बैठता है

    • Pgdog को Instacart में बहुत पहले सामने आई PgBouncer की कमियों को दूर करने और PostgreSQL को horizontally scale करने के लिए एक अधिक मजबूत आधार, यानी sharding, देने के लिए बनाया गया था
    • हम आगे pgdog उपलब्ध कराने पर भी विचार करेंगे। PgBouncer चुनने की वजह https://news.ycombinator.com/item?id=48873867 में अधिक विस्तार से लिखी गई है
    • हाल में PostgreSQL reverse proxy projects बारी-बारी से सामने आ रहे हैं। Pgdog क्यों बनाया गया, यह यहाँ देखा जा सकता है: https://news.ycombinator.com/item?id=48819308
  • मैं जानना चाहता हूँ कि PgBouncer को cancel requests तक की चिंता क्यों करनी पड़ती है। क्या इसे बस PostgreSQL तक पास नहीं कर देना चाहिए, और अगर canceled query पर PostgreSQL सामान्य response की जगह error लौटा दे, तो उसे वह PgBouncer संभाल ले जो उस connection को चला रहा है?

    • query cancel मौजूदा query connection से अलग भेजा जाता है, इसलिए PgBouncer को request को सही server और connection तक भेजना पड़ता है
      PgBouncer client को नकली लेकिन traceable cancel PID और secret value देता है, और जब cancel request वापस आती है तो वह असली server/process का PID और secret value खोजकर आगे भेजता है। साथ ही ऐसी स्थिति भी हो सकती है जहाँ client को लगता है कि query अभी चल रही है, लेकिन PgBouncer जानता है कि वह पहले ही खत्म हो चुकी है और connection reuse हो चुका है, इसलिए यह भी जाँचना पड़ता है कि वह connection और PID अभी भी उसी query को चला रहे हैं या नहीं
  • क्या यह microservices में connection pool के ज़रिए PostgreSQL server तक पहुँच को नियंत्रित करने के लिए है? अगर monolithic backend हो, तो इसकी ज़रूरत नहीं लगती
    अधिकांश अच्छे backend frameworks में connection pooling built-in होता है, और उन 98% use cases के लिए जहाँ microservices की न तो ज़रूरत होती है और न ही सिफारिश, वही काफ़ी है

    • अगर हर host पर backend application एक बड़े binary के रूप में चलता है, तो यह सही है। लेकिन अगर backend साझा state के बिना कई forked processes से बना हो, तो इसकी ज़रूरत स्पष्ट रूप से होती है
  • PgBouncer बेहतरीन software है। मैं इसका काफी इस्तेमाल करता हूँ और यह database operations को काफी सरल बना देता है

  • मुझे अभी so_reuseport के बारे में पता चला, और यह दिलचस्प है। लगता है setup की कुंजी यही और peering हैं; क्या peering PgBouncer में built-in है और क्या इसे configure करना आसान है?

  • क्या Kubernetes में भी peering इस्तेमाल किया जा सकता है? इस environment में ports reuse करने की ज़रूरत नहीं होगी, तो क्या हर Pod का अपना अलग connection pool होगा और वह स्वतंत्र रूप से काम करेगा?

  • मेरी उम्र अब 46 है, और मुझे याद है कि 23 साल की उम्र में PostgreSQL का heavy connection model देखकर मैं चौंक गया था। क्या उसके बाद भी इसमें सुधार नहीं हुआ?

    • इसमें काफ़ी सुधार हुआ है, और अब यह हज़ारों connections को भी ठीक-ठाक संभाल लेता है: https://techcommunity.microsoft.com/blog/adforpostgresql/imp...
      लेकिन connection pool न होने पर PostgreSQL को process fork करना पड़ता है, इसलिए नया connection बनाने में हमेशा कई दर्जन milliseconds या उससे अधिक की लागत आती है। connection pool के बिना लिखे गए applications आदर्श नहीं हैं, फिर भी आम तौर पर मिल जाते हैं
      application frameworks भी बदल गए हैं, और serverless architectures ऐसे दसियों हज़ार connections बना सकते हैं जहाँ PostgreSQL को दिक्कत शुरू होने लगती है। व्यक्तिगत रूप से मुझे कुछ सौ से ज़्यादा connections पसंद नहीं हैं, लेकिन आजकल यह पूरी तरह यथार्थवादी स्थिति है
    • उस समय कई कंपनियाँ Java द्वारा पेश किए गए threading model का पहली बार कोई कामचलाऊ जवाब निकाल रही थीं। यह Windows model का फीचर-समृद्ध रूप था, और Solaris भी Java workloads संभालने में जूझ रहा था, HP-UX तो उससे भी बदतर था, और SGI HP से बेहतर था या नहीं यह मुझे याद नहीं
      Java compatibility हासिल करने की प्रक्रिया में कई कंपनियों को concurrency handle करने के अपने तरीकों में बड़े बदलाव करने पड़े। PostgreSQL और SQLite भी लगभग उसी दौर में डिजाइन हुए थे, लेकिन इन्हें ऐसे अनुभवी उद्योग विशेषज्ञों ने बनाया था जो इन threading बहसों से पहले से ही high-load systems deploy कर रहे थे और पुराने hardware उपयोगकर्ताओं तक को support कर रहे थे