1

我尝试对 PostgreSql 使用带有 Doctrine ORM 的悲观锁定。具有默认配置的 Doctrine 和 PostgreSql(没有任何更改)。

这是代码示例(Symfony 命令)。

$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

实际结果

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

问题

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

4

1 回答 1

1

首先:如何在PESSIMISTIC_WRITEPostgreSql 平台上工作

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

就我而言,我启动了两个进程,第二个进程等待完成第一个进程。这是正确的行为。

我的错误:我正在探索 Doctrine 源代码并找到PessimisticLockException课程。所以,我决定 Doctrine 在使用悲观锁时抛出这个异常。但是这个类在 Doctrine 的任何地方都没有使用。

那么,我是如何解决这个问题的。

我当前的实现需要锁定行的 nowait 行为。而 PostgreSql 9.5 有这个特性 - SKIP LOCKED。但是 Doctrine 没有实现这个功能。

我们能做什么?

我们可以覆盖教义 postgresql 平台类。

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

并设置全部教义配置

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

就这样。现在我们可以不用等待就可以使用悲观锁了。

于 2016-11-15T13:23:12.410 回答