Race Condition при включенной автогенерации гидраторов Doctrine ORM
Как-то раз столкнулись с очень странной ошибкой. Периодически на продакшене вылетал то один то другой запрос с ошибкой PHP Fatal error: Class ‘Hydrators\VendorDefaultBundleDocumentUserHydrator’ not found in /web/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php.
Проблема
Что было очень странно, так как файл гидратора спокойно лежал по этому пути. При проверке на develop-машине подобный баг не воспроизводился, только под нагрузкой. Довольно долго гадали, в чём там дело, пока наш доблестный админ не обратил внимание на время изменение файлов гидраторов. Оно постоянно менялось на текущее. А это значит, что гидраторы постоянно перезаписывались. Не буду говорить, как это фигово с точки зрения производительности. Но кроме того, при наличии двух одновременных запросов, первый из них начинал генерировать гидратор, а второй не находил файл и падал. Главное условие одновременность запросов.
Решение
Солюшен прост. Надо сделать так, чтобы на боевом сервере прокси классы и гидраторы генерировались один раз при деплое, а потом не перегенерялись при запросах. Примерно так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$autoGenerateDoctrine = (APPLICATION_ENV == 'production')? false : true; // Для Doctrine 2 ORM $config = new \Doctrine\ORM\Configuration(); $config->setAutoGenerateProxyClasses($autoGenerateDoctrine); $connectionOptions = array(...); $evm = new \Doctrine\Common\EventManager(); $em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config, $evm); // Для Doctrine 2 ODM $config = new \Doctrine\ODM\MongoDB\Configuration(); $config->setAutoGenerateProxyClasses($autoGenerateDoctrine); $config->setAutoGenerateHydratorClasses($autoGenerateDoctrine); $mongoConfigOptions = ...; $dm = \Doctrine\ODM\MongoDB\DocumentManager::create(new \Doctrine\MongoDB\Connection($dsn, $mongoConfigOptions), $config); |
После этого гидраторы перестали перегенеряться и ошибка пропала.