المشكلة باختصار
كل طلب HTTP بيوصل لتطبيق Node.js أو Python بياخد connection من pool داخلي، يستخدمها في query أو اتنين، ثم يرجّعها. المشكلة بتبدأ لما عدد الطلبات المتزامنة يبقى أكتر من عدد الـ connections المتاحة. الطلب اللي ميلاقيش connection يستنّى. لو الطابور طوّل، الطلبات بتعمل timeout والـ user بيشوف 503.
ليه PostgreSQL مش بيتحمّل آلاف الـ connections زي MySQL؟
قبل ما ندخل في الحل، لازم نفهم ليه PostgreSQL بالظبط هو اللي بيقع في الفخ ده.
مثال للمبتدئين
تخيّل مطعم فيه 10 طاولات بس. لو جه 30 زبون في نفس الوقت، 10 بياكلوا والباقي 20 يقفوا برّة. لو الزبون اللي قاعد بياكل ببطء، الطابور بيطوّل أكتر. الطباخ شاطر، القائمة كويسة، بس عدد الطاولات هو اللي بيحدد كام واحد يقدر يتعامل معاه في نفس اللحظة. الـ connection في PostgreSQL هي الطاولة، وكل طلب لازم ياخد طاولة قبل ما يبدأ.
التعريف العلمي الدقيق
PostgreSQL بيستخدم نموذج process-per-connection. كل connection جديدة بتعمل fork لـ backend process مستقل بياخد ~10MB RAM وعنده شغل context switching على CPU. ده مختلف عن MySQL أو SQL Server اللي بيستخدموا threads أخفّ. النتيجة: PostgreSQL غير عملي يفتح أكتر من 100–300 connection حقيقي على سيرفر متوسط، حتى لو الـ max_connections في الـ config مكتوب 1000. لو زوّدته كتير، الـ context switching هياكل CPU وأنت قاعد.
الموقف اللي بيوقّع التطبيق
تخيّل تطبيق Node.js + Prisma، شغّال على 10 ECS tasks. كل task بياخد connection_limit = 20 في الـ pool بتاعها. ده معناه إن التطبيق ممكن يطلب 200 connection لو كل tasks ضربت في نفس الوقت. لو الـ PostgreSQL مظبوطة على max_connections = 170 (المعقول لـ db.t3.large)، 30 طلب على الأقل هيرجعوا error. والـ DB نفسها هتبدأ تعاني من context switching فوق 100 backend process، فالـ CPU يطلع 85% بسبب الـ overhead، مش بسبب شغل حقيقي.
PgBouncer كحل وسيط
PgBouncer هو pooler خفيف جدًا (process واحد، single-threaded، C). بيشتغل بين التطبيق وقاعدة البيانات. التطبيق بيتصل بـ PgBouncer زي ما هو متصل بـ PostgreSQL عادي (نفس البروتوكول)، وPgBouncer بيمسك آلاف الـ client connections المنطقية، ويقسّمهم على عدد قليل من الـ server connections الفعلية اللي مفتوحة على PostgreSQL.
الثلاث أوضاع: ركّز على ده
- session pooling: الـ client بياخد connection ثابتة طول الجلسة. مش بيحل المشكلة؛ نفس عدد الـ connections اللي عندك دلوقتي.
- transaction pooling: الـ connection بترجع للـ pool بعد كل أو . ده الوضع اللي بيعطي 10× أو 20× مكسب فعلي.