Menulis operasi yang mengubah beberapa dokumen lintas collection secara atomik dengan sessions dan transaksi multi-document, memahami kapan transaksi dibutuhkan, cara commit dan abort, serta batasan durasi dan best practices penggunaan yang bijak.

Selama bertahun-tahun, kritik terbesar terhadap MongoDB adalah "tidak mendukung transaksi". Satu operasi memang atomik, tapi operasi multi-dokumen tidak bisa digabung menjadi satu kesatuan yang berhasil atau gagal total. Semua berubah di MongoDB 4.0: multi-document ACID transactions resmi hadir, menyamai kemampuan transaksi database relational.
Episode 14 membahas fitur ini secara mendalam. Roadmap-nya: pertama kita pahami kapan transaksi benar-benar dibutuhkan, kedua kita tulis transaksi multi-document dengan sessions, ketiga kita pelajari cara commit dan abort, dan keempat kita bedah batasan serta best practices. Mari mulai.
Transaksi menjamin ACID: Atomicity (berhasil semua atau tidak sama sekali), Consistency (data valid setelah transaksi), Isolation (transaksi tidak saling mengganggu), dan Durability (perubahan tersimpan permanen). Di MongoDB, satu operasi pada satu dokumen selalu atomik. Yang membutuhkan transaksi adalah operasi yang harus mengubah beberapa dokumen lintas collection secara bersamaan.
Contoh paling klasik adalah transfer saldo antar akun. Prosesnya butuh dua langkah yang tidak bisa dipisahkan: kurangi saldo pengirim, tambahkan saldo penerima. Jika hanya pengurangan yang berhasil, uang hilang; jika hanya penambahan yang berhasil, uang diciptakan dari udara:
{ "_id": ObjectId("aaaa"), "name": "Siti", "balance": 500000 }
{ "_id": ObjectId("bbbb"), "name": "Budi", "balance": 250000 }Tanpa transaksi, kegagalan di tengah jalan akan meninggalkan data korup. Dengan transaksi, kedua update berjalan sebagai satu unit — jika salah satu gagal, keduanya dibatalkan.
Skenario lain yang lazim memakai transaksi: membuat order sambil mengurangi stok produk (kita akan bangun ini di episode 20), memindahkan dokumen antar collection, atau mengubah beberapa dokumen terkait yang harus konsisten.
Transaksi MongoDB berjalan di dalam session. Semua operasi dalam transaksi harus meneruskan object session yang sama — inilah yang mengikat operasi-operasi itu menjadi satu transaksi:
const session = client.startSession();
session.startTransaction();
try {
await accounts.updateOne(
{ _id: fromAccountId },
{ $inc: { balance: -amount } },
{ session }
);
await accounts.updateOne(
{ _id: toAccountId },
{ $inc: { balance: amount } },
{ session }
);
await session.commitTransaction();
} catch (err) {
await session.abortTransaction();
} finally {
await session.endSession();
}Perhatikan alurnya:
client.startSession() — membuat session.session.startTransaction() — memulai transaksi.updateOne menerima { session } — operasi terikat pada transaksi yang sama.session.commitTransaction() — jika semua sukses, simpan permanen.session.abortTransaction() — jika ada error, batalkan semua perubahan.session.endSession() — bersihkan sumber daya session.Kunci keamanannya: transaksi menjamin kedua update commit bersama atau tidak sama sekali. Tidak ada kondisi setengah jalan.
Transaksi juga bisa diuji langsung di mongosh. Ini cara paling cepat mempraktikkan konsep sebelum menulis kode aplikasi:
const session = db.getMongo().startSession();
session.startTransaction();
const accounts = session.getDatabase("bank").getCollection("accounts");
accounts.updateOne({ _id: ObjectId("aaaa") }, { $inc: { balance: -100000 } });
accounts.updateOne({ _id: ObjectId("bbbb") }, { $inc: { balance: 100000 } });
session.commitTransaction();
session.endSession();Di mongosh, kalian bisa menguji skenario kegagalan: jalankan update yang salah lalu session.abortTransaction() — dan periksa bahwa saldo tidak berubah sama sekali. Inilah bukti paling jelas kekuatan transaksi.
transactionLifetimeLimitSeconds). Transaksi yang lebih lama dari itu akan di-abort otomatis oleh server. Jangan menaruh pekerjaan berat atau panggilan eksternal di dalam transaksi.maxTransactionLockRequestTimeoutMillis dan ukuran oplog. Transaksi raksasa bisa melampaui kapasitas.system.profile di dalam transaksi.Transaksi adalah alat yang ampuh, tapi bukan pengganti desain schema yang baik:
Simpan transaksi tetap pendek. Semua operasi database yang berat — membaca data besar, menghitung — lakukan sebelum memulai transaksi. Di dalam transaksi, hanya operasi yang harus atomik.
Jangan panggil API eksternal di dalam transaksi. Network call yang lambat akan membakar durasi 60 detik dan mengunci resource. Selesaikan semua pekerjaan eksternal di luar transaksi.
Handling retry dengan benar. commitTransaction bisa gagal karena transient network error — dan bisa saja sebenarnya sudah commit. Gunakan pola retry pada seluruh blok (retryable writes) agar aman dari operasi ganda.
Jangan jadikan transaksi kebiasaan default. Setiap transaksi mengunci resource dan menambah overhead. Jika operasi kalian hanya mengubah satu dokumen, tidak perlu transaksi. Jika bisa dirancang ulang agar cukup satu dokumen (misal dengan embedding), itu lebih baik. Transaksi adalah penyelamat untuk kasus yang memang membutuhkan, bukan gaya hidup.
Warning
Kesalahan paling umum developer transaksi baru: lupa meneruskan { session } ke salah satu operasi. Operasi tanpa session berjalan di luar transaksi dan di-commit secara terpisah — menghancurkan jaminan atomicity. Selalu periksa bahwa setiap operasi database di dalam blok transaksi membawa session yang sama.
Info
Pahami pemikiran di balik desain: MongoDB menempatkan "atomicity per dokumen" sebagai default — ini memungkinkan operasi tunggal super cepat tanpa lock global. Transaksi multi-document adalah opt-in yang kalian bayar dengan overhead. Untuk data yang harus selalu konsisten secara atomik (uang, stok), desainlah memakai embedding agar satu dokumen = satu unit bisnis — seperti meng-embed semua item order ke dalam satu dokumen order. Transaksi hanya untuk kasus yang benar-benar melintasi unit itu.
Pada episode 14 ini kalian telah memahami kapan transaksi multi-document dibutuhkan — yaitu saat beberapa dokumen lintas collection harus berubah secara atomik seperti transfer saldo antar akun — serta menulis transaksi dengan sessions: memulai dengan startTransaction, meneruskan { session } ke setiap operasi, lalu commitTransaction atau abortTransaction, dan membersihkan session dengan endSession. Kalian juga mempelajari batasan — durasi maksimal 60 detik, kebutuhan Replica Set, dan ukuran transaksi — serta best practices: transaksi pendek, tanpa API eksternal, dan retry yang benar.
Inti yang harus dibawa pulang:
commitTransaction menyimpan semua; abortTransaction membatalkan semua.Di episode 15 berikutnya kita membangun fondasi high availability: Replica Sets (High Availability & Read Scaling). Kalian akan memahami arsitektur primary, secondary, dan arbiter, mekanisme failover otomatis lewat election, read preference untuk menskalakan pembacaan, serta mengkonfigurasi replica set tiga anggota. Sampai jumpa di episode 15!