Kapan pun Anda meminta SERIALIZABLE
isolasi DB akan mencoba membuat kumpulan kueri yang bersamaan tampak telah dieksekusi secara serial dalam hal hasil yang mereka hasilkan. Ini tidak selalu memungkinkan, mis. ketika dua transaksi memiliki ketergantungan timbal balik. Dalam hal ini, PostgreSQL akan membatalkan salah satu transaksi dengan kesalahan kegagalan serialisasi, memberi tahu Anda bahwa Anda harus mencobanya lagi.
Kode yang menggunakan SERIALIZABLE
harus selalu siap untuk mencoba kembali transaksi. Itu harus memeriksa SQLSTATE
dan, untuk kegagalan serialisasi, ulangi transaksi.
Lihat dokumentasi isolasi transaksi .
Dalam hal ini, saya pikir kesalahpahaman utama Anda mungkin adalah:
karena tidak seperti itu, ini adalah INSERT ... SELECT
yang menyentuh vo_business.repositoryoperation
baik untuk membaca maupun menulis. Itu cukup untuk menciptakan ketergantungan potensial dengan transaksi lain yang melakukan hal yang sama, atau transaksi yang membaca dan menulis ke tabel dengan cara lain.
Selain itu, kode isolasi yang dapat diserialisasi dalam beberapa keadaan dapat berubah menjadi penyimpanan informasi ketergantungan tingkat blok untuk alasan efisiensi. Jadi mungkin tidak selalu transaksi menyentuh baris yang sama, hanya blok penyimpanan yang sama, terutama di bawah beban.
PostgreSQL akan lebih memilih untuk membatalkan transaksi serial jika tidak yakin itu aman. Sistem pembuktian memiliki keterbatasan. Jadi, mungkin juga Anda baru saja menemukan kasus yang membodohinya.
Untuk mengetahui dengan pasti saya perlu melihat kedua transaksi secara berdampingan, tetapi berikut adalah bukti yang menunjukkan insert ... select
dapat bertentangan dengan dirinya sendiri. Buka tiga psql
sesi dan jalankan:
session0: CREATE TABLE serialdemo(x integer, y integer);
session0: BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
session0: LOCK TABLE serialdemo IN ACCESS EXCLUSIVE MODE;
session1: BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
session2: BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
session1: INSERT INTO serialdemo (x, y)
SELECT 1, 2
WHERE NOT EXISTS (SELECT 1 FROM serialdemo WHERE x = 1);
session2: INSERT INTO serialdemo (x, y)
SELECT 1, 2
WHERE NOT EXISTS (SELECT 1 FROM serialdemo WHERE x = 1);
session0: ROLLBACK;
session1: COMMIT;
session2: COMMIT;
session1 akan melakukan dengan baik. session2 akan gagal dengan:
ERROR: could not serialize access due to read/write dependencies among transactions
DETAIL: Reason code: Canceled on identification as a pivot, during commit attempt.
HINT: The transaction might succeed if retried.
Ini bukan kegagalan serialisasi yang sama dengan kasus Anda, dan tidak membuktikan bahwa Anda pernyataan dapat bertentangan satu sama lain, tetapi ini menunjukkan bahwa insert ... select
tidak atomik seperti yang Anda pikirkan.