Konsistensi kausal (causal consistency) menjamin bahwa operasi yang berkaitan sebab-akibat dibaca dalam urutan yang masuk akal, misalnya pembacaan selalu melihat hasil tulisan yang mendahuluinya. MongoDB menegakkan jaminan ini melalui sesi (session) dengan konsistensi kausal yang diaktifkan pada saat dibuat. Topik ini membahas makna hubungan kausal antar operasi, cara membuka sesi kausal, empat jaminan yang menyertainya, serta kesalahan yang membuat jaminan tersebut hilang tanpa pesan error. Data contohnya berupa koleksi rekening pada basis data kas.
Hubungan kausal antar operasi#
Dua operasi berhubungan kausal apabila operasi kedua dipengaruhi oleh hasil operasi pertama. Membaca saldo lalu menulis mutasi berdasarkan saldo tersebut adalah satu pasangan kausal, demikian pula menulis mutasi lalu menampilkan riwayat mutasi kepada aplikasi lain. Tanpa jaminan kausal, pembacaan setelah sebuah tulisan dapat mengembalikan keadaan lama. Kejadian ini tidak mungkin terjadi pada primary yang selalu melayani tulisan lebih dahulu, tetapi dapat terjadi pada replica set yang pembacaannya dilayani anggota kedua. Penyebabnya adalah replikasi yang berlangsung tidak serentak, sehingga anggota tersebut dapat masih tertinggal.
MongoDB menegakkan hubungan kausal melalui client session. Untuk setiap operasi yang diakui, server mengembalikan stempel waktu berupa operation time dan cluster time, lalu sesi mencatatnya. Operasi berikutnya dalam sesi yang sama dirantai ke stempel waktu tersebut, sehingga server mengetahui bahwa operasi itu harus ditempatkan setelah seluruh operasi sebelumnya pada rantai yang sama. Rantai inilah yang membuat urutan sebab-akibat tetap terjaga bahkan ketika pembacaan berpindah dari satu anggota replica set ke anggota lainnya.
Tanam data contoh berikut sebelum mencoba sesi kausal.
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: [] }
]){
acknowledged: true,
insertedIds: {
'0': ObjectId('6ae89b2ee1cf8fd37b8de666'),
'1': ObjectId('6ae89b2ee1cf8fd37b8de667')
}
}Sesi dengan konsistensi kausal#
Sesi kausal dibuat dengan memanggil db.getMongo().startSession() bersama opsi causalConsistency: true. Operasi tulisan dan pembacaan kemudian ditulis melalui objek database yang diambil dari sesi tersebut dengan getDatabase(), sama seperti pola transaksi multidokumen, tetapi tanpa startTransaction(). Contoh berikut menjalankan satu pasangan kausal, yaitu transfer keluar dari R-001 lalu membaca kembali dokumen yang sama dalam sesi yang belum ditutup.
const sesi = db.getMongo().startSession({ causalConsistency: true })
const kas = sesi.getDatabase("kas")
kas.rekening.updateOne(
{ nomor: "R-001" },
{ $inc: { saldo: -25000 }, $push: { mutasi: { jenis: "transfer keluar", jumlah: -25000, tujuan: "R-002" } } },
{ writeConcern: { w: "majority" } }
)
kas.rekening.find({ nomor: "R-001" }, { _id: 0, nomor: 1, saldo: 1, mutasi: 1 }).readConcern("majority").toArray()
sesi.endSession(){
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
[
{
nomor: 'R-001',
saldo: 225000,
mutasi: [ { jenis: 'transfer keluar', jumlah: -25000, tujuan: 'R-002' } ]
}
]Pembacaan tersebut mengembalikan saldo 225000 beserta mutasi yang baru saja ditulis, padahal keduanya dijalankan sebagai dua perintah yang terpisah. Jaminan inilah yang disebut read-your-own-writes, yaitu pola paling dasar dari konsistensi kausal. Tuliskan panggilan endSession() setelah pekerjaan selesai agar sesi tidak menumpuk di server.
Opsi causalConsistency: true mengatur perilaku sesinya saja, sedangkan tingkat concern, yakni write concern dan read concern, tetap ditetapkan secara eksplisit pada setiap operasi dengan nilai "majority", sebagaimana tampak pada contoh di atas. Sesi tanpa opsi tersebut tetap dapat dibuat, tetapi server tidak merantai operasinya, sehingga pembacaan berikutnya dapat berpindah ke keadaan lama ketika dilayani anggota replica set yang berbeda.
Empat jaminan konsistensi kausal#
Sesi kausal yang seluruh operasinya memakai tingkat majority menjanjikan empat hal sekaligus. Jaminan-jaminan tersebut berlaku untuk seluruh anggota replica set, termasuk ketika tulisan dilayani primary dan pembacaan berikutnya dilayani anggota kedua.
| Jaminan | Makna |
|---|---|
| Read your own writes | Pembacaan mencerminkan tulisan yang mendahuluinya dalam sesi yang sama |
| Monotonic reads | Pembacaan tidak kembali ke keadaan yang lebih lama daripada pembacaan sebelumnya |
| Monotonic writes | Tulisan yang mendahului tulisan lain dieksekusi lebih dahulu |
| Writes follow reads | Tulisan mencerminkan keadaan data setelah pembacaan yang mendahuluinya |
Pola baca-tulis-baca dalam satu sesi menunjukkan jaminan tersebut dalam praktik. Contoh berikut membaca saldo R-001, menambahkan setoran 100000 berdasarkan pembacaan itu, lalu membaca kembali saldo yang sama sebelum sesi ditutup.
const sesi = db.getMongo().startSession({ causalConsistency: true })
const kas = sesi.getDatabase("kas")
kas.rekening.find({ nomor: "R-001" }, { _id: 0, saldo: 1 }).readConcern("majority").toArray()
kas.rekening.updateOne({ nomor: "R-001" }, { $inc: { saldo: 100000 } }, { writeConcern: { w: "majority" } })
kas.rekening.find({ nomor: "R-001" }, { _id: 0, saldo: 1 }).readConcern("majority").toArray()
sesi.endSession()[ { saldo: 225000 } ]
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
[ { saldo: 325000 } ]Pembacaan pertama mengembalikan 225000, tulisan menambahkan 100000 di atas nilai tersebut, dan pembacaan kedua mengembalikan 325000 tanpa ada langkah yang tertukar urutannya. Jika pembacaan kedua dijalankan pada sesi yang berbeda tanpa rantai kausal, replica set dengan anggota yang tertinggal dapat mengembalikan keadaan yang lebih lama, dan perhitungan berbasis saldo menjadi tidak andal. Sesi kausal mencegah kemungkinan itu secara struktural.
Kesalahan umum#
Kesalahan pertama adalah tetap menggunakan objek db biasa padahal sesi kausal sudah dibuka. Operasi yang ditulis melalui db.rekening tidak ikut dirantai ke stempel waktu sesi. Akibatnya, pembacaan dan tulisan berikutnya tidak memperoleh jaminan apa pun, dan rantai kausal pada sesi menjadi tidak lengkap. Perbaikannya adalah mengambil objek database dari sesi.getDatabase("kas") sejak awal dan memakai objek itu untuk seluruh operasi dalam sesi tersebut.
Kesalahan kedua adalah mencampur tingkat concern di luar majority. Sesuai batasan pada bagian sebelumnya, hubungan kausal hanya dijamin untuk pembacaan "majority" dan tulisan "majority". Satu pembacaan dengan "local" di tengah rantai sudah cukup untuk mengembalikan keadaan lama tanpa pesan error, sehingga kesalahannya sulit ditemukan. Perbaikannya adalah menetapkan kedua tingkat tersebut secara eksplisit pada setiap operasi kausal dan tidak mengandalkan nilai bawaan.
Kesalahan ketiga adalah memakai satu sesi untuk beberapa utas aplikasi sekaligus. Server hanya menjamin urutan kausal apabila operasi dalam satu sesi dijalankan satu utas pada satu waktu. Sebabnya, stempel waktu sesi bergerak maju bersama setiap operasi dan tidak dapat dipertanggungjawabkan bila dua utas menulis bersamaan. Perbaikannya adalah memberi satu utas satu sesi tersendiri, lalu merantai stempel waktu antar sesi bila hubungan kausal lintas utas memang diperlukan.
Ringkasan#
- Konsistensi kausal menjamin urutan sebab-akibat antar operasi, dan ditegakkan melalui sesi dengan opsi
causalConsistency: true. - Sesi mencatat operation time dan cluster time dari setiap operasi, lalu merantai operasi berikutnya ke stempel waktu tersebut.
- Jaminan read-your-own-writes, monotonic reads, monotonic writes, dan writes follow reads hanya berlaku untuk pembacaan dan tulisan dengan tingkat majority.
- Objek database harus diambil dari sesi, dan satu sesi hanya boleh dipakai satu utas pada satu waktu.
Referensi#
- Read Isolation, Consistency, and Recency — MongoDB Manual — Menjelaskan client session, jaminan kausal, dan stempel waktu operasinya.
- Causal Consistency and Read and Write Concerns — MongoDB Manual — Menjelaskan syarat majority pada read concern dan write concern untuk jaminan kausal.
- Mongo.startSession — MongoDB Manual
— Dokumentasi pembuatan sesi, termasuk opsi
causalConsistency. - Write Concern — MongoDB Manual — Dokumentasi tingkat pengakuan tulisan yang menjadi syarat jaminan kausal.