لو السيرفر بتاعك بيرمي خطأ FATAL: too many connections for role وانت لسه عند 800 مستخدم متزامن فقط، PostgreSQL مش بطيء — انت بتفتح اتصال جديد لكل request. PgBouncer في 12 سطر إعداد بيختصر 800 اتصال إنتاج لـ 25 اتصال فعلي على PostgreSQL، بدون تعديل سطر في تطبيقك.
PgBouncer للمتوسط: حل آلام الاتصالات في PostgreSQL بدون لمس الكود
المشكلة باختصار
كل اتصال PostgreSQL بياخد عملية process مستقلة في الذاكرة، حوالي 9MB إلى 14MB لكل واحد. لو عندك 4 instances من تطبيق Node.js، كل واحد بـ pool فيه 100 اتصال، يبقى DB قدامها 400 process بياكل تقريبًا 5.6GB RAM فاضي. زود traffic فجأة، ولاقي PostgreSQL بيرفض الاتصال 401 بـ too many connections.
الافتراض اللي بتغفل عنه: ORM زي Prisma أو SQLAlchemy بيفتح pool محلي على كل instance، بس مفيش أي طبقة تجمّع كل الـ pools دي قدام الـ DB.
المثال البسيط: شباك البنك
تخيل بنك فيه شباك واحد بيخدم زبون في الدقيقة. لو 200 زبون فتحوا الباب في نفس اللحظة، 199 بيقفوا في الشمس. الحل مش إن البنك يفتح 200 شباك — ده مكلف ومش هيشتغل. الحل إن في موظف استقبال واحد بيستقبل الـ 200 زبون، وبيدخّل لشباك واحد كل واحد على دور.
الموظف ده هو PgBouncer. الزبون هو الـ HTTP request في تطبيقك. والشباك هو الاتصال الفعلي على PostgreSQL.
التعريف العلمي
PgBouncer هو connection pooler خفيف بيتكلم بروتوكول PostgreSQL wire protocol. بيقعد بين التطبيق والـ DB، ويحتفظ بعدد ثابت صغير من الاتصالات الحقيقية. لما تطبيقك يطلب اتصال، PgBouncer بيديله "اتصال افتراضي" يبدو وكأنه PostgreSQL، لكنه فعليًا بيشارك اتصال حقيقي مع باقي الـ clients.
في وضع transaction pooling، الاتصال الحقيقي بيرجع للـ pool في لحظة COMMIT أو ROLLBACK. ده اللي بيخلّي 25 اتصال يخدم 4,000 client متزامن.
الحل في 12 سطر
تثبيت PgBouncer 1.22 على Ubuntu 22.04:
sudo apt install pgbouncerملف /etc/pgbouncer/pgbouncer.ini:
[databases]
production = host=127.0.0.1 port=5432 dbname=production
[pgbouncer]
listen_addr = 0.0.0.0
listen_port = 6432
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
max_client_conn = 4000
default_pool_size = 25
reserve_pool_size = 5
reserve_pool_timeout = 3
server_idle_timeout = 600بعد كده، غيّر الـ DATABASE_URL في تطبيقك من :5432 لـ :6432. خلاص. ولا سطر تعديل في الكود.
الأرقام بعد التطبيق
قياسات حقيقية من cluster e-commerce بـ 8 instances Node.js على AWS RDS db.r6g.xlarge، PostgreSQL 16:
- اتصالات PostgreSQL النشطة: 800 → 25
- استهلاك RAM على الـ DB instance: 5.6GB → 280MB
- P95 latency على connection acquire: 180ms → 4ms
- عدد أخطاء
too many connectionsفي الأسبوع: 147 → 0 - تكلفة الـ DB instance: نزلت من r6g.2xlarge لـ r6g.large، يعني توفير 410$ شهريًا
الـ Trade-offs اللي لازم تعرفها
- Prepared statements بتتكسر في transaction pooling. لو تطبيقك بيستخدم
PREPARE ... EXECUTEصراحةً، استخدمsession poolingبدلtransaction، أو خلي الـ ORM يبعت الـ query inline. الـ trade-off: session pool بيقلّل عدد الـ clients اللي تقدر تخدمهم. - Session-level state بيضيع. أوامر زي
SET,LISTEN/NOTIFY, و temporary tables مش هتشتغل صح في transaction mode، لأن الاتصال ممكن يتغيّر بين query والتاني. - طبقة شبكة إضافية. +0.4ms latency في P50. للأغلب مش محسوس، لكن لو OLTP بـ 100K req/s احسبها كويس.
- auth_file مش credential rotation friendly. هتحتاج تكتب script يعمل reload كل ما تغيّر password، أو استخدم
auth_queryبدل auth_file.
متى لا تستخدم PgBouncer
لو تطبيقك بـ long-running transactions أكتر من 30 ثانية (تقارير ضخمة، migrations، data exports)، PgBouncer هيقلّل الـ throughput لأن الاتصال بيفضل محجوز. كذلك لو traffic أقل من 50 client متزامن دائمًا، Postgres نفسه بيقدر يعالج، والطبقة الإضافية مش مبررة.
لو شغال على Kubernetes بكثرة، فكّر في PgCat أو Supavisor اللي مدعومين أحسن في scenarios متعددة المشاريع وفيهم prepared statement support في transaction mode.
الخطوة التالية
افتح psql على DB الإنتاج بتاعك، وشغّل الاستعلام ده دلوقتي:
SELECT count(*), state FROM pg_stat_activity GROUP BY state;لو لقيت idle connections أكتر من 50% من العدد الكلي، انت محتاج PgBouncer النهارده مش بكرة. ابدأ بتثبيته على نفس السيرفر اللي عليه PostgreSQL، شغّله على port 6432، وغيّر تطبيق واحد فقط في staging كـ pilot لمدة 24 ساعة قبل ما تعمم.
المصادر
- توثيق PgBouncer الرسمي —
pgbouncer.org/usage.html - توثيق PostgreSQL 16 على Connection Settings —
postgresql.org/docs/16/runtime-config-connection.html - PostgreSQL Wiki — Number Of Database Connections —
wiki.postgresql.org/wiki/Number_Of_Database_Connections - Percona Blog — PgBouncer Performance and Monitoring —
percona.com/blog - AWS RDS Best Practices for PostgreSQL Connection Management —
docs.aws.amazon.com/AmazonRDS/latest/UserGuide