MongoDB menjamin atomicitas pada tingkat satu dokumen, yaitu setiap operasi tulisan yang menyentuh satu dokumen berlaku menyeluruh atau tidak berlaku sama sekali. Topik ini membahas cakupan jaminan tersebut, pola pembaruan banyak field dalam satu operasi, perilaku ketika operasi ditolak server, serta kesalahan yang muncul apabila satu kesatuan perubahan dipecah menjadi beberapa operasi terpisah. Data contohnya berupa koleksi rekening pada basis data kas, dengan tiap dokumen memuat saldo dan riwayat mutasi sekaligus.
Jaminan atomicitas satu dokumen#
Setiap operasi tulisan yang menyasar satu dokumen bersifat atomik menurut dokumentasi MongoDB. Maknanya konkret: apabila satu operasi memperbarui banyak field sekaligus, operasi pembacaan tidak pernah melihat dokumen dengan sebagian field sudah berubah dan sebagian lainnya belum berubah. Jaminan yang sama berlaku untuk insertOne, updateOne, replaceOne, dan deleteOne, termasuk pada dokumen yang bersarang sedalam apa pun. Atomicitas ini aktif bawaan tanpa konfigurasi apa pun, sehingga tidak ada langkah tambahan yang perlu dilakukan untuk mengaktifkannya.
Jaminan satu dokumen tidak menyebar ke beberapa dokumen sekaligus. Operasi updateMany yang memodifikasi banyak dokumen bersifat atomik per dokumen, tetapi operasinya secara keseluruhan tidak atomik, sehingga operasi lain dapat menyela di antara dua dokumen yang diperbarui. Implikasinya terhadap desain schema cukup besar. Data yang selalu berubah bersama sebaiknya ditempatkan dalam satu dokumen yang sama melalui embedding, karena dengan begitu jaminan atomicitas satu dokumen sudah mencakup seluruh data tersebut. Koleksi contoh pada topik ini mengikuti pola tersebut, yaitu saldo dan array mutasi disimpan dalam dokumen rekening yang sama.
Jalankan mongosh, pilih basis data kerja, lalu isi koleksi rekening dengan tiga dokumen berikut.
use kasswitched to db kasdb.rekening.insertMany([
{ nomor: "R-001", nama: "Ali Rahman", saldo: 250000, mutasi: [] },
{ nomor: "R-002", nama: "Budi Santoso", saldo: 175000, mutasi: [] },
{ nomor: "R-003", nama: "Citra Melati", saldo: 320000, mutasi: [] }
]){
acknowledged: true,
insertedIds: {
'0': ObjectId('6ae899ca006179d1688de666'),
'1': ObjectId('6ae899ca006179d1688de667'),
'2': ObjectId('6ae899ca006179d1688de668')
}
}Pembaruan multifield dalam satu operasi#
Operator update mengubah banyak field dalam satu dokumen sekaligus, dan jaminan atomicitas membuat kombinasi tersebut aman tanpa langkah pengamanan tambahan. Contoh berikut mencatat transfer keluar dari rekening R-001, yaitu saldo berkurang melalui $inc, array mutasi bertambah melalui $push, dan field diperbarui berganti melalui $set. Ketiganya ditulis dalam satu dokumen update yang dieksekusi oleh satu perintah updateOne.
db.rekening.updateOne(
{ nomor: "R-001" },
{
$inc: { saldo: -25000 },
$push: { mutasi: { jenis: "transfer keluar", jumlah: -25000, tujuan: "R-002", waktu: new Date("2026-11-02T09:00:00Z") } },
$set: { diperbarui: new Date("2026-11-02T09:00:00Z") }
}
){
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}Periksa keadaan dokumen setelah pembaruan dengan findOne.
db.rekening.findOne({ nomor: "R-001" }, { _id: 0 }){
nomor: 'R-001',
nama: 'Ali Rahman',
saldo: 225000,
mutasi: [
{
jenis: 'transfer keluar',
jumlah: -25000,
tujuan: 'R-002',
waktu: ISODate('2026-11-02T09:00:00.000Z')
}
],
diperbarui: ISODate('2026-11-02T09:00:00.000Z')
}Keluarannya memperlihatkan ketiga perubahan yang sudah terjadi bersamaan, yaitu saldo menjadi 225000, satu entri mutasi bertambah, dan field diperbarui terisi. Pembaca lain tidak mungkin menjumpai keadaan antara seperti saldo yang sudah berkurang tetapi mutasi belum bertambah, sebab perubahan diterapkan pada dokumen sebagai satu kesatuan. Bila ketiga perubahan itu dipecah menjadi tiga perintah updateOne terpisah, setiap perintah tetap atomik secara sendiri-sendiri, tetapi keadaan antara pada sela dua perintah dapat dibaca oleh operasi lain. Satu operasi multifield selalu lebih aman daripada beberapa operasi terpisah untuk data yang berubah bersama.
Operasi yang ditolak tidak meninggalkan perubahan#
Atomicitas juga berlaku ketika server menolak sebuah operasi. Dokumen update yang memuat satu kesalahan apa pun ditolak menyeluruh, dan tidak satu field pun ikut berubah. Contoh berikut sengaja mencampur operator $set yang valid dengan operator fiktif $bogus untuk membuktikan perilaku tersebut.
db.rekening.updateOne(
{ nomor: "R-001" },
{ $set: { status: "beku" }, $bogus: { nilai: 1 } }
)Uncaught
MongoServerError: Unknown modifier: $bogus. Expected a valid update modifier or pipeline-style update specified as an arrayServer menolak seluruh dokumen update karena $bogus bukan operator yang dikenal. Periksa kembali keadaan dokumen R-001 untuk memastikan tidak ada perubahan yang tersisa dari operasi yang gagal itu.
db.rekening.findOne({ nomor: "R-001" }, { _id: 0, saldo: 1, status: 1 }){ saldo: 225000 }Keluaran hanya memuat saldo karena field status tidak pernah terbentuk, sehingga bagian $set memang tidak dieksekusi sama sekali. Perilaku all-or-nothing seperti ini membuat aplikasi tidak pernah menemukan dokumen setengah terubah akibat satu operasi yang gagal. Perbaikannya adalah membetulkan dokumen update yang ditolak, misalnya menghapus operator yang keliru, lalu menjalankan ulang operasinya.
Penolakan menyeluruh ini berlaku untuk penyebab lain, misalnya pembaruan yang menghasilkan dokumen yang ditolak validator koleksi. Server memeriksa keseluruhan dokumen update terlebih dahulu sebelum menerapkannya, sehingga sebagian field tidak sempat ikut berubah pada operasi yang ditolak. Pesan error yang dikembalikan menyebut penyebab penolakannya, yaitu nama operator yang tidak dikenal pada contoh di atas, sehingga perbaikan dapat ditentukan tanpa pemeriksaan tambahan.
Kesalahan umum#
Kesalahan yang paling umum adalah menganggap beberapa perintah terpisah sebagai satu kesatuan atomik. Contoh berikut menanam satu dokumen rekening uji yang field saldo-nya bertipe string, lalu menjalankan dua perintah yang seharusnya berpasangan, yaitu mengurangi saldo R-003 dan menambah saldo rekening uji tersebut.
db.rekening.insertOne({ nomor: "R-009", nama: "Rekening Uji", saldo: "belum aktif" }){
acknowledged: true,
insertedId: ObjectId('6ae899ca006179d1688de669')
}db.rekening.updateOne({ nomor: "R-003" }, { $inc: { saldo: -50000 } }){
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}db.rekening.updateOne({ nomor: "R-009" }, { $inc: { saldo: 50000 } })Uncaught
MongoServerError: Plan executor error during update :: caused by :: Cannot apply $inc to a value of non-numeric type. {_id: ObjectId('6ae899ca006179d1688de669')} has the field 'saldo' of non-numeric type stringPerintah pertama berhasil tersimpan, sedangkan perintah kedua ditolak server karena $inc tidak dapat diterapkan pada nilai bertipe string. Bila kedua perintah itu adalah satu proses transfer, keadaan datanya kini tidak konsisten, yaitu R-003 sudah berkurang tetapi rekening uji tidak pernah bertambah. Perbaikannya adalah menghapus dokumen uji dan mengembalikan saldo R-003, kemudian menyusun ulang proses agar satu kesatuan perubahan dieksekusi sebagai satu operasi atau sebagai transaksi multidokumen.
db.rekening.deleteOne({ nomor: "R-009" })
db.rekening.updateOne({ nomor: "R-003" }, { $inc: { saldo: 50000 } }){ acknowledged: true, deletedCount: 1 }
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}db.rekening.findOne({ nomor: "R-003" }, { _id: 0, saldo: 1 }){ saldo: 320000 }Kesalahan kedua terjadi di lapisan aplikasi, yaitu pola baca-ubah-tulis yang dipecah menjadi findOne lalu updateOne dengan nilai yang sudah dihitung di aplikasi. Dua operasi itu bukan satu kesatuan atomik, sehingga dua proses yang berjalan bersamaan dapat saling menimpa hasil dan menyebabkan lost update, yaitu pembaruan yang hilang karena ditulis ulang dengan nilai lama. Solusinya adalah memindahkan perhitungan ke server melalui operator seperti $inc, sehingga setiap proses menambahkan perubahannya di atas nilai terbaru yang ada di server. Alternatifnya adalah menggunakan findOneAndUpdate yang menggabungkan baca dan tulis dalam satu operasi.
Ringkasan#
- Setiap operasi tulisan pada satu dokumen bersifat atomik secara bawaan, termasuk pembaruan banyak field sekaligus.
updateManybersifat atomik per dokumen, tetapi tidak atomik sebagai satu operasi utuh.- Dokumen update yang ditolak server tidak meninggalkan perubahan apa pun pada dokumen target.
- Perubahan yang berpasangan tetapi dipecah menjadi beberapa operasi terpisah berisiko meninggalkan keadaan tidak konsisten, sehingga sebaiknya digabung dalam satu operasi atau dalam transaksi multidokumen.
Referensi#
- Read Isolation, Consistency, and Recency — MongoDB Manual — Menjelaskan atomicitas satu dokumen dan batasannya pada operasi banyak dokumen.
- db.collection.updateOne — MongoDB Manual — Dokumentasi metode pembaruan satu dokumen beserta opsi-opsinya.
- Update Operators — MongoDB Manual
— Daftar operator update seperti
$inc,$set, dan$push. - db.collection.findOneAndUpdate — MongoDB Manual
— Dokumentasi metode baca-tulis atomik dengan opsi
returnDocument.