← Semua pembelajaran / Go Nol → Enterprise
Fase 4 · Database & Persistensi

Connection pool — angka yang menentukan skala

Default database/sql adalah koneksi tak terbatas. Di laptop itu tidak terasa; di produksi dengan sepuluh container, itu cara tercepat menghabiskan slot koneksi Postgres dan menjatuhkan semuanya sekaligus.

Sumber asli pkg.go.dev Resmi Rangkuman ~8 menit baca

Intisari

  • MaxOpenConns × jumlah task harus lebih kecil dari max_connections database — sisakan ruang untuk migrasi dan admin.
  • Default MaxIdleConns adalah 2. Terlalu kecil: koneksi terus dibuka-tutup, dan TLS handshake-nya mahal.
  • Setel MaxIdleConns sama dengan MaxOpenConns untuk beban stabil.
  • ConnMaxLifetime wajib di AWS — tanpa itu, failover RDS meninggalkan koneksi mati di pool.
  • Pool yang habis muncul sebagai semua endpoint melambat bersamaan, bukan sebagai error database.

Empat setelan

db.SetMaxOpenConns(25)                    // batas keras koneksi ke database
db.SetMaxIdleConns(25)                    // yang dipertahankan saat menganggur
db.SetConnMaxLifetime(30 * time.Minute)   // umur maksimum satu koneksi
db.SetConnMaxIdleTime(5 * time.Minute)    // dibuang kalau menganggur selama ini
SetelanDefaultKalau salah
MaxOpenConnstak terbatasDatabase menolak koneksi; semua aplikasi yang memakainya ikut mati
MaxIdleConns2Koneksi dibuka-tutup terus; latensi naik karena handshake TLS berulang
ConnMaxLifetimetak terbatasSetelah failover RDS, koneksi lama tetap dipakai dan gagal
ConnMaxIdleTimetak terbatasKoneksi menganggur menahan memori di sisi database

Aritmetikanya

Batas database (RDS Postgres db.t4g.medium)   ≈ 340 koneksi

Dikurangi cadangan:
  superuser + monitoring RDS                    -10
  task migrasi saat deploy                      -10
  koneksi admin / psql darurat                   -5
                                              ------
  tersedia untuk aplikasi                       315

Jumlah task saat autoscaling puncak:
  service web     : 10 task
  service worker  :  4 task
  service cron    :  1 task
                    -------
                     15 task

MaxOpenConns maksimum = 315 / 15 = 21  →  pakai 20

Yang paling sering dilupakan: max_connections di RDS ditentukan ukuran instans. Menaikkan jumlah task ECS dari 4 jadi 20 saat trafik melonjak juga melipatgandakan kebutuhan koneksi — dan yang terjadi bukan aplikasi melambat, tapi database menolak koneksi baru sehingga seluruh layanan (termasuk yang tadinya sehat) ikut gagal. Hitung untuk jumlah task maksimum, bukan yang sekarang.

Berapa nilai yang tepat

BebanMaxOpenConns per task
API baca-berat, kueri cepat (< 10 ms)10–25
Aplikasi campuran20–30
Worker batch, kueri panjang5–10 (kueri lama menahan koneksi)
Lambda1–2, atau pakai RDS Proxy

Lebih banyak koneksi tidak berarti lebih cepat. PostgreSQL memakai satu proses per koneksi; di luar titik tertentu, menambah koneksi justru menurunkan throughput karena penjadwalan dan penguncian internal. Rumus yang sering dipakai sebagai titik awal adalah sekitar (2 × core) + jumlah disk untuk seluruh aplikasi digabung — angka yang jauh lebih kecil daripada dugaan kebanyakan orang.

Kenapa ConnMaxLifetime wajib di AWS

t=0    Aplikasi punya 25 koneksi ke writer Aurora
t=100  Aurora failover: reader lama dipromosikan jadi writer
t=101  Endpoint DNS menunjuk instans baru
t=102  Aplikasi masih memakai 25 koneksi ke instans LAMA
       → semua tulisan gagal "read-only transaction"
       → dan tidak akan pulih sendiri, karena koneksinya tidak pernah kedaluwarsa

SetConnMaxLifetime(30 * time.Minute) memaksa setiap koneksi dibuang dan dibangun ulang secara berkala, sehingga DNS di-resolve lagi dan pool berpindah sendiri ke instans yang benar. Nilainya harus lebih pendek daripada batas apa pun yang memutus koneksi dari sisi lain: idle_timeout di RDS Proxy, timeout NLB, atau tcp_keepalive jaringan.

Untuk pemulihan lebih cepat, pakai endpoint yang tepat. Aurora punya cluster endpoint (selalu writer) dan reader endpoint. Arahkan pool tulis ke yang pertama dan pool baca ke yang kedua — dua *sql.DB terpisah — supaya beban baca pindah ke replica tanpa mengubah kode domain.

Memantau pool

s := db.Stats()

s.OpenConnections   // koneksi terbuka saat ini
s.InUse             // sedang dipakai kueri
s.Idle              // menganggur di pool
s.WaitCount         // BERAPA KALI ada yang antre menunggu koneksi
s.WaitDuration      // TOTAL waktu yang dihabiskan menunggu
s.MaxIdleClosed     // ditutup karena MaxIdleConns
s.MaxLifetimeClosed // ditutup karena ConnMaxLifetime

WaitCount dan WaitDuration adalah dua metrik terpenting di seluruh lapisan database. Selama keduanya nol, poolmu cukup. Begitu naik, setiap permintaan mulai menghabiskan waktunya menunggu giliran — dan itu muncul sebagai p95 yang meledak sementara p50 masih terlihat sehat. Ekspor keduanya ke CloudWatch dan pasang alarm (Fase 9).

// Ekspor berkala sebagai metrik
go func() {
	for range time.Tick(15 * time.Second) {
		s := db.Stats()
		metrik.Gauge("db.pool.in_use", float64(s.InUse))
		metrik.Gauge("db.pool.idle", float64(s.Idle))
		metrik.Gauge("db.pool.wait_count", float64(s.WaitCount))
	}
}()

Gejala pool habis

Yang terlihatYang sebenarnya terjadi
Semua endpoint melambat bersamaanSemua antre di pool yang sama
Endpoint tanpa database ikut lambatBukan pool — cek CPU atau GC
Error context deadline exceeded pada kueri sepeleWaktu habis saat menunggu koneksi, kuerinya sendiri belum jalan
Membaik setelah restart, memburuk lagi setelah beberapa jamKoneksi bocor — rows.Close() yang hilang
Database bilang "too many connections"MaxOpenConns × jumlah task melebihi kapasitas

RDS Proxy: kapan perlu

SituasiRDS Proxy?
ECS/EC2 dengan jumlah task stabilTidak perlu — pool aplikasi sudah cukup
LambdaYa — tiap eksekusi punya poolnya sendiri, jumlahnya meledak
Autoscaling agresif (2 → 50 task)Ya — meratakan lonjakan koneksi
Butuh failover lebih cepatYa — proxy menahan koneksi klien selama failover

Latihan: setel SetMaxOpenConns(2) lalu jalankan 20 permintaan bersamaan ke endpoint yang menjalankan SELECT pg_sleep(1). Cetak db.Stats() setiap 500 ms dan amati WaitCount naik. Naikkan ke 20 dan ulangi. Terakhir, hitung MaxOpenConns yang benar untuk aplikasimu memakai aritmetika di atas.

Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.