Kapan sebuah write boleh disebut “berhasil”? Jawabannya tidak tunggal: begitu primary menerima write, berapa anggota replica set yang harus turut menyimpan salinannya sebelum aplikasi diberi kabar sukses? Keputusan itu diatur oleh write concern — dan pilihannya adalah tawar-menawar langsung antara durabilitas dan kecepatan. Chapter ini membedah tiga tombol pengaturnya (w, j, wtimeout), mengujinya pada rs0 dengan keluaran asli, lalu menutup dengan kasus write yang gagal diakui padahal sudah tersimpan.
Anatomi Write Concern: w, j, wtimeout #
Write concern menyertai setiap operasi tulis dan terdiri dari tiga field:
| Field | Bentuk | Arti |
|---|---|---|
w | 1 | Primary saja yang harus mengakui |
"majority" | Mayoritas anggota data-bearing harus mengakui | |
N (mis. 2, 3) | Tepat N anggota harus mengakui | |
j | true / false | Pengakuan dihitung setelah write masuk journal di disk |
wtimeout | milidetik | Batas waktu menunggu pengakuan; lewat dari itu operasi dilaporkan gagal |
w menjawab “berapa salinan”, j menjawab “seberapa tahan dari restart”, dan wtimeout menjawab “berapa lama aplikasi bersedia menunggu”. Nilai w: 1 tanpa j adalah pengakuan paling longgar — write sudah dilaporkan sukses sebelum sempat tersentuh disk; nilai "majority" dengan j: true adalah yang paling ketat di antara bentuk umum.
Eksperimen di rs0: Majority dengan Journal #
Kita tulis satu pesanan dengan write concern paling ketat itu dan mengamati jawaban server secara penuh:
use latihan_replika
db.dropDatabase()
db.runCommand({
insert: "pesanan",
documents: [{ kode: "PSN-001", pelanggan: "Budi", buku: "Laut Bercerita", jumlah: 2 }],
writeConcern: { w: "majority", j: true, wtimeout: 5000 }
})Output yang Dihasilkan:
switched to db latihan_replika
{ ok: 1, dropped: 'latihan_replika' }
{
n: 1,
electionId: ObjectId('7fffffff0000000000000008'),
opTime: { ts: Timestamp({ t: 1792267877, i: 2 }), t: Long('8') },
ok: 1,
'$clusterTime': {
clusterTime: Timestamp({ t: 1792267877, i: 2 }),
signature: {
hash: Binary.createFromBase64('AAAAAAAAAAAAAAAAAAAAAAAAAAA=', 0),
keyId: Long('0')
}
},
operationTime: Timestamp({ t: 1792267877, i: 2 })
}Jawaban ini bentuk acknowledged write yang sehat. Mari kita bedah fieldnya:
n: 1: satu document berhasil dimasukkan.opTime: posisi entri oplog write ini — pasangantsdantyang sama dengan yang kita lihat di Chapter Oplog.ok: 1tanpawriteConcernError: write concern terpenuhi — mayoritas anggota telah mengakui write, dan pengakuan itu berlaku setelah journal di disk. Dirs0yang beranggota satu node, primary sendiri adalah mayoritas, sehinggaw: "majority"tercapai begitu journal lokal selesai.$clusterTime/operationTime: penanda waktu logical clock cluster — dipakai fitur konsistensi dan transaksi.
Keluaran di atas juga menjelaskan bentuk kegagalannya: bila write concern tidak terpenuhi, respons membawa field writeConcernError berisi kode galat dan pesannya — write tetap ok: 1 karena penerapan di primary sukses, yang gagal adalah pengakuannya.
Durabilitas versus Latensi #
Mari kita ukur harganya. Tiga varian write concern dijalankan berurutan dengan pengukuran waktu di sekitarnya:
const t0 = Date.now()
const r1 = db.runCommand({ insert: "pesanan", documents: [{ kode: "PSN-002", pelanggan: "Siti", buku: "Bumi Mantra", jumlah: 1 }], writeConcern: { w: 1 } })
const t1 = Date.now()
const r2 = db.runCommand({ insert: "pesanan", documents: [{ kode: "PSN-003", pelanggan: "Dewi", buku: "Rumah Kaca", jumlah: 3 }], writeConcern: { w: "majority", j: true } })
const t2 = Date.now()
({ w1_ms: t1 - t0, majority_j_ms: t2 - t1, keduanyaOk: r1.ok === 1 && r2.ok === 1 })Output yang Dihasilkan:
{ w1_ms: 24, majority_j_ms: 36, keduanyaOk: true }Di rs0 kedua varian berada di rentang puluhan milidetik — mayoritas satu node adalah node itu sendiri, dan journal lokal memang sudah menjadi bagian jalur tulis WiredTiger. Angka akan berubah bentuk di cluster nyata:
| Write concern | Yang ditunggu | Durabilitas | Latensi |
|---|---|---|---|
{ w: 1 } | Primary menerima (memori) | Hilang bila primary mati sebelum replikasi | Terendah |
{ w: 1, j: true } | Journal primary di disk | Tahan restart primary, tidak tahan kehilangan mesin | Rendah |
{ w: "majority" } | Mayoritas anggota menerima | Tahan kehilangan satu node | Sedang |
{ w: "majority", j: true } | Journal mayoritas anggota | Tahan kehilangan node dan restart | Tertinggi |
w: "majority" menunggu n+1 dari 2n+1 anggota — pada cluster tiga anggota berarti dua node — sehingga latensinya ditentukan oleh anggota kedua yang paling lambat. Inilah harga yang dibayar untuk jaminan: write yang sudah diakui mayoritas tidak mungkin hilang oleh satu kegagalan node mana pun.
Write yang Tak Diakui: w Melebihi Anggota #
Apa yang terjadi bila write concern meminta pengakuan lebih banyak daripada jumlah anggota? Kita minta w: 2 pada replica set satu anggota:
try {
db.runCommand({
insert: "pesanan",
documents: [{ kode: "PSN-004", pelanggan: "Eka", buku: "Filosofi Teras", jumlah: 2 }],
writeConcern: { w: 2, wtimeout: 2000 }
})
} catch (e) {
print("Galat " + e.code + ": " + e.message)
}
db.pesanan.countDocuments({ kode: "PSN-004" })Output yang Dihasilkan:
Galat 100: Not enough data-bearing nodes
1Dua baris ini membuka pelajaran penting. Server menolak pengakuan w: 2 karena hanya ada satu anggota penyimpan data (galat 100), dan panggilan di atas menangkapnya lewat try...catch. Namun countDocuments({ kode: "PSN-004" }) tetap mengembalikan 1 — document PSN-004 benar-benar tersimpan. Pengakuan dan penerapan adalah dua hal terpisah: primary menerapkan write lebih dulu, lalu menunggu pengakuan; kegagalan pengakuan tidak otomatis menggulung write.
Perilaku yang sama berlaku pada wtimeout: bila batas waktu tercapai sebelum w terpenuhi, respons membawa writeConcernError bertipe wtimeout — sementara write-nya sendiri mungkin sudah (atau belum) diterapkan di anggota yang lambat. Aplikasi yang menerima galat semacam ini harus memeriksa keadaan data sebelum mencoba ulang, karena operasi ulang butuh desain idempoten (misalnya unique key pada kode pesanan).
Default dan Praktik Aman #
Seperti dibahas di Chapter Konfigurasi Replication, write concern default efektif cluster modern adalah w: "majority" sumber implisit — bukan w: 1 yang tersirat dari field lawas getLastErrorDefaults. Aplikasi dapat menimpanya per operasi seperti contoh-contoh di atas, atau menetapkan default sendiri lewat setDefaultRWConcern.
Tiga situasi yang hampir selalu layak memakai w: "majority":
- Data kritis: pesanan, pembayaran, dan stok — data yang kehilangan satu baris pun berarti insiden.
- Sebelum kejadian berisiko: menjelang maintenance, failover terencana, atau pemindahan anggota — write majority tidak bisa hilang pada transisi kepemimpinan.
- Transaksi multi-document: commit transaksi memakai write concern majority secara bawaan di banyak driver.
Rangkuman #
- Write concern diatur tiga field:
w(jumlah pengakuan),j(journal di disk), danwtimeout(batas menunggu). - Respons acknowledged memuat
n,opTime, danok; kegagalan pengakuan muncul sebagaiwriteConcernError— write yang gagal diakui bisa saja sudah diterapkan. - Pada
rs0,w: "majority"sama cepat denganw: 1karena primary tunggal adalah mayoritasnya; di cluster nyata majority menunggu anggota kedua terlama. - Data kritis, transisi maintenance, dan commit transaksi sebaiknya selalu
w: "majority"; default implisit cluster modern memang sudah demikian.
Write sudah dijamin; bagaimana dengan jaminan pada sisi pembaca? Chapter Read Concern melengkapi pasangan ini.