从文档find_or_create
:
注意:由于find_or_create()从数据库中读出,然后可能根据该结果插入,该方法是受争用条件。 另一种方法可以在表中创建一个记录查找完成后和前创建已启动。 为了避免此问题,使用find_or_create()在事务内。
它是不够的,只是使用find_or_create()
PostgreSQL中事务中?
从文档find_or_create
:
注意:由于find_or_create()从数据库中读出,然后可能根据该结果插入,该方法是受争用条件。 另一种方法可以在表中创建一个记录查找完成后和前创建已启动。 为了避免此问题,使用find_or_create()在事务内。
它是不够的,只是使用find_or_create()
PostgreSQL中事务中?
不,文档不正确。 单独使用交易不回避这个问题。 它只是保证了整个事务回滚如果发生异常 - 所以没有不一致的状态将持久化到数据库。
为了避免这个问题,你必须锁定表-在一个事务中,因为所有的锁在事务结束时释放。 就像是:
BEGIN;
LOCK TABLE mytbl IN SHARE MODE;
-- do your find_or_create here
COMMIT;
但是,这不是一个神奇的治愈一切。 它可以成为一个性能问题,并且有可能是死锁 (相互试图锁定了另外一个已经锁定资源并发事务)。 PostgreSQL将侦测这样的条件并取消所有,但竞争的事务之一。 你必须准备重试失败的操作。
PostgreSQL的手册有关锁。
如果你没有很多并发的也可能只是忽略的问题。 时隙是非常小的,因此只有很少实际情况。 如果赶上重复键冲突错误,这将不伤害,那么你已经把这个。
此实现find_or_create
应防止竞争条件,在OP描述:
eval {
$row = $self->model->create( { ... } );
}
if($@ && $@ =~ /duplicate/i) {
$row = $self->model->find( { ... } );
}
它也能减少find_or_create()
在最好的情况下,单一的查询。