Transaksi & ACID: Semua Berhasil atau Tidak Sama Sekali
BEGIN, COMMIT, ROLLBACK, SAVEPOINT; memahami ACID lewat kasus transfer saldo; dan sekilas isolasi transaksi serta locking di PostgreSQL.
Analogi: Transaksi itu seperti transfer lewat ATM: mengurangi saldo pengirim dan menambah saldo penerima adalah satu paket. Kalau mesin mati di tengah jalan, bank membatalkan semuanya, bukan meninggalkan uangmu menggantung. COMMIT = "konfirmasi", ROLLBACK = "batalkan".
Kasus klasik: transfer uang
Ayu mentransfer Rp100.000 ke Bima. Secara teknis ada dua langkah:
- Kurangi saldo Ayu 100.000.
- Tambah saldo Bima 100.000.
Bagaimana kalau listrik padam setelah langkah 1 tapi sebelum langkah 2? Uang Ayu hilang, Bima tidak menerima apa pun. Bencana. Kita butuh jaminan "dua langkah ini satu paket".
Transaksi = satu paket kerja
BEGIN; -- mulai transaksi
UPDATE rekening SET saldo = saldo - 100000 WHERE pemilik = 'Ayu';
UPDATE rekening SET saldo = saldo + 100000 WHERE pemilik = 'Bima';
COMMIT; -- simpan permanen
BEGIN: mulai. Perubahan setelahnya belum final dan hanya terlihat olehmu.COMMIT: semua perubahan disimpan sekaligus.ROLLBACK: batalkan semuanya seolah tidak pernah terjadi.
BEGIN;
UPDATE rekening SET saldo = saldo - 999999999 WHERE pemilik = 'Ayu'; -- ERROR: melanggar CHECK saldo >= 0
ROLLBACK; -- semua dibatalkan
Di PostgreSQL, bila ada error di tengah transaksi, seluruh transaksi berstatus "aborted" dan kamu harus ROLLBACK dulu sebelum bisa bekerja lagi.
ACID: empat janji transaksi
| Huruf | Arti | Maksudnya |
|---|---|---|
| Atomicity | Atomik | Semua langkah berhasil, atau tidak ada yang berhasil. |
| Consistency | Konsisten | Data selalu memenuhi aturan (constraint) sebelum dan sesudah. |
| Isolation | Terisolasi | Transaksi yang berjalan bersamaan tidak saling mengganggu. |
| Durability | Tahan lama | Setelah COMMIT, data aman walau server mati (disimpan di WAL). |
SAVEPOINT: titik simpan di tengah jalan
BEGIN;
UPDATE ...;
SAVEPOINT langkah1;
UPDATE ...; -- ternyata salah
ROLLBACK TO langkah1; -- kembali ke titik simpan, langkah pertama tetap
COMMIT;
Autocommit
Tanpa BEGIN, setiap perintah SQL langsung menjadi transaksi sendiri dan otomatis di-COMMIT. Itu sebabnya DELETE tanpa WHERE di luar transaksi tidak bisa dibatalkan.
Isolasi dan locking (sekilas)
PostgreSQL memakai MVCC: pembaca tidak memblokir penulis dan sebaliknya, karena setiap transaksi melihat "foto" data pada saat tertentu. Level isolasi default adalah READ COMMITTED. Untuk kasus sensitif, kunci baris secara eksplisit:
BEGIN;
SELECT saldo FROM rekening WHERE pemilik = 'Ayu' FOR UPDATE; -- kunci baris sampai COMMIT
UPDATE rekening SET saldo = saldo - 100000 WHERE pemilik = 'Ayu';
COMMIT;
Dengan FOR UPDATE, dua kasir yang memproses saldo yang sama tidak akan saling menimpa.
Kasus nyata: checkout: stok berkurang, pesanan tercatat, atau tidak sama sekali
BEGIN;
INSERT INTO pesanan (pelanggan_id, tanggal, status) VALUES (1, CURRENT_DATE, 'baru');
UPDATE buku SET stok = stok - 1 WHERE id = 1 AND stok > 0;
-- jika ada langkah gagal, jalankan ROLLBACK; jika semua beres:
COMMIT;Tanpa transaksi, kegagalan di tengah jalan bisa menghasilkan pesanan tanpa pengurangan stok, atau sebaliknya. Itulah sebabnya sistem pembayaran dan inventori wajib memakai transaksi.
Kesalahan yang sering terjadi
- Lupa
COMMITsehingga transaksi menggantung dan menahan kunci baris, membuat pengguna lain menunggu. - Membuat transaksi sangat panjang (menunggu input pengguna di tengahnya). Pertahankan transaksi sependek mungkin.
- Menganggap error di tengah transaksi bisa dilanjutkan. Di PostgreSQL, setelah error transaksi berstatus aborted dan harus di-
ROLLBACK.