Я пишу PHP-функцию, которая хранит / обновляет большие массивы данных в таблице и может вызвать тупик. Я попытался расследовать, как повторить неудачную транзакцию с Doctrine, но, к сожалению, не смог найти какую-либо информацию в Интернете. В конце концов я написал следующий код
$retry = 0; $done = false; while (!$done and $retry < 3) { try { $this->entityManager->flush(); $done = true; } catch (\Exception $e) { sleep(1); $retry++; } } if ($retry == 3) { throw new Exception( "[Exception: MySQL Deadlock] Too many people accessing the server at the same time. Try again in few minutes" ); }
Мой вопрос: есть ли шанс, что этот подход будет вставлять дубликаты в базу данных? если да, то как я могу заставить Doctrine отменить транзакции?
Тупик возвращает ошибку 1213, которую вы должны обрабатывать на стороне клиента
Обратите внимание, что взаимоблокировка и блокировка ждут разные вещи. В тупике нет «неудачной» транзакции: они оба виновны. Нет гарантии, что вы откажетесь.
Вы должны использовать rollback
, код стиля вставляет дубликат. например, вы должны:
$retry = 0; $done = false; $this->entityManager->getConnection()->beginTransaction(); // suspend auto-commit while (!$done and $retry < 3) { try { $this->entityManager->flush(); $this->entityManager->getConnection()->commit(); // commit if succesfull $done = true; } catch (\Exception $e) { $this->entityManager->getConnection()->rollback(); // transaction marked for rollback only $retry++; } }
Надеюсь, эта помощь.
Вот как я имею дело с повторением неудачных транзакций с Sf2.7 и доктриной 2.4.7:
use Doctrine\Bundle\DoctrineBundle\Registry; use Doctrine\ORM\EntityManager; class Foo { /** * @var Registry */ protected $doctrine; public function __construct(Registry $registry) { $this->doctrine = $registry; } protected function doSomething($entity, $attempt) { $em = $this->getEntityManager(); $conn = $em->getConnection(); try{ $conn->beginTransaction(); $entity->setBar("baz"); $em->flush(); $conn->commit(); } catch(\PDOException $e){ $conn->rollBack(); $attempt++; if($attempt <= 3){ $this->doSomething($repayment, $attempt); } } } /** * @return EntityManager */ protected function getEntityManager() { /** @var EntityManager $em */ $em = $this->doctrine->getManager(); if(!$em->isOpen()){ $this->doctrine->resetManager(); $em = $this->doctrine->getManager(); } return $em; } }
Если вы не используете ORM, тогда используйте, что он автоматически управляет ситуацией взаимоблокировки.