[英]How to avoid race conditions when using the find_or_create method of DBIx::Class::ResultSet?
从find_or_create
的文档中:
注意:因为find_or_create()从数据库读取,然后可能根据结果插入,所以此方法受竞争条件的影响。 查找完成后且开始创建之前,另一个过程可以在表中创建记录。 为避免此问题,请在事务内使用find_or_create()。
仅在PostgreSQL中的事务内使用find_or_create()
就足够了吗?
不,文档不正确。 单独使用事务无法避免此问题。 它仅保证在发生异常时将回滚整个事务-这样就不会将不一致的状态持久化到数据库中。
为避免此问题,您必须锁定表-在事务内部,因为所有锁定都在事务结束时释放。 就像是:
BEGIN;
LOCK TABLE mytbl IN SHARE MODE;
-- do your find_or_create here
COMMIT;
但这并不是万能的灵丹妙药。 这可能会成为性能问题,并且可能会出现死锁 (并发事务相互尝试锁定另一个已经锁定的资源)。 PostgreSQL将检测到这种情况,并取消所有竞争事务,但其中之一除外。 您必须准备在失败时重试该操作。
如果您没有太多的并发性,那么您也可以忽略该问题。 时隙很小,因此实际上很少发生。 如果您发现重复的键冲突错误,这不会造成任何危害,那么您也已解决了这一问题。
find_or_create
此实现应防止出现竞争情况,如OP中所述:
eval {
$row = $self->model->create( { ... } );
}
if($@ && $@ =~ /duplicate/i) {
$row = $self->model->find( { ... } );
}
在最佳情况下,它还将find_or_create()
为单个查询。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.