Connection pool adalah kumpulan koneksi database yang sudah dibuka dan di-reuse oleh aplikasi — alih-alih membuka koneksi baru setiap request. Ini praktik standar untuk aplikasi production.
Kenapa Connection Pooling Wajib?
Membuka koneksi database itu mahal — biasanya 50–200ms untuk TCP handshake, authentication, SSL negotiation. Untuk request API yang idealnya <100ms total, connection overhead ini mematikan.
Juga: database punya limit maksimum koneksi. PostgreSQL default max_connections = 100. Kalau aplikasi kamu punya 50 worker × 2 koneksi per request = sudah mentok. Request ke-51 harus antri atau gagal.
Tanpa pooling: Dengan pooling:
Request → connect (100ms) Request → ambil idle conn (<1ms)
→ query (5ms) → query (5ms)
→ disconnect → kembalikan ke pool
Total: ~105ms Total: ~6ms
Built-in Pool (per aplikasi)
Framework modern punya pool built-in:
// Laravel (config/database.php)
"mysql" => [
"driver" => "mysql",
// ...
"options" => [
PDO::ATTR_PERSISTENT => true, // reuse connections
],
],
// Node.js with pg
const { Pool } = require("pg");
const pool = new Pool({
host: "localhost",
database: "mydb",
max: 20, // max connection dalam pool
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
});
// Setiap pool.query() ambil dari pool, kembalikan otomatis setelah selesai
const result = await pool.query("SELECT * FROM users WHERE id = $1", [id]);
Tapi built-in pool per-process. Kalau kamu jalanin 10 worker Laravel/Node, masing-masing punya pool sendiri. Database harus handle 10 × pool-size connection total.
External Pooler: PgBouncer / ProxySQL
Untuk production skala besar, pasang connection pooler eksternal di depan database. Aplikasi connect ke pooler, pooler me-multiplex ke database.
App 1 ─┐
App 2 ─┼─→ PgBouncer (port 6432) ─→ PostgreSQL (port 5432)
App 3 ─┤ [1000 incoming conns] [20 outgoing conns]
App N ─┘
Manfaat:
- 1000 client connection di pooler = 20 connection ke DB sebenarnya
- Database resource hemat (memory, CPU)
- Pooler handle failover & retry
3 Mode PgBouncer
| Mode | Connection Sharing | Cocok untuk | Caveat |
|---|---|---|---|
| Session | 1 client = 1 server conn selama session | Legacy apps | Hampir tidak ada benefit vs no-pool |
| Transaction | 1 server conn di-share antar client per transaction | REST API, web apps | Tidak bisa pakai prepared statements, SET statements |
| Statement | Rotate conn per query | Stateless sangat strict | Banyak fitur Postgres tidak jalan |
Default yang dipakai production: transaction mode. 90% aplikasi web cocok.
Sizing the Pool — Aturan Praktis
Terlalu kecil = antrian. Terlalu besar = memory wasted di DB.
Formula PostgreSQL ([^wiki]):
connections = ((core_count * 2) + effective_spindle_count)
Untuk SSD 4-core: (4 * 2) + 1 = 9 connection per pool instance bisa sangat cukup.
Myth: makin banyak pool size = makin cepat. Reality: kalau DB cuma bisa proses 10 query paralel, pool 1000 cuma menciptakan bottleneck di sisi DB.
Monitoring — Apa yang Harus Dipantau
-- PostgreSQL: connection aktif saat ini
SELECT count(*), state FROM pg_stat_activity GROUP BY state;
-- Max connection setting
SHOW max_connections;
-- Connection yang idle in transaction (leaky!)
SELECT pid, state, query_start, query FROM pg_stat_activity
WHERE state = 'idle in transaction'
AND query_start < now() - interval '5 minutes';
"idle in transaction" adalah red flag — aplikasi lupa commit/rollback, koneksi stuck tidak bisa dipakai orang lain. Set idle_in_transaction_session_timeout = 60s di postgresql.conf untuk auto-kill.
Checklist Production
- Pool size di-tune sesuai core DB (bukan asal besar)
- PgBouncer/ProxySQL di-deploy kalau >1 app server
-
idle_in_transaction_session_timeoutdi-set - Monitoring connection state (aktif, idle, idle-in-transaction)
- Alert kalau connection usage >70% dari max
- Prepared statement di-disable kalau pakai transaction mode PgBouncer
[^wiki]: PostgreSQL wiki "Number of Database Connections" — baca untuk formula yang lebih presisi.