Saya pernah mendengar masalah konkurensi seperti itu di MySQL sebelumnya. Tidak demikian halnya dengan Postgres.
Kunci tingkat baris bawaan pada READ COMMITTEDtingkat isolasi transaksi default sudah cukup.
Saya menyarankan satu pernyataan dengan CTE pemodifikasi data (sesuatu yang MySQL juga tidak miliki) karena lebih mudah untuk menyampaikan nilai dari satu tabel ke tabel lainnya secara langsung (jika Anda memerlukannya). Jika Anda tidak memerlukan apa pun dari coupontabel, Anda dapat menggunakan transaksi dengan laporan terpisah UPDATEdan INSERTjuga.
WITH upd AS (
UPDATE coupon
SET used = true
WHERE coupon_id = 123
AND NOT used
RETURNING coupon_id, other_column
)
INSERT INTO log (coupon_id, other_column)
SELECT coupon_id, other_column FROM upd;
Seharusnya menjadi hal yang langka bahwa lebih dari satu transaksi mencoba menebus kupon yang sama. Mereka memiliki nomor unik, bukan? Lebih dari satu transaksi yang mencoba pada saat yang sama harus jauh lebih jarang. (Mungkin bug aplikasi atau seseorang mencoba permainan sistem?)
Bagaimanapun , UPDATEsatu - satunya berhasil untuk tepat satu transaksi, apa pun yang terjadi. Seorang UPDATEmemperoleh kunci level baris pada setiap baris target sebelum memperbarui. Jika transaksi bersamaan mencoba ke UPDATEbaris yang sama, itu akan melihat kunci di baris dan menunggu sampai transaksi pemblokiran selesai ( ROLLBACKatau COMMIT), kemudian menjadi yang pertama dalam antrian kunci:
Jika berkomitmen, periksa kembali kondisinya. Jika masih NOT used, kunci baris dan lanjutkan. Kalau tidak, UPDATEsekarang tidak menemukan baris kualifikasi dan tidak melakukan apa pun , tidak mengembalikan baris, sehingga INSERTjuga tidak melakukan apa pun.
Jika dibatalkan, kunci baris dan lanjutkan.
Tidak ada potensi untuk kondisi balapan .
Tidak ada potensi kebuntuan kecuali Anda lebih banyak menulis ke dalam transaksi yang sama atau mengunci lebih banyak baris daripada hanya satu.
Ini INSERTbebas perawatan. Jika, karena suatu kesalahan coupon_idsudah ada dalam logtabel (dan Anda memiliki batasan UNIK atau PK log.coupon_id), seluruh transaksi akan dibatalkan setelah pelanggaran unik. Menunjukkan keadaan ilegal di DB Anda. Jika pernyataan di atas adalah satu-satunya cara untuk menulis ke logtabel, itu seharusnya tidak pernah terjadi.