RDS & Aurora untuk aplikasi Go
Database terkelola menghilangkan pekerjaan operasional, tapi tidak menghilangkan keputusan: berapa koneksi, ke endpoint mana, dan apa yang terjadi pada pool-mu saat failover berlangsung.
Intisari
- Aurora punya cluster endpoint (selalu writer) dan reader endpoint (menyebar ke replica).
- Pakai dua
*sql.DBterpisah β satu untuk tulis, satu untuk baca β supaya beban baca pindah tanpa mengubah kode domain. ConnMaxLifetimewajib: tanpa itu, pool tetap memegang koneksi ke instans lama setelah failover (Fase 4).- Replica punya lag: baca-setelah-tulis harus diarahkan ke writer.
- Aurora Serverless v2 bagus untuk beban tidak menentu; instans tetap lebih murah untuk beban stabil.
Endpoint dan perannya
toko.cluster-abc123.ap-southeast-1.rds.amazonaws.com β WRITER
toko.cluster-ro-abc123.ap-southeast-1.rds.amazonaws.com β READER (menyebar)
toko-instance-1.abc123.ap-southeast-1.rds.amazonaws.com β instans tertentu
(jangan dipakai aplikasi)
type DB struct {
Tulis *pgxpool.Pool
Baca *pgxpool.Pool
}
func Buka(ctx context.Context, cfg config.Config) (*DB, error) {
tulis, err := bukaPool(ctx, cfg.DSNWriter, 20)
if err != nil {
return nil, err
}
// Reader boleh punya pool lebih besar: kueri baca biasanya lebih pendek,
// dan replica-nya bisa lebih dari satu.
baca, err := bukaPool(ctx, cfg.DSNReader, 30)
if err != nil {
tulis.Close()
return nil, err
}
return &DB{Tulis: tulis, Baca: baca}, nil
}
// Repository memutuskan pool mana β domain tidak perlu tahu (Fase 5).
func (r *Repo) Ambil(ctx context.Context, id int64) (Produk, error) {
return ambil(ctx, r.db.Baca, id) // boleh sedikit basi
}
func (r *Repo) Simpan(ctx context.Context, p Produk) error {
return simpan(ctx, r.db.Tulis, p)
}
// Baca-setelah-tulis WAJIB ke writer: replica bisa tertinggal.
func (r *Repo) AmbilSetelahTulis(ctx context.Context, id int64) (Produk, error) {
return ambil(ctx, r.db.Tulis, id)
}
Replication lag adalah sumber bug yang membingungkan pengguna. Pengguna menyimpan perubahan, halaman berikutnya membaca dari replica yang tertinggal 200 ms, dan perubahannya "hilang". Aturan praktis: setelah operasi tulis, baca berikutnya dalam permintaan yang sama β dan biasanya juga permintaan berikutnya dari pengguna itu β harus ke writer.
Failover
t=0 Instans writer bermasalah
t=5 Aurora mempromosikan reader jadi writer baru
t=6 DNS cluster endpoint menunjuk instans baru (TTL ~5 detik)
t=6 β Pool aplikasi MASIH memegang 20 koneksi ke instans lama
β semua tulisan gagal "cannot execute INSERT in a read-only transaction"
β dan TIDAK pulih sendiri, karena koneksinya tidak pernah kedaluwarsa
β
Dengan ConnMaxLifetime = 5 menit:
koneksi dibuang bertahap, DNS di-resolve ulang, pool pindah sendiri
cfg.MaxConnLifetime = 5 * time.Minute // lebih pendek dari biasanya
cfg.MaxConnIdleTime = 1 * time.Minute
cfg.HealthCheckPeriod = 30 * time.Second
Untuk pemulihan yang lebih cepat, tangani error read-only transaction secara khusus:
kalau muncul, tutup koneksi tersebut dan ambil yang baru. Tapi ConnMaxLifetime yang pendek
sudah menutup sebagian besar kasus, dan jauh lebih sederhana.
Memilih konfigurasi
| Pilihan | Cocok kalau | Catatan |
|---|---|---|
| RDS PostgreSQL, Single-AZ | Pengembangan / staging | Termurah; ada jeda saat pemeliharaan |
| RDS PostgreSQL, Multi-AZ | Produksi kecilβmenengah | Failover 60β120 detik |
| Aurora PostgreSQL | Produksi | Failover < 30 detik; reader murah ditambahkan |
| Aurora Serverless v2 | Beban tidak menentu | Skala per 0,5 ACU; mahal kalau selalu ramai |
Batas koneksi
max_connections di RDS ditentukan ukuran instans:
db.t4g.medium (4 GB) β 340
db.r6g.large (16 GB) β 1.600
db.r6g.xlarge (32 GB) β 3.000
Hitung untuk jumlah task MAKSIMUM (Fase 4):
(MaxOpenConns Γ task web) + (worker) + (cron) + cadangan < max_connections
20 Γ 20 web = 400
10 Γ 4 work = 40
10 Γ 1 cron = 10
cadangan = 25 (migrasi, psql darurat, monitoring)
ββββββββββββββββββ
total = 475 β butuh instans yang lebih besar dari t4g.medium
Autoscaling ECS bisa menjatuhkan database. Trafik naik β ECS menambah task β koneksi melampaui batas β database menolak koneksi baru β seluruh layanan gagal, termasuk task yang tadinya sehat. Batas maksimum autoscaling harus diturunkan dari kapasitas koneksi database, bukan dari anggaran saja.
Kredensial dan rotasi
Opsi A: Secrets Manager dengan rotasi terkelola
β
sederhana, kredensial berputar otomatis
β οΈ rotasi harus memicu deployment baru, atau aplikasi harus memuat ulang
Opsi B: Autentikasi IAM ke database
β
tanpa sandi sama sekali; token berumur 15 menit
β οΈ ada batas laju pembuatan token; perlu penanganan di kode
// TLS ke RDS bukan opsional (Fase 6)
dsn := "postgres://pengguna:sandi@host:5432/toko?sslmode=verify-full&pool_max_conns=20"
Yang harus dipantau
| Metrik | Alarm kalau |
|---|---|
DatabaseConnections | > 80% dari max_connections |
CPUUtilization | > 80% berkelanjutan |
FreeableMemory | Turun terus β working set melebihi RAM |
ReadLatency / WriteLatency | > 20 ms |
AuroraReplicaLag | > 1 detik |
DiskQueueDepth | Naik β I/O jadi kemacetan |
Deadlocks | > 0 secara rutin (Fase 4) |
Nyalakan Performance Insights sejak awal. Ia menunjukkan kueri mana yang memakan waktu
database terbanyak β padanan terkelola dari pg_stat_statements (Fase 4), dan biasanya
jawaban pertama saat p95 naik tanpa perubahan kode.
Cadangan dan pemulihan
- Backup otomatis minimal 7 hari; 30 hari untuk data yang menyangkut uang.
- Point-in-time recovery aktif β menyelamatkan dari
DELETEtanpaWHERE. - Perlindungan penghapusan aktif di produksi.
- Uji pemulihannya setahun sekali. Backup yang belum pernah dipulihkan bukan backup.
- Snapshot manual sebelum migrasi besar β dan sebelum setiap perubahan skema yang tidak bisa dibatalkan.
Latihan: siapkan cluster Aurora dengan satu reader, pisahkan pool baca dan tulis di aplikasimu,
lalu picu failover manual dari konsol sambil mengirim trafik. Catat berapa lama sampai aplikasimu pulih
sendiri β dengan dan tanpa ConnMaxLifetime.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.