ROLLBACK penuh yang kita kuasai bekerja seperti tombol darurat: seluruh transaksi dibatalkan tanpa pandang bulu. Skenario nyata sering butuh kelonggaran di antara dua kutub itu — wizard pendaftaran lima langkah yang gagal di langkah pembayaran, misalnya, sebaiknya membatalkan langkah keempat dan kelima saja tanpa merusak data langkah pertama hingga ketiga yang sudah tervalidasi. MySQL menjawab kebutuhan itu dengan savepoint: penanda bernama yang kita tanam di tengah transaksi dan menjadi titik pulih untuk ROLLBACK TO. Pada chapter ini kita praktikkan pembatalan parsial, savepoint bertingkat, perintah pelepasan RELEASE SAVEPOINT, serta error yang muncul saat penanda yang dirujuk tidak dikenal.
Menyiapkan Tabel Latihan #
Tabel rekening yang sama kembali dipakai agar fokus tetap pada perilaku savepoint:
DROP DATABASE IF EXISTS belajar_trx;
CREATE DATABASE belajar_trx;
USE belajar_trx;
CREATE TABLE rekening (
id INT AUTO_INCREMENT PRIMARY KEY,
pemilik VARCHAR(50) NOT NULL,
saldo DECIMAL(12,2) NOT NULL
);
INSERT INTO rekening (pemilik, saldo) VALUES ('Budi', 500000.00), ('Siti', 300000.00);
SELECT id, pemilik, saldo FROM rekening ORDER BY id;Output yang Dihasilkan:
Query OK, 0 rows affected, 1 warning (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Database changed
Query OK, 0 rows affected (0.00 sec)
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
+----+---------+-----------+
| id | pemilik | saldo |
+----+---------+-----------+
| 1 | Budi | 500000.00 |
| 2 | Siti | 300000.00 |
+----+---------+-----------+
2 rows in set (0.00 sec)SAVEPOINT dan ROLLBACK TO: Pembatalan Parsial #
Perintah SAVEPOINT nama menanam penanda pada titik statement saat itu berjalan. Perintah ROLLBACK TO SAVEPOINT nama memutar ulang hanya statement yang berjalan setelah penanda itu — sementara transaksi tetap terbuka dan statement sebelum penanda tetap utuh. Kita uji pada transfer yang sengaja dibuat mundur: potong saldo Budi dulu, tanam penanda, baru tambah saldo Siti:
START TRANSACTION;
UPDATE rekening SET saldo = saldo - 100000 WHERE id = 1;
SAVEPOINT setelah_potong;
UPDATE rekening SET saldo = saldo + 100000 WHERE id = 2;
SELECT id, pemilik, saldo FROM rekening ORDER BY id;
ROLLBACK TO SAVEPOINT setelah_potong;
SELECT id, pemilik, saldo FROM rekening ORDER BY id;
COMMIT;
SELECT id, pemilik, saldo FROM rekening ORDER BY id;Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
Query OK, 0 rows affected (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
+----+---------+-----------+
| id | pemilik | saldo |
+----+---------+-----------+
| 1 | Budi | 400000.00 |
| 2 | Siti | 400000.00 |
+----+---------+-----------+
2 rows in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
+----+---------+-----------+
| id | pemilik | saldo |
+----+---------+-----------+
| 1 | Budi | 400000.00 |
| 2 | Siti | 300000.00 |
+----+---------+-----------+
2 rows in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
+----+---------+-----------+
| id | pemilik | saldo |
+----+---------+-----------+
| 1 | Budi | 400000.00 |
| 2 | Siti | 300000.00 |
+----+---------+-----------+
2 rows in set (0.00 sec)Mari kita bedah hasilnya baris demi baris. SELECT pertama menampilkan keadaan di puncak transaksi: saldo Budi Rp400.000,00 dan saldo Siti Rp400.000,00 — dua langkah transfer sama-sama berjalan. ROLLBACK TO SAVEPOINT setelah_potong lalu memutar ulang hanya langkah kedua: SELECT kedua menunjukkan saldo Siti kembali Rp300.000,00, sedangkan pemotongan saldo Budi Rp100.000,00 tetap utuh. Poin terpenting ada di COMMIT yang menyusul: karena ROLLBACK TO tidak menutup transaksi, COMMIT masih punya sesuatu untuk di-commit — SELECT terakhir menegaskan saldo Budi terkunci di Rp400.000,00. Bandingkan dengan ROLLBACK total di chapter sebelumnya yang menutup transaksi dan memulihkan kedua langkah sekaligus.
Savepoint Bertingkat: Kembali ke Tengah #
Penanda bisa ditanam bertingkat sepanjang transaksi, dan kita bebas memilih titik pulih mana pun. Kita buktikan dengan tiga penanda bernama pada rangkaian pemotongan saldo Siti:
START TRANSACTION;
UPDATE rekening SET saldo = saldo - 100000 WHERE id = 2;
SAVEPOINT langkah_satu;
UPDATE rekening SET saldo = saldo - 50000 WHERE id = 2;
SAVEPOINT langkah_dua;
UPDATE rekening SET saldo = saldo - 25000 WHERE id = 2;
SAVEPOINT langkah_tiga;
SELECT saldo FROM rekening WHERE id = 2;
ROLLBACK TO SAVEPOINT langkah_dua;
SELECT saldo FROM rekening WHERE id = 2;
COMMIT;
SELECT saldo FROM rekening WHERE id = 2;Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
Query OK, 0 rows affected (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
Query OK, 0 rows affected (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
Query OK, 0 rows affected (0.00 sec)
+-----------+
| saldo |
+-----------+
| 125000.00 |
+-----------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
+-----------+
| saldo |
+-----------+
| 150000.00 |
+-----------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
+-----------+
| saldo |
+-----------+
| 150000.00 |
+-----------+
1 row in set (0.00 sec)Ketiga pemotongan berjalan berturut-turut dan masing-masing diikuti penanda: saldo Rp300.000,00 turun menjadi Rp200.000,00 lalu ditandai langkah_satu, menjadi Rp150.000,00 lalu ditandai langkah_dua, dan menjadi Rp125.000,00 lalu ditandai langkah_tiga — SELECT pertama menampilkan angka Rp125.000,00 di puncak rantai. ROLLBACK TO SAVEPOINT langkah_dua memutar ulang hanya pemotongan terakhir sebesar Rp25.000,00: SELECT kedua menampilkan saldo Rp150.000,00. Dua pemotongan sebelumnya — Rp100.000,00 dan Rp50.000,00 — tetap utuh karena berada sebelum penanda yang kita pilih; penanda langkah_satu dan langkah_tiga juga tetap berdiri meski yang tengah yang dipakai. COMMIT kemudian mengukuhkan hasil akhir Rp150.000,00. Pola “rollback ke tengah” seperti inilah yang dipakai proses bertingkat: mundur sejauh yang diperlukan, tidak lebih.
RELEASE SAVEPOINT: Melepas Penanda Tanpa Membatalkan #
Penanda yang sudah tidak diperlukan bisa dilepas dengan RELEASE SAVEPOINT nama. Melepas penanda berbeda dari memutar ulang: RELEASE tidak mengubah satu baris data pun — penandanya saja yang dihapus sehingga tidak bisa lagi menjadi tujuan ROLLBACK TO. Tanpa penanda, ROLLBACK total menjadi satu-satunya jalan pembatalan:
START TRANSACTION;
UPDATE rekening SET saldo = saldo + 60000 WHERE id = 1;
SAVEPOINT sebelum_pulih;
UPDATE rekening SET saldo = saldo + 30000 WHERE id = 1;
RELEASE SAVEPOINT sebelum_pulih;
SELECT saldo FROM rekening WHERE id = 1;
ROLLBACK;
SELECT saldo FROM rekening WHERE id = 1;Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
Query OK, 0 rows affected (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
Query OK, 0 rows affected (0.00 sec)
+-----------+
| saldo |
+-----------+
| 490000.00 |
+-----------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
+-----------+
| saldo |
+-----------+
| 400000.00 |
+-----------+
1 row in set (0.00 sec)SELECT pertama menampilkan saldo Rp490.000,00 — kedua kenaikan tetap utuh meski penandanya sudah dilepas, bukti bahwa RELEASE tidak membatalkan apa pun. Namun ketika ROLLBACK total dijalankan, seluruh transaksi ikut terpulihkan: saldo kembali Rp400.000,00 karena titik pulih parsial sudah tidak ada lagi. Penanda memang murah, tetapi melepasnya tepat waktu menjaga daftar penanda tidak menumpuk — hanya boleh ada satu penanda dengan nama yang sama dalam satu transaksi; penamaan ulang akan menghapus penanda lama secara diam-diam.
Savepoint Tak Dikenal: ERROR 1305 #
Menarik satu jebakan terakhir: setiap savepoint hidup hanya selama transaksi pembuatnya. Begitu transaksi ditutup — lewat COMMIT, ROLLBACK, atau koneksi terputus — seluruh penanda di dalamnya ikut lenyap. Merujuk penanda yang tidak ada memicu error:
-- @error
START TRANSACTION;
UPDATE rekening SET saldo = saldo + 60000 WHERE id = 2;
ROLLBACK TO SAVEPOINT langkah_dua;Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
ERROR 1305 (42000): SAVEPOINT langkah_dua does not exist
ByeERROR 1305 melaporkan bahwa SAVEPOINT langkah_dua tidak dikenal — penanda bernama persis itu memang pernah kita buat, tetapi di transaksi sebelumnya yang sudah lama tertutup. Transaksi baru yang kita buka tidak mewarisi penanda transaksi lawannya. Karena statement penutup error ini membuat transaksi terbuka tanpa penutup, koneksi yang terputus akan membatalkan kenaikan saldo Rp60.000,00 secara otomatis — konsisten dengan aturan di chapter sebelumnya.
Use Case: Wizard Multi-Langkah #
Pola penggunaan paling natural untuk savepoint adalah proses multi-langkah yang mahal diulang dari nol. Bayangkan wizard pendaftaran anggota dengan empat langkah: validasi data diri, simpan alamat, cadangkan preferensi, lalu potong saldo biaya keanggotaan. Tanpa savepoint, kegagalan di langkah keempat memaksa ROLLBACK total — pengguna harus mengulang validasi data diri dari awal. Dengan savepoint ditanam setelah langkah kedua, kegagalan pembayaran cukup ditangani dengan ROLLBACK TO ke penanda itu; data diri dan alamat tetap tersimpan dalam transaksi yang sama, pengguna tinggal mengulang langkah pembayaran. COMMIT di ujung sukses mengukuhkan semuanya sekaligus — jaminan all-or-nothing dari transaksi tetap utuh, hanya bendannya lebih halus.
Membersihkan Database Latihan #
Rangkaian latihan selesai dan database latihan kita jatuhkan kembali:
DROP DATABASE belajar_trx;Output yang Dihasilkan:
Query OK, 1 row affected (0.00 sec)Referensi #
- SAVEPOINT, ROLLBACK TO SAVEPOINT, and RELEASE SAVEPOINT Statements — MySQL 8.4 Reference Manual
- START TRANSACTION, COMMIT, and ROLLBACK Statements — MySQL 8.4 Reference Manual
Pembatalan parsial kini lengkap: SAVEPOINT menanam penanda, ROLLBACK TO memutar ulang sebagian transaksi tanpa menutupnya, RELEASE melepas penanda, dan penanda mati bersama transaksinya — lengkap dengan bukti ERROR 1305. Ketiga perintah ini menuntaskan kotak peralatan dasar transaksi. Berikutnya kita naik satu tingkat abstraksi dan bertanya: jaminan apa sebenarnya yang diberikan transaksi pada data kita? Empat huruf — A, C, I, D — menjawabnya, dan tiap huruf punya mekanisme nyata di dalam InnoDB yang layak dipahami.