Mở một kết nối DB tốn 20-100ms (bắt tay TCP, thương lượng SSL, auth...). Nếu mỗi request mở mới rồi đóng thì với hàng nghìn req/s là sự cố nghiêm trọng. Connection pool giữ sẵn một rổ kết nối để tái dùng, request mượn xong trả lại.
PgBouncer (pooler phổ biến cho PostgreSQL, nằm giữa app và DB) có 3 chế độ:
- Session: giữ kết nối suốt phiên client.
- Transaction (khuyến nghị): trả về pool sau mỗi transaction.
- Statement: trả sau mỗi câu lệnh — mạnh tay nhất nhưng không hỗ trợ transaction nhiều lệnh.
Kích thước pool: công thức tham khảo (số core * 2) + số đĩa — vd 4 core + SSD ≈ 9 kết nối. To hơn chưa chắc tốt vì tốn chi phí chuyển ngữ cảnh.
Bẫy với serverless: mỗi instance Lambda/Edge có pool riêng → 1000 invocation đồng thời = 1000 × pool kết nối, dễ vượt max_connections (mặc định 100) của PostgreSQL. Khắc phục: PgBouncer ở chế độ transaction, Prisma Accelerate, hoặc driver serverless của Neon (kết nối qua HTTP, không cần TCP bền).
Opening a DB connection costs 20-100ms (TCP handshake, SSL negotiation, auth...). Opening and closing a fresh one per request is catastrophic at thousands of req/s. A connection pool keeps a basket of reusable connections; a request borrows one and returns it.
PgBouncer (the popular PostgreSQL pooler, sitting between app and DB) has 3 modes:
- Session: holds a connection for the whole client session.
- Transaction (recommended): returns it to the pool after each transaction.
- Statement: returns after each statement — most aggressive but doesn't support multi-statement transactions.
Pool size: a rule of thumb is (cores * 2) + disks — e.g. 4 cores + SSD ≈ 9 connections. Bigger isn't always better due to context-switching overhead.
The serverless trap: each Lambda/Edge instance has its own pool → 1000 concurrent invocations = 1000 × pool connections, easily exceeding PostgreSQL's max_connections (default 100). Fix: PgBouncer in transaction mode, Prisma Accelerate, or Neon's serverless driver (connects over HTTP, no persistent TCP).