Di mongosh, koneksi adalah urusan yang tak terlihat — shell membukanya saat startup dan menutupnya saat kita keluar. Di aplikasi, siklus hidup koneksi menjadi tanggung jawab kode kita: kapan dibuat, kapan dipakai, dan kapan ditutup. Sebagian besar insiden “aplikasi tidak bisa tersambung” di produksi berakar pada kesalahan di siklus ini — client dibuat berulang tiap request, close() yang lupa dipanggil, atau timeout yang tidak pernah disetel. Chapter ini menelusuri empat tahap hidup koneksi dengan eksekusi driver sungguhan, lalu membahas pola lazy vs eager dan singleton.
Empat Tahap Hidup Koneksi #
Aturan mainnya: new MongoClient(uri) hanya membuat object konfigurasi tanpa menyentuh jaringan; connect() (atau operasi pertama) yang membuka koneksi sungguhan; operasi berjalan lewat pool; dan close() menutup semuanya. Diagram berikut merangkum transisinya:
Tahap “Terhubung” mencakup server selection, pembukaan TCP/TLS, handshake hello, dan autentikasi — baru setelah itu pool siap melayani operasi. Kita jalankan keempat tahap itu dalam satu file dengan driver Node.js (pasang dulu paketnya dengan npm install mongodb):
// siklus-koneksi.js — empat tahap hidup koneksi dengan driver Node.js
// Jalankan: node siklus-koneksi.js (butuh paket: npm install mongodb)
const { MongoClient } = require("mongodb")
const uri = process.env.MONGODB_URI ?? "mongodb://127.0.0.1:27017/?directConnection=true"
const client = new MongoClient(uri, { serverSelectionTimeoutMS: 5000 })
async function main() {
console.log("1. new MongoClient(uri) — client dibuat, jaringan belum disentuh")
await client.connect()
console.log("2. connect() selesai — handshake dan autentikasi berhasil")
const ping = await client.db("admin").command({ ping: 1 })
console.log("3. ping diterima, ok =", ping.ok)
const res = await client.db("latihan_siklus").collection("kunjungan")
.insertOne({ pada: new Date() })
console.log("4. insertOne acknowledged:", res.acknowledged, "| insertedId:", res.insertedId)
await client.close()
console.log("5. client.close() — seluruh koneksi pool ditutup")
}
main().catch(err => {
console.error("GAGAL:", err.name, "-", err.message)
process.exit(1)
})node siklus-koneksi.jsOutput yang Dihasilkan:
1. new MongoClient(uri) — client dibuat, jaringan belum disentuh
2. connect() selesai — handshake dan autentikasi berhasil
3. ping diterima, ok = 1
4. insertOne acknowledged: true | insertedId: new ObjectId('6ad3de9d5d99a094658a5408')
5. client.close() — seluruh koneksi pool ditutupMari kita bedah baris-per-baris: baris new MongoClient(uri, ...) hanya mengurai connection string dan menyimpan opsi — itulah mengapa tahap ini instan dan murah. Baris await client.connect() memaksa koneksi terbuka sekarang, sehingga galat konfigurasi atau kredensial langsung terlihat. command({ ping: 1 }) adalah pemeriksaan kesiapan paling ringan yang tersedia di semua driver. Perhatikan juga bentuk insertedId pada baris keempat: driver menampilkan BSON ObjectId sebagai new ObjectId('...') — tipe asli, bukan string biasa. Terakhir, client.close() menutup seluruh socket milik pool sekaligus.
Server merekam setiap koneksi yang masuk. Sebelum membaca catatannya, kita buktikan dulu bahwa koneksi dipakai ulang — dua command berurutan dari client yang sama berjalan lewat satu koneksi:
db.hello().connectionId === db.hello().connectionIdOutput yang Dihasilkan:
trueconnectionId kedua panggilan hello identik: mongosh (dan driver pada umumnya) tidak membuka koneksi baru untuk setiap operasi, melainkan memakai ulang yang sudah ada. Sekarang kita baca statistik sisi server:
const c = db.serverStatus().connections
printjson({ current: c.current, available: c.available, totalCreated: c.totalCreated })Output yang Dihasilkan:
{
current: 6,
available: 403,
totalCreated: 10160
}current adalah koneksi aktif saat ini, available sisa slot sebelum maxIncomingConnections tercapai, dan totalCreated akumulasi koneksi yang pernah dibuat sejak server menyala. Setiap connect() dari aplikasi menaikkan totalCreated — pengamatan ini kita pakai lagi secara mendalam di Chapter Connection Pooling.
Lazy atau Eager #
Dua gaya membuka koneksi: lazy (biarkan operasi pertama yang memicu) dan eager (connect() eksplisit di awal). File kecil berikut membuktikan bahwa driver memang mengizinkan gaya lazy:
// koneksi-malas.js — operasi pertama bisa memicu koneksi tanpa connect() eksplisit
// Jalankan: node koneksi-malas.js (butuh paket: npm install mongodb)
const { MongoClient } = require("mongodb")
const client = new MongoClient("mongodb://127.0.0.1:27017/?directConnection=true")
async function main() {
// Tidak ada client.connect() — operasi pertama-lah yang memicu koneksi
const jumlah = await client.db("latihan_siklus").collection("kunjungan").countDocuments()
console.log("operasi tanpa connect() eksplisit berhasil, jumlah dokumen:", jumlah)
await client.close()
console.log("close() tetap wajib dipanggil")
}
main().catch(err => {
console.error("GAGAL:", err.name, "-", err.message)
process.exit(1)
})Output yang Dihasilkan (latihan_siklus berisi satu document dari file sebelumnya):
operasi tanpa connect() eksplisit berhasil, jumlah dokumen: 1
close() tetap wajib dipanggilGaya lazy nyaman untuk skrip sekali jalan. Untuk server berumur panjang, pilih eager: connect() di saat startup membuat galat konfigurasi muncul saat boot — bukan pada request pengguna pertama. Prinsipnya sama dengan aplikasi apa pun: gagal lebih awal, saat masih murah.
Timeout yang Mengikat Setiap Tahap #
Setiap tahap siklus punya batas waktunya sendiri di sisi client. Nilai bawaan driver Node.js 7.x:
| Opsi | Bawaan | Mengikat |
|---|---|---|
serverSelectionTimeoutMS | 30000 | menunggu server yang cocok (tersambung + lolos autentikasi) |
connectTimeoutMS | 30000 | pembukaan TCP/TLS per koneksi |
socketTimeoutMS | 0 | lama menunggu jawaban per operasi (0 = tanpa batas) |
heartbeatFrequencyMS | 10000 | jeda pemeriksaan kesehatan topologi |
Gejala klasik salah setel: aplikasi terlihat menggantung 30 detik lalu melempar galat — hampir selalu serverSelectionTimeoutMS yang sedang menghitung. Turunkan nilainya untuk gagal cepat, naikkan bila jaringan ke cluster memang lambat.
Close dan Pola Singleton #
Koneksi yang tidak ditutup tetap memakan slot di server (current naik, available turun) dan file descriptor di proses aplikasi. Di Node.js ada gejala tambahan yang khas: socket yang terbuka menjaga event loop tetap hidup, sehingga skrip yang lupa close() selesai bekerja tetapi prosesnya tidak pernah keluar.
Untuk aplikasi berumur panjang, solusinya satu client per proses — dibuat saat startup, dibagikan ke seluruh request, ditutup saat proses menerima sinyal berhenti:
// mdtest: skip — pola umum pada aplikasi web Node.js
const { MongoClient } = require("mongodb")
const client = new MongoClient(process.env.MONGODB_URI)
// modul ini diekspor satu kali; seluruh handler memakai client dan pool yang sama
module.exports = clientDriver juga memancarkan event monitoring pool — connectionPoolCreated, connectionCreated, connectionCheckedOut — yang bisa disubsikripsi untuk observability; kita praktikkan lengkap di Chapter Connection Pooling. Database latihan_siklus dari latihan ini boleh dibersihkan kapan saja lewat db.getSiblingDB("latihan_siklus").dropDatabase().
Rangkuman #
- Empat tahap: buat client (tanpa I/O) →
connect()(server selection, TCP/TLS, handshake, auth) → operasi via pool →close(). - Gaya lazy membiarkan operasi pertama memicu koneksi; gaya eager (
connect()di startup) gagal cepat — pilihan yang tepat untuk server produksi. - Timeout bawaan:
serverSelectionTimeoutMS30 detik,connectTimeoutMS30 detik,socketTimeoutMStanpa batas — gejala “menggantung 30 detik” hampir selalu server selection. - Satu client per proses, dibagikan lintas request;
close()wajib dijalankan saat proses berhenti agar slot server dan event loop terbebaskan.