Menjalankan transaksi itu mudah — menjalankannya tanpa merusak kesehatan sistem adalah soal lain. Selama transaksi terbuka, server menahan lock atas document yang disentuh dan menyimpan snapshot di cache WiredTiger; transaksi yang lambat membuat transaksi lain menunggu, konflik bermunculan, dan cache tertekan. Chapter ini merumuskan aturan praktis merancang batas transaksi: sekecil apa, sependek apa, dan bagaimana merancang coba-ulang yang aman — lalu memverifikasi batas waktu bawaan server langsung dari rs0.
Aturan Praktis #
| Lakukan | Hindari |
|---|---|
| Awali transaksi setelah semua bacaan persiapan selesai | Membuka transaksi lalu menunggu input pengguna |
| Masukkan hanya operasi tulis yang membentuk invariant | Menjejalkan seluruh alur bisnis (validasi, kirim email) ke dalam transaksi |
| Targetkan durasi di bawah satu detik; hitungan milidetik lebih baik | Transaksi yang memproses banyak document sekaligus dalam satu paket |
| Rancang coba-ulang yang idempoten | Mengulang transaksi yang efeknya bisa berlipat |
Tangani errorLabels pada setiap kegagalan | Mengabaikan galat commit dan berharap semuanya baik-baik saja |
Dua aturan pertama adalah satu prinsip yang sama: baca dulu di luar, tulis cepat di dalam. Validasi “saldo cukup tidak”, “buku masih dijual tidak”, semuanya bisa dilakukan sebelum startTransaction() — data bisa berubah sedikit di sela waktu itu, tetapi itulah gunanya filter penjaga (saldo: { $gte: nominal }) di dalam operasi tulis. Transaksi hanya untuk bagian yang tidak bisa ditunda: rangkaian tulis yang harus menyatu.
Batas kerasnya di sisi server: transaksi yang terlalu lama hidup akan dipaksa berhenti. Kita tanyakan langsung ke rs0:
db.adminCommand({ getParameter: 1, transactionLifetimeLimitSeconds: 1 })Output yang Dihasilkan:
{
transactionLifetimeLimitSeconds: 60,
ok: 1,
'$clusterTime': {
clusterTime: Timestamp({ t: 1792252932, i: 11 }),
signature: {
hash: Binary.createFromBase64('AAAAAAAAAAAAAAAAAAAAAAAAAAA=', 0),
keyId: Long('0')
}
},
operationTime: Timestamp({ t: 1792252932, i: 11 })
}switched to db latihan_relasi
true
true
{
acknowledged: true,
insertedIds: {
'0': ObjectId('6ad39c06a3619e0395db0ccb'),
'1': ObjectId('6ad39c06a3619e0395db0ccc')
}
}
kode_1[Function: transferIdempoten]
[
{ pengguna: 'Budi', saldo: 80000 },
{ pengguna: 'Siti', saldo: 70000 }
]Ditolak: E11000 duplicate key error collection: latihan_relasi.mutasi index: kode_1 dup key: { kode: "TRF-777" }
[
{ pengguna: 'Budi', saldo: 80000 },
{ pengguna: 'Siti', saldo: 70000 }
]
[ { kode: 'TRF-777', dari: 'Budi', ke: 'Siti', nominal: 20000 } ]Percobaan kedua ditolak E11000 pada kode: "TRF-777"; transaksi batal total, dan saldo tetap 80000/70000 — persis seperti hasil percobaan pertama. Satu mutasi TRF-777 tercatat. Berapa kali pun request diulang, efeknya tidak berlipat: inilah syarat yang membuat strategi coba-ulang transaksi menjadi aman.
Rangkuman #
- Selama terbuka, transaksi menahan lock dan memakan cache — kuncinya: baca persiapan di luar, tulis sesingkat mungkin di dalam.
- Batas waktu bawaan server: 60 detik (
transactionLifetimeLimitSeconds), diverifikasi langsung darirs0; satu transaksi menjadi banyak entri oplog yang masing-masing ≤ 16MB. - Coba-ulang aman hanya bila idempoten: idempotency key (
kodetransfer) + unique index membuat percobaan kedua tertolak tanpa efek samping. - Penjagaan bisnis tetap relevan di dalam transaksi — filter
$gtedan pemeriksaanmatchedCountmenjaga invariant,throwmemicu abort.
Transaksi menuntaskan sisi penulisan yang konsisten. Satu dimensi terakhir menunggu: konsistensi pembacaan di tengah replication — kombinasi read concern, write concern, dan session, pembahasan penutup Section ini.