2010-09-10 28 views
1

我有一個特例模型,它不應該成爲外部事務的一部分:如何從ActiveRecord中的事務中排除模型?

Outer.Transaction do 
    ... 

    Inner.create(:blah) 

    ... 
end 

如何停止內成爲交易的一部分,假設內竟然一無所知哪些具體事務是越來越被拉進?

例如,創建內部交易是不行的,因爲這也將成爲外部交易的一部分。

我想這樣做,因爲內部模型需要立即寫入,而不是等待外部事務提交。

回答

1

我很好奇什麼會需要這樣的結構!

我想你會努力做到這一點,沒有像你所描述的一些駭客。例如,您可以在mysql中將Inner的表存儲類型設置爲不支持事務(MyIsam例如)的表存儲類型,同時將其他類的表存儲爲支持事務(YUK!)的某些內容。

如果可以的話,你幾乎可以肯定會更好延遲Inner.create,直到交易完成後。您可以使用begin來確保始終發生創建。喜歡的東西:

create_inner = false 
begin 
    Outer.transaction.do 
    ... 
    create_inner = true # instead of Inner.create(:blah) 
    ... 
    end 
ensure 
    if create_inner 
    Inner.create(:blah) 
    end 
end 

這將成爲,如果你的塊的其餘部分依賴於創建Inner情況更加複雜。您可以在塊中創建實例,並在塊的末尾將created_inner設置爲false,這樣,如果代碼無例外地運行,它將在事務中創建,並且您不會再在確保中創建。

如果您想在一般情況下執行此操作,您可以在Inner上定義類方法以執行塊,但始終會創建Inner對象。您還需要將after_create添加到Inner。當事務成功時,您將依靠塊中的Inner.create調用來創建它,但如果它回滾了,則需要在之後創建它。例如:

class Inner < ActiveRecord::Base 

    def self.ensure_created(&block) 
    Thread.current[:created_inner] = false   
    begin 
     block.call 
    rescue => e 
     if Thread.current[:created_inner] 
     Inner.create(:blah) 
     end 
     raise e 
    end 
    end 

    def after_create 
    # Flag that an instance has been created in this thread so 
    # that if we rollback out of a transaction we can create again 
    Thread.current[:created_inner] = true 
    end 

你會然後調用它像:

Inner.ensure_created do 
    Outer.transaction do 
    ... 
    Inner.create(:blah) 
    ... 
    end 
end 

然而,有很多不足之處,以本辦法的,我不知道我會主張它。這很複雜。如果引發ActiveRecord :: Rollback,則異常將不會冒出Outer.transaction,但會導致不會創建Inner實例。當兩個或多個調用嵌套時,它將無法正常工作。最後我還沒有徹底測試 - 謹慎使用!

+0

感謝您的建議!爲了回答他的場景問題,其基本上用於鎖定批量作業任務,其中「內部」是鎖定模型。如果有兩批試圖在第二批時發生故障而不是排隊等候,或者可能會堆積如山。外部事務實際上是停止發生這種故障,導致每個鎖定輪流等待。 – 2010-09-10 19:17:39

+0

我並沒有因爲提供的任何建議而結束,但我將其標記爲「接受的答案」,因爲這是最好的嘗試。多謝你們! – 2010-09-27 04:55:13

+0

@greg malcolm:如果你沒有遵循任何建議......你做了什麼? – marcgg 2010-10-07 12:37:54

0

您可以爲Inner定義一個單獨的數據庫連接,然後事務將只應用於Outer的連接。

相關問題