2012-04-21 59 views
5

從文檔find_or_create如何在使用DBIx :: Class :: ResultSet的find_or_create方法時避免競爭條件?

注意:由於find_or_create()從數據庫中讀取信息,然後基於該結果 可能插入,該方法是受a種族 條件。在 查找已完成且在創建開始之前,另一個進程可以在表中創建一條記錄。爲避免 這個問題,請在事務中使用find_or_create()。

在PostgreSQL的事務中使用find_or_create()足夠嗎?

回答

6

不,文檔不正確。單獨使用交易而不是避免此問題。它只能保證如果發生異常時整個事務會回滾 - 這樣就不會有任何不一致的狀態持久存在數據庫中。

避免這個問題你必須鎖定表 - 在一個事務中,因爲所有的鎖都在事務結束時被釋放。類似於:

BEGIN; 
LOCK TABLE mytbl IN SHARE MODE; 

-- do your find_or_create here 

COMMIT; 

但是,這並不是萬能的萬能藥。它可能會成爲一個性能問題,並且可能有死鎖(併發事務相互試圖鎖定另一個已經鎖定的資源)。 PostgreSQL將檢測到這種情況並取消除競爭事務之外的所有事務。您必須準備好在失敗時重試操作。

The PostgreSQL manual about locks.

如果你沒有很多併發的也可能只是忽略的問題。時隙非常小,所以它實際上很少發生。如果您捕獲重複的密鑰違規錯誤,這將不會造成任何損害,那麼您也已經介紹了這一點。

+2

其他有用的頁面。 Docs:http://www.postgresql.org/docs/current/interactive/mvcc.html PostgreSQL 9.1或更高版本的可串行化實現:http://wiki.postgresql.org/wiki/SSI其他隔離級別或PostgreSQL版本:http ://www.postgresql.org/files/developer/concurrency.pdf – kgrittn 2012-04-22 00:27:17

+0

但是,在DBIC中捕捉「重複密鑰違規錯誤」的正確方法是什麼? – 2012-10-15 21:37:21

+0

@eugeney:我想你會爲此提出一個新問題,而不是評論。你總是可以鏈接到這一個,以節省自己的一些打字。 – 2012-10-15 22:09:19

0

這個實現的find_or_create應防止競爭條件,在OP描述:

eval { 
    $row = $self->model->create({ ... }); 
} 
if([email protected] && [email protected] =~ /duplicate/i) { 
    $row = $self->model->find({ ... }); 
} 

它還減少find_or_create()在最好的情況下,單一的查詢。

+1

這反轉了邏輯。但是現在你有一個很小的時間段,其中的條目可以被刪除 - 在這種情況下,邏輯將失敗。試圖寫第一個比嘗試閱讀更昂貴。所以這只是一個改進,如果重複是非常罕見的。不管怎樣,衝突應該是非常罕見的,因爲時間間隔很小。 – 2012-04-22 13:35:50