我尝试对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
?
答案 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锁而无需等待。