Write concern mengatur seberapa jauh server menunggu sebelum mengakui sebuah tulisan, sedangkan read concern mengatur tingkat kemutakhiran data yang boleh dikembalikan sebuah pembacaan. Keduanya adalah pengatur keandalan pada replica set, dan pilihannya menentukan keseimbangan antara durabilitas, konsistensi, dan latensi. Topik ini membahas arti setiap tingkat concern, cara menetapkannya pada operasi, keluaran yang berbeda ketika tulisan tidak diakui, serta panduan memilih kombinasi untuk data yang sensitif. Data contohnya berupa koleksi rekening pada basis data kas.
Write concern mengatur pengakuan tulisan#
Write concern ditulis sebagai opsi writeConcern pada operasi tulisan dan memiliki tiga pengatur utama. Opsi w menyatakan berapa banyak anggota replica set yang harus mengakui tulisan, yaitu w: 1 untuk primary saja, anggota yang melayani seluruh tulisan, atau w: "majority" untuk mayoritas anggota yang memiliki hak suara. Opsi j: true menambahkan syarat bahwa tulisan sudah masuk ke journal, yaitu berkas log di disk yang menjamin data bertahan ketika server mati mendadak. Opsi wtimeout memberi batas waktu tunggu dalam milidetik agar aplikasi tidak menggantung tanpa batas ketika replikasi tersendat.
Contoh berikut menanam data dengan tingkat pengakuan paling kuat, lalu memperbarui saldo dengan tingkat pengakuan paling ringan. Kedua operasi sama-sama berhasil, dan perbedaannya baru terasa ketika ada anggota yang bermasalah.
use kasswitched to db kasdb.rekening.insertMany(
[
{ nomor: "R-001", nama: "Ali Rahman", saldo: 250000 },
{ nomor: "R-002", nama: "Budi Santoso", saldo: 175000 }
],
{ writeConcern: { w: "majority", j: true, wtimeout: 5000 } }
){
acknowledged: true,
insertedIds: {
'0': ObjectId('6ae89a9044b8c654f58de666'),
'1': ObjectId('6ae89a9044b8c654f58de667')
}
}db.rekening.updateOne({ nomor: "R-001" }, { $inc: { saldo: -25000 } }, { writeConcern: { w: 1 } }){
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}Tingkat pengakuan w: 0 menghasilkan perilaku yang berbeda secara kasat mata. Tulisan dengan opsi ini tidak menunggu pengakuan apa pun karena aplikasi tidak pernah menunggu jawaban server. Hasil operasinya melaporkan acknowledged: false dan tidak memuat jumlah dokumen yang cocok. Jalankan blok berikut, lalu perhatikan bahwa server tetap memproses perintahnya meskipun aplikasi tidak diberi kepastian apa pun tentang hasil tulisan tersebut.
db.rekening.updateOne({ nomor: "R-002" }, { $inc: { saldo: 1000 } }, { writeConcern: { w: 0 } })
db.rekening.countDocuments(){
acknowledged: false,
insertedId: null,
matchedCount: undefined,
modifiedCount: undefined,
upsertedCount: 0
}
2Read concern mengatur kemutakhiran pembacaan#
Read concern ditetapkan per pembacaan dan menentukan dari mana data boleh diambil. Tingkat "local" mengembalikan data terbaru pada node yang dituju, termasuk data yang belum tersebar ke mayoritas dan berisiko dibatalkan saat failover. Tingkat "majority" hanya mengembalikan data yang sudah diakui mayoritas anggota, sehingga tahan rollback, yaitu pembatalan tulisan yang belum menyebar ke mayoritas anggota. Tingkat "linearizable" memberikan jaminan terkuat, yaitu pembacaan mencerminkan seluruh tulisan yang diakui sebelum pembacaan dimulai, tetapi paling mahal dan hanya mendukung query pada field unik. Tingkat "snapshot" membaca keadaan data pada satu titik waktu tertentu dan dipakai di dalam transaksi.
Metode readConcern() pada kursor menetapkan tingkatnya untuk satu pembacaan. Tingkat majority hanya melihat data yang sudah diakui mayoritas. Karena itu, tulisan yang mendahului pembacaan kritis perlu ditulis dengan write concern majority agar nilai yang dibaca pasti nilai terakhir. Contoh berikut mencatat transfer keluar kedua dari R-001 dengan write concern majority, lalu membaca saldo R-001 dengan tingkat majority.
db.rekening.updateOne({ nomor: "R-001" }, { $inc: { saldo: -25000 } }, { writeConcern: { w: "majority", j: true } })
db.rekening.find({ nomor: "R-001" }, { _id: 0, saldo: 1 }).readConcern("majority").toArray(){
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
[ { saldo: 200000 } ]Bandingkan dengan pembacaan linearizable, yang wajib menunjuk dokumen melalui field unik seperti _id.
const idR001 = db.rekening.findOne({ nomor: "R-001" }, { _id: 1 })._id
db.rekening.find({ _id: idR001 }, { _id: 0, saldo: 1 }).readConcern("linearizable").toArray()[ { saldo: 200000 } ]Kedua pembacaan mengembalikan saldo 200000, tetapi jalannya berbeda. Pembacaan majority mengambil keadaan yang sudah diakui mayoritas, sehingga tulisan yang baru diakui dengan write concern majority pasti terlihat. Tulisan yang hanya diakui w: 1 belum tentu terlihat karena commit point mayoritas dapat belum melewatinya. Pembacaan linearizable mencerminkan tulisan terakhir yang diakui server tanpa menunggu commit point, dan jaminan itu dibayar dengan koordinasi tambahan pada setiap query. Nilai default kedua concern dapat dibaca dari server melalui perintah getDefaultRWConcern. Server latihan ini memakai default write concern implisit bernilai majority dan default read concern implisit bernilai local, sedangkan server lain dapat berbeda menurut konfigurasinya.
db.adminCommand({ getDefaultRWConcern: 1 }).defaultWriteConcern
db.adminCommand({ getDefaultRWConcern: 1 }).defaultReadConcern{ w: 'majority', wtimeout: 0 }
{ level: 'local' }Menyeimbangkan keandalan dan latensi#
Semakin kuat jaminan yang diminta, semakin lama server menunggu sebelum menjawab. Write concern majority menunggu replikasi ke lebih dari setengah anggota, j: true menunggu journal selesai ditulis ke disk, sedangkan w: 1 hanya menunggu primary sendiri. Pada sisi pembacaan, local paling cepat dan linearizable paling lambat. Tabel berikut merangkum jaminan dan biaya setiap kombinasi yang lazim.
| Kombinasi | Jaminan | Biaya |
|---|---|---|
w: 1 dengan read concern local | Tulisan tercatat di primary, pembacaan paling cepat | Tulisan dapat hilang saat rollback, pembacaan bisa melihat data yang dibatalkan |
w: "majority" dengan read concern local | Tulisan tahan rollback, pembacaan tetap cepat | Tulisan menunggu replikasi |
w: "majority" dan j: true dengan read concern "majority" | Tulisan tahan mati mendadak, pembacaan tahan rollback | Menunggu journal dan replikasi pada setiap operasi |
Read concern "linearizable" | Pembacaan mencerminkan tulisan terakhir yang diakui | Paling lambat, hanya untuk query field unik |
Untuk data uang seperti saldo rekening, kombinasi w: "majority" dengan j: true untuk tulisan dan "majority" untuk pembacaan menjadi pilihan yang seimbang. Untuk data yang boleh sedikit tertinggal, misalnya penghitung tampilan atau daftar yang sering dibaca, kombinasi w: 1 dengan local menghemat waktu tanpa kerugian yang berarti. Pemilihan concern adalah keputusan per operasi, bukan keputusan sekali seumur aplikasi, sehingga operasi kritis dan operasi ringan dapat hidup berdampingan pada satu koleksi yang sama.
Perbedaan antar kombinasi tidak terlihat pada server tunggal yang sehat seperti lingkungan latihan ini, karena tidak ada anggota kedua yang dapat tertinggal. Perbedaannya muncul saat failover, yaitu ketika primary berganti dan tulisan yang hanya ada di primary lama harus dibatalkan. Kombinasi yang kuat menjamin keadaan yang dibaca aplikasi selalu keadaan yang sudah disepakati mayoritas, bukan keadaan sementara yang akan berubah.
Kesalahan umum#
Kesalahan pertama adalah menganggap acknowledged: true pada w: 1 berarti tulisan sudah pasti bertahan. Nilai itu hanya berarti primary telah menerima tulisan, sedangkan anggota lain belum mengetahuinya. Apabila primary tidak tersedia sebelum tulisan tersebar, tulisan tersebut dibatalkan saat failover dan aplikasi tidak pernah menerima kabar pembatalannya. Perbaikannya adalah memakai w: "majority" untuk data yang tidak boleh hilang dan menyimpan w: 1 untuk tulisan yang bersifat sekunder.
Kesalahan kedua adalah memakai read concern "linearizable" untuk semua pembacaan. Jaminan tersebut dibayar dengan menunggu koordinasi pada setiap query, dan pembacaan linearizable hanya boleh menyaring field unik seperti _id. Pembacaan daftar yang panjang dengan tingkat ini justru membebani server tanpa manfaat yang terasa. Perbaikannya adalah membatasi linearizable untuk pembacaan tunggal yang kritis, misalnya memeriksa saldo sebelum eksekusi, dan memakai "majority" untuk sisanya.
Kesalahan ketiga adalah menafsirkan WriteConcernError dari batas wtimeout sebagai tanda tulisan gagal. Ketika batas waktu tercapai, server mengembalikan error yang menjelaskan bahwa pengakuan belum terkumpul, tetapi tulisannya sendiri sudah terjadi di primary dan tetap berpotensi tersebar kemudian. Aplikasi yang langsung mengulang tulisan berisiko melakukan penggandaan. Perbaikannya adalah memverifikasi keadaan data terlebih dahulu, misalnya membaca kembali dokumen yang dituju, sebelum memutuskan mengulang operasi.
Ringkasan#
- Write concern mengatur pengakuan tulisan melalui
w,j, danwtimeout, denganw: "majority"danj: truesebagai kombinasi paling tahan gagal. - Read concern mengatur kemutakhiran pembacaan dari
"local","majority","linearizable", sampai"snapshot". - Tulisan dengan
w: 0mengembalikanacknowledged: falsetetapi tetap diproses, sehingga tidak layak untuk data penting. - Default kedua concern dapat dibaca dengan
getDefaultRWConcern, dan pilihan concern sebaiknya disesuaikan per operasi antara data kritis dan data ringan.
Referensi#
- Write Concern — MongoDB Manual
— Dokumentasi opsi
w,j, danwtimeoutbeserta perilaku pengakuannya. - Read Concern — MongoDB Manual
— Dokumentasi tingkat
local,available,majority,linearizable, dansnapshot. - Read Isolation, Consistency, and Recency — MongoDB Manual — Menjelaskan jaminan isolasi dan konsistensi di balik setiap tingkat read concern.
- getDefaultRWConcern — MongoDB Manual — Perintah untuk membaca default write concern dan read concern pada server.
- cursor.readConcern — MongoDB Manual — Dokumentasi metode penentuan read concern pada kursor.