2011-05-06 93 views
4

我試圖使用ActiveRecord的find_or_create_by_*column*,但我從Postgres得到的錯誤讓我知道它偶爾無法找到模型,並試圖插入一個反正。保持這張表的獨特性非常重要,所以我在其遷移中添加了:unique => true屬性,以便Postgres知道我對此很認真。find_or_create競爭條件

而且,失敗:

ActiveRecord::StatementInvalid: PGError: ERROR: duplicate key value violates unique constraint "index_marketo_leads_on_person_id" DETAIL: Key (person_id)=(9968932) already exists. : INSERT INTO "marketo_leads" ("mkt_person_id", "synced_at", "person_updated_at", "person_id") VALUES(NULL, NULL, '2011-05-06 12:57:02.447018', 9968932) RETURNING "id"

我有車型像這樣:

class User < AR::Base 
    has_one :marketo_lead 

    before_save :update_marketo_lead 

    def update_marketo_lead 
    if marketo_lead 
     if (User.marketo_columns & self.changes.keys).any? 
     marketo_lead.touch(:person_updated_at) 
     end 
    elsif self.id 
     marketo_lead = MarketoLead.find_or_create_by_person_id(:person_updated_at => Time.now, :person_id => self.id) 
    end 
    end 
end 

class MarketoLead 
    belongs_to :user, :foreign_key => 'person_id' 
end 

第二個模型用於我們的用戶賬戶鏈接到的Marketo電子郵件服務器,並保持記錄上次修改用戶的某些字段時,我們可以推送已批量後臺任務中已更改的記錄。

我想不出任何此回調的原因,update_marketo_lead失敗,除了某種我無法想象的競爭條件。

(請忽略 '用戶' 與 '人' 共享一個主鍵的horribleness) (使用Rails 2.3.11,Postgres的9.0.3)

回答

5

它很可能是find_or_create被執行時,匹配爲person_id沒有找到,因此使用了創建邏輯,但是可能在find_or_create和實際user.save之間,另一個請求設法完成保存事務,並且此時您的數據庫約束導致此異常。

我會建議是捕捉StatementInvalid異常,重試儲蓄(高達有限次數...

begin 
    user.save! 
rescue ActiveRecord::StatementInvalid => error 
    @save_retry_count = (@save_retry_count || 5) 
    retry if((@save_retry_count -= 1) > 0) 
    raise error 
end 

注意這應該被執行,無論你嘗試保存用戶。所有的回調函數並驗證發生在保存!交易

PS Im我假設您的版本的軌道支持交易:)在Rails 3中,它不必包裝保存!在交易中,因爲它已經在內部使用了一個

+0

在編輯我的應用程序的東西,觸發user.save是不是真的實用,我很欣賞的好建議挽救StatementInvalid錯誤並添加有限重試循環。我在'find_or_create'調用中添加了這樣的異常處理。乾杯! – nessur 2011-05-09 17:13:21

+0

在我的應用程序中,模型。ave是由控制器觸發的,所以它很容易包裝在開始救援中。事實上,我在需要這個 – 2011-05-09 18:14:20

+0

P.S的模型中添加了一個名爲save_with_retries(retry_count = 5)的方法。我刪除了user.save中的Transaction,因爲ActiveRecord(根據事務文檔)已經包裝了save,save!並破壞內部交易。 – 2011-05-09 18:16:08

0

我在一個搭檔工作中碰到了這個問題,重試並重復獲取錯誤並最終自行清理。我不相信它是來自另一個請求的競爭條件,或者它會非常罕見,只發生一次或兩次,但不是像我所看到的那樣連續11次。我發現的最好的解釋是在博客文章here。要點是postgres保留一個內部存儲的值,用於增加主鍵的不確定性。這對我來說是正確的,因爲我設置的是主鍵,而不是僅僅使用遞增的值,所以也許就是這樣。從上面的鏈接評論的解決方案似乎是叫ActiveRecord::Base.connection.reset_pk_sequence!(table_name)

我無法驗證這還因爲我無法攝製的問題,但我試圖修復,從弗拉基米爾修復上面修改爲:

begin 
    user.save! 
rescue ActiveRecord::StatementInvalid => error 
    @save_retry_count = (@save_retry_count || 1) 
    ActiveRecord::Base.connection.reset_pk_sequence!(:user) 
    retry if((@save_retry_count -= 1) >= 0) 
    raise error 
end 

因此,如果這沒有解決它的第一次嘗試,我會看到一個錯誤引發