Mengatasi Deadlock Detected pada Transaksi Paralel PostgreSQL
Deadlock adalah situasi di mana dua atau lebih transaksi menunggu satu sama lain untuk melepaskan kunci pada sumber daya tertentu, sehingga tidak ada yang dapat melanjutkan. Dalam PostgreSQL, deadlock dapat terjadi jika transaksi paralel tidak dirancang atau dikelola dengan hati-hati.
Contoh Masalah Deadlock
Skema Tabel
Misalkan kita memiliki dua tabel:
CREATE TABLE accounts (
account_id SERIAL PRIMARY KEY,
balance NUMERIC(10, 2) NOT NULL
);
Table lainnya adalah
CREATE TABLE transactions (
transaction_id SERIAL PRIMARY KEY,
account_id INT REFERENCES accounts(account_id),
amount NUMERIC(10, 2) NOT NULL,
transaction_date TIMESTAMP DEFAULT now()
);
Contoh Skenario Deadlock
Transaksi 1:
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
UPDATE transactions SET amount = 100 WHERE transaction_id = 10;
-- Menunggu Transaksi 2 selesai
Transaksi 2:
BEGIN;
UPDATE transactions SET amount = 200 WHERE transaction_id = 10;
UPDATE accounts SET balance = balance + 100 WHERE account_id = 1;
-- Menunggu Transaksi 1 selesai
Karena kedua transaksi saling menunggu kunci yang dipegang oleh transaksi lain, PostgreSQL akan mendeteksi deadlock dan membatalkan salah satu transaksi.
Pesan Kesalahan:
ERROR: deadlock detected
DETAIL: Process 12345 waits for ShareLock on transaction 67890; blocked by process 67890.
HINT: See server log for query details.
Langkah-Langkah Mengatasi Deadlock
1. Pahami Pola Akses Sumber Daya
Deadlock terjadi karena transaksi mengakses sumber daya dalam urutan yang berbeda.
Solusi: Pastikan semua transaksi mengakses sumber daya dalam urutan yang sama.
Contoh:
-- Urutan konsisten untuk semua transaksi
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
UPDATE transactions SET amount = 100 WHERE transaction_id = 10;
COMMIT;
2. Gunakan FOR UPDATE untuk Kunci Eksklusif
Jika Anda akan memperbarui data, gunakan kunci eksklusif untuk menghindari deadlock.
Contoh:
-- Transaksi 1
BEGIN;
SELECT * FROM accounts WHERE account_id = 1 FOR UPDATE;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
COMMIT;
-- Transaksi 2
BEGIN;
SELECT * FROM accounts WHERE account_id = 1 FOR UPDATE;
UPDATE accounts SET balance = balance + 100 WHERE account_id = 1;
COMMIT;
3. Gunakan Waktu Tunggu Kunci (lock_timeout)
Batasi waktu tunggu untuk memperoleh kunci, sehingga transaksi tidak menggantung terlalu lama.
Tambahkan pengaturan berikut di PostgreSQL:
SET lock_timeout = '2s';
4. Kurangi Waktu Transaksi Berlangsung
Transaksi panjang meningkatkan risiko deadlock.
Tips:
Hindari operasi yang memakan waktu lama dalam transaksi, seperti membaca file atau menunggu input pengguna.
Pastikan transaksi hanya mencakup operasi database yang penting.
5. Gunakan Tingkat Isolasi yang Sesuai
Gunakan tingkat isolasi transaksi yang sesuai untuk mengurangi konflik kunci.
Default: READ COMMITTED
Jika deadlock sering terjadi, pertimbangkan untuk menggunakan SERIALIZABLE.
Contoh:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
COMMIT;
6. Monitor dan Identifikasi Deadlock
Gunakan log PostgreSQL untuk menganalisis deadlock.
Aktifkan logging dengan menambahkan konfigurasi berikut di postgresql.conf:
log_min_messages = ERROR
log_lock_waits = on
Setelah itu, restart PostgreSQL dan analisis log untuk menemukan pola akses yang menyebabkan deadlock.
Contoh Penanganan Ulang Transaksi
Saat transaksi dibatalkan karena deadlock, aplikasi harus menangkap error dan mencoba ulang transaksi.
Contoh Pseudocode:
import psycopg2
from psycopg2 import OperationalError
connection = psycopg2.connect("dbname=test user=postgres password=secret")
retry_count = 3
for attempt in range(retry_count):
try:
with connection:
with connection.cursor() as cursor:
cursor.execute("BEGIN")
cursor.execute("UPDATE accounts SET balance = balance - 100 WHERE account_id = 1")
cursor.execute("UPDATE transactions SET amount = 100 WHERE transaction_id = 10")
cursor.execute("COMMIT")
break
except OperationalError as e:
if "deadlock detected" in str(e):
print(f"Deadlock detected, retrying ({attempt + 1}/{retry_count})...")
else:
raise e
Deadlock adalah masalah yang dapat menyebabkan kegagalan transaksi pada PostgreSQL. Dengan memastikan urutan akses yang konsisten, menggunakan kunci eksklusif, dan membatasi waktu transaksi, Anda dapat meminimalkan kemungkinan deadlock. Pastikan aplikasi Anda juga dirancang untuk menangani deadlock dengan mencoba ulang transaksi jika diperlukan. Semoga bermanfaat dan mohon maaf jika ada informasi yang tidak sesuai.
Comments
Post a Comment