【问题标题】:Concurrent requests trying to create the same entity if not exists in Doctrine如果 Doctrine 中不存在,则尝试创建相同实体的并发请求
【发布时间】:2025-12-25 22:55:07
【问题描述】:

我有一个看起来像这样的函数:

function findByAndCreateIfNotExists($criteria){

    $entity = $this->findBy(['criteria'=>$criteria]);

    // this is the problem area, if request 1 is still creating the entity, request 2 won't find it yet.

    if (! $entity) {
        $entity = $this->createEntity($criteria);
    }

    return $entity;
}

这个函数被各种请求使用,我发现并发请求有时会尝试创建同一个实体,抛出一个DBALException抱怨唯一键的重复条目。

我考虑过LOCK TABLES,如下所述: How to lock a whole table in symfony2 with doctrine2?

但鉴于 Doctrine 中没有执行此操作的功能,我猜这不是首选方法。我的问题是,如何防止并发请求尝试创建相同的实体并始终返回正确的实体?

【问题讨论】:

    标签: php mysql symfony doctrine-orm


    【解决方案1】:

    作为想法创建自己的锁定系统:

    类似:

    function findByAndCreateIfNotExists($criteria){
    
        $entity = $this->findBy(['criteria'=>$criteria]);
    
        // this is the problem area, if request 1 is still creating the entity, request 2 won't find it yet.
        $lockPath ='/tmp/'.md5($criteria).'.lock';
    
        if (! $entity && !file_exists($lockPath)) {
            $fh = fopen($lockPath, 'w') or die("Can't create file");
            $entity = $this->createEntity($criteria);
            unlink(($lockPath);
        }
    
        return $entity;
    }
    

    【讨论】:

    • 有趣的想法,但在这种情况下,请求 2 将返回 null,这是我并不真正想要的。我想始终返回目标实体,如果它不存在则创建它。但问题是并发请求多次创建同一实体时失败。
    • 我不知道你的应用程序逻辑到底有多重要,延迟有多重要,但就像想法一样,很容易修改这个解决方案,让并发请求等到第一个进程实现锁定并重新读取实体第二次
    【解决方案2】:

    但从你所说的来看,我认为你做错了方向,最好稍微重建应用程序架构并将 RabbitMQ 队列作为中间点来满足你的请求

    【讨论】:

      最近更新 更多