Doctrine (postgresql) пессимистическая блокировка — не вызывает PessimisticLockException

Я пытаюсь использовать пессимистическую блокировку с Doctrine ORM для PostgreSql.
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

Фактический результат

Вторая команда ждет первой фиксация транзакции а затем обновляет строку.

Вопрос

Что я должен сделать, чтобы бросить Доктрину PessimisticLockException если строка сейчас заблокирована?

1

Решение

Для начала: Как работает PESSIMISTIC_WRITE для платформы PostgreSql

PESSIMISTIC_WRITE — это запрос SELECT ... FOR UPDATE, Этот запрос блокирует выбранную строку и другие соединения, которые запрашивают ту же строку, ожидая, когда текущее соединение завершит свою работу.

В моем случае я запускаю два процесса, а второй ждет завершения первого. И это правильное поведение.

Виноват: я изучаю исходный код доктрины и нахожу PessimisticLockException учебный класс. Итак, я решаю, что Doctrine выдает это исключение, когда используется пессимистическая блокировка. Но этот класс нигде не используется в Учении.

Итак, как я решил эту проблему.

Моя текущая реализация требовала поведения nowait для заблокированных строк. И PostgreSql 9.5 имеет эту функцию — Пропустить заблокирован. Но Doctrine не имеет реализации для этой функции.

Что мы можем сделать?

Мы можем переопределить доктрину postgresql platfrom из класса.

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'

Это все. Теперь мы можем использовать пессимистическую блокировку без ожидания.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]