Doctrine (postgresql) Pessimistic Locking - 不会抛出 PessimisticLockException
Doctrine (postgresql) Pessimistic Locking - doesn't throw PessimisticLockException
我尝试将悲观锁与 Doctrine ORM 一起用于 PostgreSql。
具有默认配置的 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
实际结果
第二个命令等待第一个事务提交,然后更新行。
问题
如果行现在被锁定,我应该怎么做才能让 Doctrine 抛出 PessimisticLockException
?
首先: PostgreSql 平台的工作方式PESSIMISTIC_WRITE
PESSIMISTIC_WRITE - 这是查询 SELECT ... FOR UPDATE
。此查询锁定所选行和请求同一行的其他连接,等待当前连接完成它的工作。
在我的例子中,我启动了两个进程,第二个等待完成第一个。这是正确的行为。
我的错误: 我正在探索 Doctrine 源代码并找到 PessimisticLockException
class。所以,我决定 Doctrine 在使用悲观锁时抛出这个异常。但是这个 class 没有在 Doctrine 的任何地方使用。
那么,我是如何解决这个问题的。
我当前的实现需要锁定行的 nowait 行为。并且 PostgreSql 9.5 具有此功能 - SKIP LOCKED。但是 Doctrine 没有这个特性的实现。
我们能做什么?
我们可以覆盖学说 postgresql 平台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
并设置 tot 学说配置
#app/config/config.yml
doctrine:
dbal:
connections:
mmi:
driver: pdo_pgsql
host: ...
...
platform_service: 'mmi.dbal.pgsql_platform'
就是这样。现在我们可以不用等待就可以使用悲观锁了
我尝试将悲观锁与 Doctrine ORM 一起用于 PostgreSql。 具有默认配置的 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
实际结果
第二个命令等待第一个事务提交,然后更新行。
问题
如果行现在被锁定,我应该怎么做才能让 Doctrine 抛出 PessimisticLockException
?
首先: PostgreSql 平台的工作方式PESSIMISTIC_WRITE
PESSIMISTIC_WRITE - 这是查询 SELECT ... FOR UPDATE
。此查询锁定所选行和请求同一行的其他连接,等待当前连接完成它的工作。
在我的例子中,我启动了两个进程,第二个等待完成第一个。这是正确的行为。
我的错误: 我正在探索 Doctrine 源代码并找到 PessimisticLockException
class。所以,我决定 Doctrine 在使用悲观锁时抛出这个异常。但是这个 class 没有在 Doctrine 的任何地方使用。
那么,我是如何解决这个问题的。
我当前的实现需要锁定行的 nowait 行为。并且 PostgreSql 9.5 具有此功能 - SKIP LOCKED。但是 Doctrine 没有这个特性的实现。
我们能做什么?
我们可以覆盖学说 postgresql 平台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
并设置 tot 学说配置
#app/config/config.yml
doctrine:
dbal:
connections:
mmi:
driver: pdo_pgsql
host: ...
...
platform_service: 'mmi.dbal.pgsql_platform'
就是这样。现在我们可以不用等待就可以使用悲观锁了