繁忙應用程序中的API客戶端正在爭奪現有資源。他們一次請求1或2,然後嘗試對這些記錄採取行動。我試圖使用事務來保護狀態,但是無法獲得行鎖的清晰畫面,尤其是在嵌套事務(我猜是保存點,因爲PG不是確實在事務內執行事務?)。ActiveRecord和Postgres行鎖定
的過程應該是這樣的:
- 請求N3資源
- 從池中刪除這些資源,以防止其他用戶試圖要求他們
- 與這些資源
- 卷執行操作如果發生錯誤,則返回整個事務並返回資源池
(As sume所有例子的快樂路徑。請求總是導致退貨產品)
一個版本看起來是這樣的:
def self.do_it(request_count)
Product.transaction do
locked_products = Product.where(state: 'available').lock('FOR UPDATE').limit(request_count).to_a
Product.where(id: locked_products.map(&:id)).update_all(state: 'locked')
do_something(locked_products)
end
end
在我看來,我們能有對第一線死鎖如果兩個用戶請求2,只有3可用。因此,要解決它,我想做...
def self.do_it(request_count)
Product.transaction do
locked_products = []
request_count.times do
Product.transaction(requires_new: true) do
locked_product = Product.where(state: 'available').lock('FOR UPDATE').limit(1).first
locked_product.update!(state: 'locked')
locked_products << locked_product
end
end
do_something(locked_products)
end
end
但是,從我已經成功地在網上找到的,是內部事務的end
不會釋放行級鎖 - 他們只會當最外面的交易結束時被釋放。
最後,我認爲是這樣的:
def self.do_it(request_count)
locked_products = []
request_count.times do
Product.transaction do
locked_product = Product.where(state: 'available').lock('FOR UPDATE').limit(1).first
locked_product.update!(state: 'locked')
locked_products << locked_product
end
end
Product.transaction { do_something(locked_products) }
ensure
evaluate_and_cleanup(locked_products)
end
這給了我兩個完全獨立的交易之後的第三執行的動作,但我不得不做人工檢查(或者我能救)如果do_something
失敗,這使事情變得更加混亂。如果有人在交易中致電do_it
,這也可能導致死鎖,這很可能發生。
所以我的大問題:
- 是我行鎖釋放的理解是否正確?只有在最外層事務處理關閉時纔會釋放嵌套事務中的行鎖定?
- 是否有一個命令可以在不關閉事務的情況下更改鎖定類型?
我的小問題:
有一些在這裏建立或完全明顯的模式是正在跳躍出來給別人更多的三立處理呢?