مستوى المقال: متوسط — مناسب لمطورين شغّالين على Node.js أو Python مع PostgreSQL ووصلوا لحاجز الـ connections في الإنتاج.
لو الـ API بتاع منصتك بيرجّع FATAL: sorry, too many clients already في ساعات الذروة، PostgreSQL مش ضعيف — انت بتفتح 1,200 connection على DB قابل لـ 100 فقط. PgBouncer 1.23 في transaction mode بيخلّي 1,000 طلب متزامن يشتغلوا على 25 connection حقيقي، وبيقلّل connection errors من 8,420 في الدقيقة لـ صفر، مع نزول P95 latency من 480ms لـ 28ms.
PgBouncer Transaction Pooling: تشريح المشكلة والحل العملي
المشكلة باختصار
كل process في Node.js cluster بيفتح pg-Pool خاص بيه، الافتراضي 10 connections. لو عندك 6 instances من الـ API على Kubernetes، بتطلع 60 connection ثابتة من غير ما حد يدخل الموقع. وقت الذروة، الـ pg-Pool بيفتح connections إضافية لأي query، وفجأة بتعدّي حاجز max_connections في postgresql.conf اللي افتراضه 100.
كل connection في Postgres بياخد حوالي 9MB ذاكرة (backend process)، وبيستهلك file descriptor، وبيدخل في contention على ProcArrayLock. النتيجة المباشرة: زمن الـ query بيطلع من 12ms لـ 480ms حتى لو الـ query نفسها بسيطة، لأن الـ scheduling بقى الـ bottleneck مش الـ disk.
تشبيه للمبتدئ: مطعم فيه 100 طاولة و1,200 زبون
تخيّل مطعم بـ 100 طاولة (دي هي max_connections). لو دخل 1,200 زبون مرة واحدة، الـ 1,100 الباقيين هيقفوا برّه ويلغوا الطلب (ده الـ connection refused). الحل التقليدي إنك تشتري المطعم اللي جنبك عشان تزوّد طاولات — ده غالي وبيحتاج RAM وCPU إضافي على السيرفر.
PgBouncer هو الـ host اللي بيقف على الباب. بيقول للزبون: "استنى على الكنبة، الطاولة جاية في 80 مللي ثانية". الزبون بياخد طاولة بس وقت ما هو طالبها فعلاً (transaction)، ولما يخلّص، الطاولة بترجع للي بعده فوراً من غير ما حد ينضف الكرسي. النتيجة: 100 طاولة بتخدم 1,200 زبون من غير ما حد يقف برّه.
الشرح العلمي: ليه Transaction Mode الأهم
PgBouncer بيقدّم 3 modes للـ pooling حسب التوثيق الرسمي:
- session pooling: الـ client بياخد connection طول مدة جلسته. ده مش بيحل المشكلة، بس بيوحّد الإدارة.
- transaction pooling: الـ connection بيتحجز بس وقت ما في transaction مفتوحة. أوقف الـ transaction (COMMIT أو ROLLBACK)، الـ connection بيرجع للـ pool. ده الـ mode اللي بيعمل المعجزة.
- statement pooling: الـ connection بيرجع بعد كل statement. ممنوع فيه transactions متعدّدة العبارات تمامًا.
الافتراض في الكلام ده: تطبيقاتك stateless زي REST APIs، transactions قصيرة (أقل من 50ms)، ومش بتعتمد على session-level state زي SET العادي أو advisory locks الجلسية. لو الـ workload بتاعك من النوع ده، انت أصلًا المرشّح الطبيعي لـ transaction pooling.