วาง Connection Pool ระหว่าง Node.js กับ PostgreSQL ไม่ให้ชนเพดาน
คำนวณ pool จากจำนวน process และ pod ทั้งระบบ ตั้ง timeout ป้องกัน connection leak และพิจารณา PgBouncer เมื่อระบบมีหลาย instance หรือ autoscaling

ภาพรวม
PostgreSQL ไม่ได้พังเพราะ Node.js ต่อ DB ไม่เป็น หลายครั้งมันพังเพราะเรา “ต่อเยอะเกินไป” โดยไม่รู้ตัว 🧯
ภาพที่เจอบ่อยคือ local รันดีมาก แต่พอขึ้น production แล้วมี 4 pods แต่ละ pod เปิด pool 20 connections รวมเป็น 80 connections ทันที ยังไม่รวม worker, cron, migration, admin tool, BI tool
Postgres connection ไม่ใช่ของฟรี ทุก connection คือ backend process หนึ่งชุดฝั่ง database มี memory, context switching, lock state, transaction state และต้องถูก scheduler ของ OS จัดคิวทำงาน
ถ้ามี connection เยอะเกินไป ไม่ได้แปลว่า database ทำงานได้ขนานขึ้นเสมอ บางทีแปลว่า CPU เสียเวลาไปกับการสลับงาน RAM ถูกกินโดย session ที่นั่งรอ query จริงกลับช้าลงกว่าเดิม 🐢
จุดที่หลายคนมองข้ามคือ max_connections PostgreSQL มีเพดานจำนวน connection พร้อมกัน ค่า default มักอยู่แถว 100 บาง managed database อาจกำหนดตามขนาดเครื่อง และถ้าชนเพดานเมื่อไร app จะเริ่มเจอ error แนวนี้ “sorry, too many clients already”
ฝั่ง Node.js ปัญหามักเริ่มจาก pool เช่นใช้ node-postgres แล้วตั้ง max: 20 ฟังดูไม่เยอะ แต่ max นี้คือ “ต่อ process” ไม่ใช่ต่อทั้งระบบ
ถ้าใช้ Node cluster 4 processes pool max 20 = 80 connections ถ้า deploy Kubernetes 5 pods ก็กลายเป็น 400 connections ได้แบบเงียบ ๆ ทั้งที่ code มีแค่ new Pool() บรรทัดเดียว
ดังนั้นเวลา sizing pool อย่าถามว่า “app หนึ่งตัวควรมี pool เท่าไร” ให้ถามว่า “ทั้งระบบจะเปิด connection สูงสุดกี่เส้นพร้อมกัน”
สูตรคิดแบบ practical คือ เอา max_connections ที่ DB รับได้จริง หักไว้ให้ admin, migration, monitoring, background jobs แล้วค่อยแบ่งให้จำนวน instance ทั้งหมด
เช่น DB รับได้ 100 กันไว้ 20 สำหรับงานอื่น เหลือ 80 ให้ web app ถ้ามี 8 pods pool ต่อ pod อาจเริ่มที่ 10 ไม่ใช่ 30
สำหรับ production beginner pool เล็กแต่นิ่ง มักดีกว่า pool ใหญ่แต่สุ่มพัง ถ้า request ต้องรอ connection นิดหน่อยยังพอรับได้ แต่ถ้า DB รับ connection ใหม่ไม่ได้เลย ระบบจะเริ่มล้มเป็นลูกโซ่
อีกตัวที่ควรรู้คือ PgBouncer มันเป็น connection pooler อยู่หน้า PostgreSQL ฝั่ง app ต่อไปที่ PgBouncer แล้ว PgBouncer คุมจำนวน connection จริงไปยัง Postgres อีกที
ประโยชน์คือช่วยลดจำนวน backend connection ฝั่ง DB เหมาะมากกับหลาย pods, serverless, autoscale หรือ app ที่เปิด connection ถี่ ๆ แต่ต้องเข้าใจโหมดของมันด้วย transaction pooling ใช้ดีมากกับ web request ทั่วไป แต่ feature ที่ผูกกับ session ต้องระวัง เช่น prepared statements, temp tables, session variables
อีกเรื่องที่ช่วยชีวิตคือ timeout อย่าปล่อยให้ query หรือ transaction ค้างตลอดไป ตั้ง statement_timeout เพื่อกัน query ยาวผิดปกติ ตั้ง idle_in_transaction_session_timeout เพื่อฆ่า session ที่เปิด transaction แล้วลืมปิด และฝั่ง pool ควรมี idleTimeoutMillis / connectionTimeoutMillis ที่สมเหตุสมผล
connection leak คือฆาตกรเงียบ 🔍 ใน node-postgres ถ้า client = await pool.connect() แล้วลืม client.release() connection นั้นจะหายไปจาก pool จนกว่าจะ restart ยิ่งมี error path ที่ไม่ release ยิ่งเจ็บ ใช้ try/finally เสมอ หรือใช้ pool.query() สำหรับ query สั้น ๆ ที่ไม่ต้องถือ client เอง
สัญญาณว่าเริ่มมีปัญหา request ช้าทั้งที่ query ไม่ได้หนัก pool waiting count สูง Postgres มี connection จำนวนมากแต่หลายตัว idle มี idle in transaction ค้างนาน หรือ error too many clients already โผล่เป็นช่วง ๆ
สิ่งที่ควร monitor คือ จำนวน connection ต่อ database active / idle / idle in transaction เวลารอ connection จาก pool query ช้า และจำนวน pods/processes ที่กำลังรันจริง
สรุปแบบใช้งานได้เลย อย่าตั้ง pool max จากความรู้สึก คูณจำนวน process/pod ทุกครั้ง เหลือ connection เผื่อระบบอื่นเสมอ ใช้ PgBouncer เมื่อ scale หลาย instance ตั้ง timeout กันค้าง และตรวจ leak ด้วย try/finally
Connection pooling ไม่ใช่แค่ performance trick มันคือวิธีป้องกันไม่ให้ app ทั้งก้อนแย่งประตูเข้า DB จนประตูพัง 🚪
แนวทางนำไปใช้
- นำแนวคิดจากโพสต์นี้ไปทดลองกับงานจริงของคุณ
- แยกเป็นขั้นตอนเล็ก ๆ แล้วทำทีละส่วน
- บันทึกผลลัพธ์และสิ่งที่เรียนรู้เพื่อต่อยอด
สรุป
คำนวณ pool จากจำนวน process และ pod ทั้งระบบ ตั้ง timeout ป้องกัน connection leak และพิจารณา PgBouncer เมื่อระบบมีหลาย instance หรือ autoscaling