Довольно интересный факт, который я только что обнаружил, когда испортил приложение WebSocket, которое я сейчас создаю с использованием Symfony 4.1 и https://github.com/GeniusesOfSymfony/WebSocketBundle (который построен на PHP Ratchet).
Я хотел периодически получать новейшие данные из базы данных MySQL для целей тестирования с использованием $repository->find(2);, но он всегда возвращал один и тот же результат, хотя я менял данные во время подписки на канал WebSocket.
После многих часов возни с кодом и слез я обнаружил, что Doctrine по какой-то причине кэширует результаты (или, как мне кажется, именно так).
Чтобы проверить свою теорию, я создал сервис, который обрабатывает выборку из базы данных со следующим кодом:
/**
* @return mixed
* @throws \Doctrine\DBAL\DBALException
*/
public function fetchNewest()
{
$stmt = $this->em->getConnection()->prepare('SELECT * FROM test WHERE id=2');
$stmt->execute();
return $stmt->fetch();
}
и по какой-то причине это сработало. Может ли кто-нибудь объяснить, почему метод find(2) не дает новейших данных, в то время как необработанный SQL дает?






Мне и самому удалось в этом разобраться.
Метод Doctrine EntityManager find(...) фактически кэширует результат внутри частного поля с именем $unitOfWork под именем объекта и идентификатором записи.
Каждый раз, когда вы пытаетесь найти что-то с помощью $em->find(...), и это удается, результат сохраняется внутри $this->unitOfWork, и если вы снова попытаетесь получить то же самое, он просто загрузится из кеша.
// Check identity map first
if (($entity = $unitOfWork->tryGetById($sortedId, $class->rootEntityName)) !== false) {
if ( ! ($entity instanceof $class->name)) {
return null;
}
switch (true) {
case LockMode::OPTIMISTIC === $lockMode:
$this->lock($entity, $lockMode, $lockVersion);
break;
case LockMode::NONE === $lockMode:
case LockMode::PESSIMISTIC_READ === $lockMode:
case LockMode::PESSIMISTIC_WRITE === $lockMode:
$persister = $unitOfWork->getEntityPersister($class->name);
$persister->refresh($sortedId, $entity, $lockMode);
break;
}
return $entity; // Hit!
}
Это случай "symfony/orm-pack": "^1.0" и "doctrine/orm": "^2.5.11"
Редактировать:
Вызов $repository->find(2, LockMode::NONE); решает проблему.
Это правильное и ожидаемое поведение ORM. Это способ облегчить нагрузку на базу данных.
Это хорошо задокументировано, но в большинстве случаев мы просто переходим к примерам и пропускаем их. :)
Обычно вы не замечаете этого в приложении из-за жизненного цикла PHP. Приходит запрос, отключается ответ и все отключается. Следующий запрос начинается с чистого листа.
Если вы используете веб-сокет, этого никогда не произойдет, PHP просто работает вечно (не совсем, но в идеале), а храповик реагирует на события.
(ВЫКЛ: проверьте PHP-PM ... они смогли добиться резкого увеличения количества запросов, потому что они подорвали жизненный цикл PHP).
Режим блокировки - это решение, но вы также можете вызвать $em->refresh($entity), который запускает перезагрузку из базы данных.
Обновлено: документы https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/working-with-objects.html#entities-and-the-identity-map
Я не уверен, но думаю, что сначала заметил это у пользователя rabbitmq. Действительно легко пропустить в простом приложении. :)
Да, забавно, что я пропустил такое поведение, работая с этими небольшими HTTP-запросами, лол. Спасибо!