Doctrine(postgresql)悲观锁定 - 不会抛出PessimisticLockException

时间:2016-11-14 10:15:42

标签: php postgresql doctrine-orm locking pessimistic

我尝试对PostgreSql使用Pessimistic Locking和Doctrine ORM。 带有默认配置的Doctrine和PostgreSql(没有任何更改)。

这是代码示例(Symfony Command)。

$sleep - 这是以秒为单位的时间

$manager = $this->getContainer()->get('mmi.manager.message');
$conn = $manager->em()->getConnection();

$manager->em()->getConnection()->beginTransaction();
try {
    $entity = $manager->repo()->find('cd7eb9e9', LockMode::PESSIMISTIC_WRITE);

    $entity->setState(EntityActionInterface::STATE_IN_PROGRESS);
    $manager->em()->persist($entity);
    $manager->em()->flush();

    $ts = (new \DateTime())->getTimestamp();
    $output->writeln("TS: {$ts}");

    if ($sleep) {
        $output->writeln("Sleep: {$sleep}");
        sleep($sleep);
    }

    $entity->setMessage([$ts]);
    $manager->em()->persist($entity);
    $manager->em()->flush();

    $conn->commit();
} catch (PessimisticLockException $ex) {
    var_dump(get_class($ex));

    $conn->rollBack();
    throw $ex;
} catch (\Exception $ex) {
    var_dump(get_class($ex));

    $conn->rollBack();
    throw $ex;
}

如何测试

运行两个命令。第一个命令以超时20秒运行。第二个命令没有任何超时运行。

预期结果

第二个命令抛出PessimisticLockException

实际结果

第二个命令等待第一个事务提交,然后更新行。

问题

如果行现在被锁定,我应该怎么做才能使Doctrine抛出PessimisticLockException

1 个答案:

答案 0 :(得分:1)

首先:如何为PostgreSql平台工作PESSIMISTIC_WRITE

PESSIMISTIC_WRITE - 这是查询SELECT ... FOR UPDATE。此查询锁定所选行和其他连接,请求同一行,waitng为当前连接完成它的工作。

在我的情况下,我开始两个进程,第二个等待完成第一个进程。这是正确的行为。

我的错误:我正在探索Doctrine源代码并找到PessimisticLockException类。因此,我决定在使用悲观锁时,Doctrine抛出此异常。但是这个类在Doctrine中没有使用过。

所以,我是如何解决这个问题的。

我当前的实现需要nowait锁定行的行为。 PostgreSql 9.5具有此功能 - SKIP LOCKED。但是,Doctrine没有实现此功能。

我们可以做些什么?

我们可以覆盖doctrine postgresql platfrom class。

use Doctrine\DBAL\Platforms\PostgreSqlPlatform;

class PgSqlPlatform extends PostgreSqlPlatform
{
    /**
     * Returns the FOR UPDATE expression.
     *
     * @return string
     */
    public function getForUpdateSQL()
    {
        return 'FOR UPDATE SKIP LOCKED';
    }
}

将其定义为服务

#app/config/services.yml
services:
    mmi.dbal.pgsql_platform:
        class: {Namespace}\PgSqlPlatform

并设置为doctrine config

#app/config/config.yml
doctrine:
    dbal:
        connections:
            mmi:
                driver:   pdo_pgsql
                host:     ...
                ...
                platform_service: 'mmi.dbal.pgsql_platform'

这就是全部。现在我们可以使用Pessimistic锁而无需等待。