[英]How to avoid race conditions in GORM
我正在開發一個系統來啟用具有增量隊列號的患者注冊。 我正在使用 Go、GORM 和 MySQL。
當多個患者同時注冊時會出現問題,他們往往會獲得相同的隊列號,這是不應該發生的。
我嘗試使用事務和鈎子來實現這一點,但我仍然得到重復的隊列號。 我還沒有找到任何關於在事務發生時如何鎖定數據庫的資源。
func (r repository) CreatePatient(pat *model.Patient) error {
tx := r.db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
err := tx.Error
if err != nil {
return err
}
// 1. get latest queue number and assign it to patient object
var queueNum int64
err = tx.Model(&model.Patient{}).Where("registration_id", pat.RegistrationID).Select("queue_number").Order("created_at desc").First(&queueNum).Error
if err != nil && err != gorm.ErrRecordNotFound {
tx.Rollback()
return err
}
pat.QueueNumber = queueNum + 1
// 2. write patient data into the db
err = tx.Create(pat).Error
if err != nil {
tx.Rollback()
return err
}
return tx.Commit().Error
}
正如@O所說。 瓊斯,事務不會在這里保存您,因為您正在提取列的最大值,在數據庫之外遞增它,然后保存該新值。 從數據庫的角度來看,更新的值不依賴於查詢的值。
您可以嘗試在單個查詢中進行更新,這將使依賴性變得明顯:
UPDATE patient AS p
JOIN (
SELECT max(queue_number) AS queue_number FROM patient WHERE registration_id = ?
) maxp
SET p.queue_number = maxp.queue_number + 1
WHERE id = ?
在 gorm 中,您無法運行這樣的復雜更新,因此您需要使用Exec
。
我不是 100% 肯定上述方法會起作用,因為我不太熟悉 MySQL 事務隔離保證。
總的來說,保留一個隊列表(通過reference_id)和一個你原子更新的計數器會更干凈:
開始交易,然后
SELECT queue_number FROM queues WHERE registration_id = ? FOR UPDATE;
增加應用代碼中的隊列號,然后
UPDATE queues SET queue_number = ? WHERE registration_id = ?;
現在,您可以在事務提交之前在患者創建/更新中使用遞增的隊列號。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.