Pilihan Anda adalah:
-
Jalankan di
SERIALIZABLE
isolasi. Transaksi interdependen akan dibatalkan pada komit karena mengalami kegagalan serialisasi. Anda akan mendapatkan banyak spam log kesalahan, dan Anda akan melakukan banyak percobaan ulang, tetapi ini akan bekerja dengan andal. -
Tentukan
UNIQUE
kendala dan coba lagi pada kegagalan, seperti yang Anda catat. Masalah yang sama seperti di atas. -
Jika ada objek induk, Anda dapat
SELECT ... FOR UPDATE
objek induk sebelum melakukanmax
pertanyaan. Dalam hal ini Anda akanSELECT 1 FROM bar WHERE bar_id = $1 FOR UPDATE
. Anda menggunakanbar
sebagai kunci untuk semuafoo
s denganbar_id
itu . Anda kemudian dapat mengetahui bahwa aman untuk melanjutkan, selama setiap kueri yang melakukan peningkatan penghitung Anda melakukannya dengan andal. Ini bisa bekerja dengan baik.Ini masih melakukan kueri agregat untuk setiap panggilan, yang (per opsi berikutnya) tidak diperlukan, tetapi setidaknya itu tidak mengirim spam ke log kesalahan seperti opsi di atas.
-
Gunakan meja penghitung. Inilah yang akan saya lakukan. Baik di
bar
, atau di tabel samping sepertibar_foo_counter
, dapatkan ID baris menggunakanUPDATE bar_foo_counter SET counter = counter + 1 WHERE bar_id = $1 RETURNING counter
atau opsi yang kurang efisien jika kerangka kerja Anda tidak dapat menangani
RETURNING
:SELECT counter FROM bar_foo_counter WHERE bar_id = $1 FOR UPDATE; UPDATE bar_foo_counter SET counter = $1;
Kemudian, dalam transaksi yang sama , gunakan baris penghitung yang dihasilkan untuk
number
. Saat Anda melakukan, baris tabel penghitung untukbar_id
itu akan dibuka untuk kueri berikutnya untuk digunakan. Jika Anda memutar kembali, perubahan akan dibuang.
Saya merekomendasikan pendekatan penghitung, menggunakan tabel samping khusus untuk penghitung alih-alih menambahkan kolom ke bar
. Itu lebih bersih untuk dimodelkan, dan berarti Anda membuat lebih sedikit pembaruan mengasapi di bar
, yang dapat memperlambat kueri ke bar
.