Transaction di Postgres

Transaksi di PostgreSQL adalah unit kerja logis yang terdiri dari satu atau lebih pernyataan SQL yang dijalankan sebagai satu kesatuan. Tujuan utamanya adalah untuk menjaga konsistensi, isolasi, dan integritas data.


πŸ”„ Konsep Transaksi di PostgreSQL

1. ACID

Transaksi harus memenuhi sifat ACID:

  • Atomicity: Semua pernyataan dalam transaksi berhasil semua atau dibatalkan semua.

  • Consistency: Data tetap konsisten sebelum dan sesudah transaksi.

  • Isolation: Transaksi tidak terlihat oleh transaksi lain sampai selesai.

  • Durability: Setelah commit, data tersimpan permanen.

2. Perintah Dasar

BEGIN;           -- Memulai transaksi
-- Perintah SQL
COMMIT;          -- Menyimpan perubahan
ROLLBACK;        -- Membatalkan semua perubahan

πŸ”’ Apa itu FOR UPDATE?

FOR UPDATE digunakan untuk mengunci baris yang dipilih dalam transaksi agar tidak bisa diubah oleh transaksi lain sampai transaksi selesai (commit/rollback).

Tujuannya:

  • Mencegah kondisi race condition (misalnya dua pengguna mengedit data yang sama).

  • Memastikan data yang dibaca tidak berubah sebelum transaksi selesai.


🎯 Contoh dan Ilustrasi

1. Setup Tabel


2. Simulasi Transaksi tanpa FOR UPDATE

Terminal 1:

Terminal 2 (dijalankan sebelum commit Terminal 1):

Masalah: Kedua transaksi menganggap saldo awal 1000 dan memperbarui berdasarkan data yang sudah kadaluarsa β†’ terjadi inconsistency.


3. Simulasi Transaksi dengan FOR UPDATE

Terminal 1:

Terminal 2:

Keuntungan: Data tidak bisa dibaca dan diproses oleh transaksi lain sebelum transaksi pertama selesai β†’ race condition dihindari.


πŸ§ͺ Praktek Langsung: Transfer Saldo

Misal: Transfer 100 dari Alice ke Bob

Jika terjadi error, bisa langsung:


🧠 Kesimpulan

Konsep
Penjelasan

Transaksi

Sekumpulan perintah SQL yang dieksekusi sebagai satu kesatuan

BEGIN/COMMIT

Memulai dan menyelesaikan transaksi

ROLLBACK

Membatalkan transaksi

FOR UPDATE

Mengunci baris yang dipilih untuk mencegah konflik antar transaksi


⚠️ Apa Itu Deadlock?

Deadlock terjadi ketika dua transaksi saling menunggu satu sama lain untuk melepaskan kunci, sehingga tidak ada yang bisa lanjut.

πŸ” Ilustrasi Kasus Deadlock

Misalkan kita punya tabel akun yang sama:


πŸ§ͺ Simulasi Deadlock dengan FOR UPDATE

Transaksi 1 (T1):

Transaksi 2 (T2):

πŸ” Hasil:

  • T1 mengunci Alice β†’ tunggu Bob

  • T2 mengunci Bob β†’ tunggu Alice

  • Keduanya saling tunggu β†’ deadlock

  • PostgreSQL akan memilih satu untuk dibatalkan (ERROR: deadlock detected)


πŸ› οΈ Kenapa Bisa Deadlock Walau Pakai FOR UPDATE?

Karena FOR UPDATE mengunci baris secara eksklusif. Kalau dua transaksi mengunci dengan urutan berbeda, maka bisa saling tunggu. Urutan pengambilan kunci itu penting.


βœ… Solusi: Gunakan FOR NO KEY UPDATE

❓ Apa Itu FOR NO KEY UPDATE?

  • FOR NO KEY UPDATE adalah varian lebih ringan dari FOR UPDATE.

  • Mengunci baris hanya untuk update nilai kolom, tanpa mengizinkan update kunci (misalnya kolom primary key).

  • Ini mengurangi kemungkinan deadlock karena kunci yang digunakan tidak terlalu agresif.


πŸ“Š Perbandingan Lock Mode

Lock Mode
Deskripsi

FOR UPDATE

Mengunci baris untuk update penuh (termasuk kunci)

FOR NO KEY UPDATE

Mengunci baris untuk update non-kunci (lebih ringan)

FOR SHARE

Hanya baca data, bisa dibaca transaksi lain

FOR KEY SHARE

Bisa dibaca dan referensi foreign key, tapi tidak bisa dihapus/dikunci penuh


πŸ§ͺ Simulasi dengan FOR NO KEY UPDATE

Jika transaksi hanya ingin update saldo (bukan id), maka lebih aman:

T1:

T2:

⚠️ Hasil:

  • Deadlock masih mungkin terjadi kalau urutan ambil lock-nya berbeda, meskipun lebih jarang.

  • Maka kunci solusi utamanya tetap: urutkan pengambilan kunci secara konsisten!


βœ… Best Practice Hindari Deadlock

  1. Selalu akses resource dalam urutan yang sama (misal: urut berdasarkan id ASC).

  2. Gunakan lock mode serendah mungkin (FOR NO KEY UPDATE lebih baik dari FOR UPDATE).

  3. Gunakan timeout / pengecekan deadlock dan rollback otomatis jika perlu.


πŸ”š Kesimpulan

  • FOR UPDATE kuat tapi agresif β†’ rawan deadlock jika resource diakses tidak konsisten.

  • FOR NO KEY UPDATE lebih ringan β†’ cocok jika hanya update kolom non-kunci.

  • Urutan pengambilan kunci lebih penting dari jenis lock-nya.


πŸ” Apa Maksudnya "Pengambilan Kunci Secara Konsisten"?

Artinya: semua transaksi yang mengakses banyak baris/data harus mengunci data dalam urutan yang sama, misalnya:

  • berdasarkan id ASC

  • berdasarkan abjad nama

  • urutan waktu tertentu

❌ Jika Transaksi A mengunci data dengan urutan A β†’ B dan Transaksi B mengunci data dengan urutan B β†’ A, ⚠️ maka potensi deadlock sangat tinggi.

βœ… Jika semua transaksi selalu mengunci A β†’ B, maka tidak akan ada saling tunggu silang.


πŸ” Contoh Deadlock karena Kunci Tidak Konsisten

Misal tabel akun:

Transaksi A

Transaksi B

πŸ” Keduanya saling tunggu β†’ Deadlock!


βœ… Solusi: Ambil Kunci Berdasarkan Urutan Tetap

Cara Aman:

Jadi, transaksi A dan B:

βœ… Hasil: tidak ada saling tunggu silang, karena kedua transaksi:

  • mengakses id = 1 lebih dulu

  • baru id = 2 berikutnya


πŸ§ͺ Contoh Praktis: Transfer Saldo Aman


🧠 Ringkasan Tips

Tips Pengambilan Kunci Konsisten
Penjelasan

πŸ” Gunakan urutan tetap

Gunakan ORDER BY berdasarkan id, nama, atau kriteria stabil lainnya

🚫 Jangan kunci berbeda-beda urutan

Hindari transaksi A β†’ B dan B β†’ A

βš–οΈ Gunakan LEAST / GREATEST

Untuk menentukan siapa dikunci lebih dulu secara deterministik

πŸ” Gabungkan dalam satu query

Semakin sedikit query terpisah, semakin kecil peluang deadlock

πŸ’‘ Gunakan lock ringan (NO KEY UPDATE)

Kalau tidak perlu ubah PK atau referensi, pakai lock lebih ringan


Last updated