2013-02-28 76 views
0

夥計們,交易中的ActiveRecord

我是相當新的在軌的ActiveRecord交易,我有一段代碼,在那裏我做這樣的事情:使用事務

transaction do 
    specimen = Specimen.find_by_doc_id(25) 
    specimen.state = "checking" 
    specimen.save 
    result = Inventory.do_check(specimen) 
    if result 
    specimen.state="PASS" 
    else 
    specimen.state="FAIL" 
    end 
    specimen.save 
end 

我的目標在這裏如果我在Inventory.do_check(它是外部Web服務的客戶端並執行一堆HTTP調用和檢查)中遇到異常,那麼我希望specimen.state回滾到之前的值。我想知道這是否會像上面那樣工作?另外,它看起來像在我的開發機器上,鎖在整個Specimen表上設置,當我嘗試查詢該表/模型時,我得到一個BUSY異常(我正在使用SQLLite)。我在想鎖只能在那個對象/記錄上設置。

任何反饋都非常感謝,因爲我說我真的是新來的,所以我的問題可能是非常天真的。

回答

1

實施和鎖定取決於數據庫。我不使用SQLLite,如果在這種情況下鎖定整個表,我不會感到驚訝。但是閱讀應該仍然有效,因爲它可能是因爲它不允許在單個連接上進行兩個併發操作,所以在允許任何其他操作之前等待事務完成。例如,參見這個SO回答:https://stackoverflow.com/a/7154699/2117020

但是,我的主要觀點是,在任何情況下,您都不應該在訪問外部服務時持有交易。然而,它被實現,保持交易秒不是你想要的。在你的情況下,你想要的只是從異常中恢復。您是否僅僅想將狀態設置爲「FAIL」或「initial」,或者do_check()修改了您的標本?如果do_check()不修改樣本,則應該更好地執行如下操作:

specimen = Specimen.find_by_doc_id(25) 
specimen.state="checking" 
specimen.save 
# or simply specimen.update_attribute(:state, "checking") 

begin 
    specimen.state = Inventory.do_check(specimen) ? "PASS" : "FAIL" 
rescue 
    specimen.state = "FAIL" # or "initial" or whatever 
end 
specimen.save 
+1

謝謝。這很有幫助,我不能投票,它說我需要15個聲望。 – user1781472 2013-02-28 21:28:42

0

鎖定將高度依賴於您的數據庫。你可以使用行鎖。事情是這樣的:

specimen = Specimen.find_by_doc_id(25) 

success = true 

# reloads the record and does a select for update which locks the row until the block exits (its wrapped in a transation) 
specimen.with_lock do 
    result = Inventory.do_check(specimen) 
    if(result) 
    specimen.state="PASS" 
    else 
    specimen.state="FAIL" 
    end 
    specimen.save! 
end 

檢查事務中的外部網站是不理想的,但如果你使用with_lock和你的數據庫支持行鎖,你應該只被鎖定這個單行(它會阻止讀取,所以在使用仔細)

看一看在活動記錄的悲觀鎖定文檔: http://ruby-docs.com/docs/ruby_1.9.3-rails_3.2.2/Rails%203.2.2/classes/ActiveRecord/Locking/Pessimistic.html