从文档find_or_create
:如何在使用DBIx :: Class :: ResultSet的find_or_create方法时避免竞争条件?
注意:由于find_or_create()从数据库中读取信息,然后基于该结果 可能插入,该方法是受a种族 条件。在 查找已完成且在创建开始之前,另一个进程可以在表中创建一条记录。为避免 这个问题,请在事务中使用find_or_create()。
在PostgreSQL的事务中使用find_or_create()
足够吗?
从文档find_or_create
:如何在使用DBIx :: Class :: ResultSet的find_or_create方法时避免竞争条件?
注意:由于find_or_create()从数据库中读取信息,然后基于该结果 可能插入,该方法是受a种族 条件。在 查找已完成且在创建开始之前,另一个进程可以在表中创建一条记录。为避免 这个问题,请在事务中使用find_or_create()。
在PostgreSQL的事务中使用find_or_create()
足够吗?
不,文档不正确。单独使用交易而不是避免此问题。它只能保证如果发生异常时整个事务会回滚 - 这样就不会有任何不一致的状态持久存在数据库中。
到避免这个问题你必须锁定表 - 在一个事务中,因为所有的锁都在事务结束时被释放。类似于:
BEGIN;
LOCK TABLE mytbl IN SHARE MODE;
-- do your find_or_create here
COMMIT;
但是,这并不是万能的万能药。它可能会成为一个性能问题,并且可能有死锁(并发事务相互试图锁定另一个已经锁定的资源)。 PostgreSQL将检测到这种情况并取消除竞争事务之外的所有事务。您必须准备好在失败时重试操作。
The PostgreSQL manual about locks.
如果你没有很多并发的也可能只是忽略的问题。时隙非常小,所以它实际上很少发生。如果您捕获重复的密钥违规错误,这将不会造成任何损害,那么您也已经介绍了这一点。
这个实现的find_or_create
应防止竞争条件,在OP描述:
eval {
$row = $self->model->create({ ... });
}
if([email protected] && [email protected] =~ /duplicate/i) {
$row = $self->model->find({ ... });
}
它还减少find_or_create()
在最好的情况下,单一的查询。
这反转了逻辑。但是现在你有一个很小的时间段,其中的条目可以被删除 - 在这种情况下,逻辑将失败。试图写第一个比尝试阅读更昂贵。所以这只是一个改进,如果重复是非常罕见的。不管怎样,冲突应该是非常罕见的,因为时间间隔很小。 – 2012-04-22 13:35:50
其他有用的页面。 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
但是,在DBIC中捕捉“重复密钥违规错误”的正确方法是什么? – 2012-10-15 21:37:21
@eugeney:我想你会为此提出一个新问题,而不是评论。你总是可以链接到这一个,以节省自己的一些打字。 – 2012-10-15 22:09:19