Problemas al usar Doctrine ODM en procesos Daemon

Quiero hablar sobre mi experiencia usando Doctrine ODM en un proyecto PHP relativamente pequeño en el que la base del código principal se concentra en los procesos de daemon. Y en general, cómo fijamos Doctrine ODM a Yii2. Te advierto de inmediato: la historia será muy aburrida y probablemente solo interesante para aquellos que ya han encontrado problemas al trabajar con Doctrine ODM en los procesos de daemon.


Donde vamos a atornillar


Hace unos 6 años, me confiaron la tarea: cortar de un monolito en un Yii1código en un servicio separado. Dicho y hecho. El marco no se eligió por mucho tiempo, excepto Yiique no sabía nada, y luego anunciaron un lanzamiento inminente Yii2, que en ese momento parecía muy elegante / a la moda / juvenil, pero el conocimiento de la primera versión de este marco permitió reducir el umbral para ingresar a la segunda versión. MongoDB también estaba de moda, con lo que ya estábamos familiarizados y trabajamos Yii1con una extensión separada, pero Yii2fue compatible de inmediato.


Inicialmente, el microservicio era bastante simple: era necesario recibir archivos de los usuarios y almacenarlos en un almacenamiento persistente, mantener registros de estos archivos en la base de datos, emitir información sobre estos archivos y enlaces a ellos en respuesta a solicitudes relevantes a través de la API.


Con el tiempo, este microservicio comenzó a crecer: apareció el procesamiento de video, la detección de virus, el almacenamiento distribuido en diferentes regiones (a la CDN). El tiempo pasó rápidamente, el proyecto se desarrolló activamente e incluso se refactorizó, pero en algún momento, debido a las nuevas tendencias y al crecimiento profesional, también apareció una sensación de incomodidad al interactuar con este proyecto. El deseo de trabajar en este proyecto se hizo cada vez menos y pensé seriamente.


Embedded Document . , PHP . , , , . , , . , .


PHP 7- , . ActiveRecord Yii2 — "" , , phpdoc. MongoDB, - , - {user_id : '12'}


, . - , Yii2 . , , , , , .



, , MongoDB.
Symfony Doctrine ORM, Doctrine ODMORM MongoDB.


Yii2 Symfony , , :
- — , , .
- — ActiveRecord " " Yii2


, ActiveRecord Doctrine ODM, . , ORM .


. Doctrine DocumentManager ( EntityManager Doctrine ORM). " ".


Dependency Injection Container Yii2 , .


Yii2 DataProvider'a. -, CRUD- . " " . , Yii2. , , — , .


, Doctrine UnitOfWork , .



, : DI- , . - , Symfony .


, :


while (!$this->isShuttingDown()) {
    //  Task,  ,     
    //  MANAGED    ,
    //     
    $task = $this->taskProvider->getNextTask();
    try {
        //   -  , ,  
        $this->someService->runTask($task);
    } catch (SomeException $e) {
        //    :       
        //     MANAGED,      
        //       -     
        $this->switchTaskToError($task, $e);
    }
}

, - dirty ( flush() ), , , .


clear() "" . , , . clear() , , , $this->someService->runTask($task);



, ( , — ), . . , , . , , . .


:


while (!$this->isShuttingDown()) {
    //  ,  , 
    //   -  ,   
    //    detached,      , 
    //      
    $task = $this->taskProvider->getNextTask();
    try {
        //   -     
        $this->someService->runTask($task);
    } catch (SomeException $e) {
        //    ,  -   
        $this->switchTaskToError($task, $e);
    }
}

taskProvider someService , .


merge. runTask() :


public function runTask(Task $task) {
    $dm = $thic->dmFactory->createDocumentManager();
    $task = $dm->merge($task);
    ....
    $task->setStatus(Task::STATUS_OK);
    $dm->flush();
}

switchTaskToError() :


public function switchTaskToError(Task $task, Throwable $) {
    $dm = $this->dmFactory->createDocumentManager();
    $task = $dm->merge($task);
    $task->setStatus(Task::STATUS_ERROR);
    $task->setError($e->getMessage());
    $dm->flush();
}

, $dm .


, , - . , :


$tasks = $this->taskProvider->getProblemTasks();
$dm = $thic->dmFactory->createDocumentManager();
foreach ($tasks as $task) {
     $task = $dm->merge($task);
     $task->setState(..);
}
$dm->flush();

getProblemTasks , , "" , Task merge(). , merge() — .. , .


, , :


$dm = $thic->dmFactory->createDocumentManager();
$tasks = $this->taskProvider->getProblemTasks($dm);
foreach ($tasks as $task) {
     $task->setState(..);
}
$dm->flush();

.


JPA


, Doctrine JPA: , UnitOfWork, (managed, detached ..). JPA , , . , — . (Spring Framework ). JPA , , .


, , . - , , - (? ), ( ). , , .


, hibernate Doctrine.


Doctrine ODM


" doctrine" , - http , , , .


, . , . . , :


$session = $this->sessionManager->createNewSession();
try {
    $dm= $session->getDocumentManager();
    $tasks = $this->taskProvider->getProblemTasks();
    foreach ($tasks as $task) {
        $task->setState(..);
    }
    $dm->flush();
} finally {
    $this->sessionManager->close($session);
}

getProblemTasks


public function getProblemTasks(): array
{
    $repo = $this->sessionManager
                      ->getCurrentSession()
                      ->getDocumentManager()
                      ->getRepository(Task::class);

    return $repo->findBy(['status' => Task::STATUS_ERROR]);
}

, , , , .


:


public function switchTaskToError(Task $task, Throwable $) {
    //   ,  ,    
    $session $this->sessionManager->createNewSession();
    $dm = $session->getDocumentManager();
    try {
        $task = $dm->merge($task);
        $task->setStatus(Task::STATUS_ERROR);
        $task->setError($e->getMessage());
        $dm->flush();
    finally {
        $this->sessionManager->close($session);
    }
}

, , , wrap, , .


, 3 SessionManager


createNewSession — , "" .
getCurrentSession — .
close


, ( php-ds).


:
createNewSession — , .
getCurrentSession — , , .
close — .


, , , .


wrap, , :


public function switchTaskToError(Task $task, Throwable $) {
    $this->sessionManager->wrapBySession(function(DocumentManager $dm) use ($task, $e) {
        $task = $dm->merge($task);
        $task->setStatus(Task::STATUS_ERROR);
        $task->setError($e->getMessage());
        $dm->flush();
    });
}


En general, el mecanismo de la sesión resultó ser bastante conveniente. Al menos resolvió todos nuestros problemas. Por supuesto, sería bueno administrar también las sesiones utilizando un enfoque declarativo, como se hace en Spring Framework, por ejemplo. Esto reduciría significativamente la sobrecarga en forma de código redundante, pero por el momento ni siquiera puedo imaginar cómo se puede hacer esto en PHP.


All Articles