Я пытаюсь использовать пессимистическую блокировку с 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
если строка сейчас заблокирована?
Для начала: Как работает 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'
Это все. Теперь мы можем использовать пессимистическую блокировку без ожидания.
Других решений пока нет …