简体   繁体   English

使用DBIx :: Class :: ResultSet的find_or_create方法时如何避免竞争条件?

[英]How to avoid race conditions when using the find_or_create method of DBIx::Class::ResultSet?

From the documentation for find_or_create : find_or_create的文档中:

Note: Because find_or_create() reads from the database and then possibly inserts based on the result, this method is subject to a race condition. 注意:因为find_or_create()从数据库读取,然后可能根据结果插入,所以此方法受竞争条件的影响。 Another process could create a record in the table after the find has completed and before the create has started. 查找完成后且开始创建之前,另一个过程可以在表中创建记录。 To avoid this problem, use find_or_create() inside a transaction. 为避免此问题,请在事务内使用find_or_create()。

Is it enough to just use find_or_create() inside a transaction in PostgreSQL? 仅在PostgreSQL中的事务内使用find_or_create()就足够了吗?

No, the documentation is incorrect. 不,文档不正确。 Using a transaction alone does not avoid this problem. 单独使用事务无法避免此问题。 It only guarantees that the whole transaction is rolled back if an exception should occur - so that no inconsistent state will be persisted to the database. 它仅保证在发生异常时将回滚整个事务-这样就不会将不一致的状态持久化到数据库中。

To avoid this problem you must lock the table - inside a transaction, because all locks are released at the end of a transaction. 避免此问题,您必须锁定表-在事务内部,因为所有锁定都在事务结束时释放。 Something like: 就像是:

BEGIN;
LOCK TABLE mytbl IN SHARE MODE;

-- do your find_or_create here

COMMIT;

But that's not a magic cure for everything. 但这并不是万能的灵丹妙药。 It can become a performance problem, and there may be deadlocks (concurrent transactions mutually trying to lock resources that the other one has locked already). 这可能会成为性能问题,并且可能会出现死锁 (并发事务相互尝试锁定另一个已经锁定的资源)。 PostgreSQL will detect such a condition and cancel all but one of the competing transactions. PostgreSQL将检测到这种情况,并取消所有竞争事务,但其中之一除外。 You must be prepared to retry the operation on failure. 您必须准备在失败时重试该操作。

The PostgreSQL manual about locks. PostgreSQL有关锁的手册。

If you don't have a lot of concurrency you might also just ignore the problem. 如果您没有太多的并发性,那么您也可以忽略该问题。 The time slot is very tiny so it only very rarely actually happens. 时隙很小,因此实际上很少发生。 If you catch the duplicate key violation error, which will do no harm, then you have covered this, too. 如果您发现重复的键冲突错误,这不会造成任何危害,那么您也已解决了这一问题。

This implementation of find_or_create should prevent the race condition, described in the OP: find_or_create此实现应防止出现竞争情况,如OP中所述:

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

It also reduces find_or_create() to a single query in the best case. 在最佳情况下,它还将find_or_create()为单个查询。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM