ProblÚmes d'utilisation de Doctrine ODM dans les processus démon

Je veux parler de mon expérience en utilisant Doctrine ODM dans un projet PHP relativement petit dans lequel la base de code principale est concentrée dans les processus démon. Et en général, comment nous avons attaché Doctrine ODM à Yii2. Je vous préviens tout de suite - l'histoire sera trÚs fastidieuse et trÚs probablement intéressante uniquement pour ceux qui ont déjà rencontré des problÚmes lors de l'utilisation de Doctrine ODM dans les processus démon.


OĂč nous allons visser


Il y a environ 6 ans, ils m'ont confiĂ© la tĂąche: couper d'un monolithe en un Yii1morceau de code dans un service sĂ©parĂ©. À peine dit que c'Ă©tait fait. Le framework n'a pas choisi depuis longtemps, sauf que Yiije ne savais rien, puis ils ont annoncĂ© une sortie imminente Yii2, qui Ă  l'Ă©poque semblait trĂšs Ă©lĂ©gante / Ă  la mode / jeune, mais la connaissance de la premiĂšre version de ce framework nous a permis de rĂ©duire le seuil d'entrĂ©e dans la deuxiĂšme version. MongoDB Ă©tait Ă©galement Ă  la mode, avec lequel nous Ă©tions dĂ©jĂ  familiers et travaillions Yii1avec une extension distincte, mais Yii2il Ă©tait pris en charge dĂšs le dĂ©part.


Initialement, le microservice était assez simple: il était nécessaire de recevoir des fichiers des utilisateurs et de les stocker dans un stockage persistant, de conserver des enregistrements de ces fichiers dans la base de données, d'émettre des informations concernant ces fichiers et des liens vers eux en réponse aux demandes pertinentes via l'API.


Au fil du temps, ce micro-service a commencĂ© Ă  se dĂ©velopper: le traitement vidĂ©o, la dĂ©tection de virus, le stockage distribuĂ© dans diffĂ©rentes rĂ©gions (Ă  la CDN) sont apparus. Le temps a passĂ© rapidement, le projet s'est activement dĂ©veloppĂ© et a mĂȘme subi une refactorisation, mais Ă  un moment donnĂ©, en raison des nouvelles tendances et de la croissance professionnelle, un sentiment d'inconfort est apparu lors de l'interaction avec ce projet. Le dĂ©sir de travailler sur ce projet est devenu de moins en moins et j'ai sĂ©rieusement pensĂ©.


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


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


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



, , MongoDB.
Symfony Doctrine ORM, Doctrine ODM — ORM 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 gĂ©nĂ©ral, le mĂ©canisme de session s'est avĂ©rĂ© assez pratique. Au moins, il a rĂ©solu tous nos problĂšmes. Bien sĂ»r, il serait bien de gĂ©rer Ă©galement les sessions en utilisant une approche dĂ©clarative, comme c'est le cas dans Spring Framework, par exemple. Cela rĂ©duirait considĂ©rablement les frais gĂ©nĂ©raux sous forme de code redondant, mais pour le moment je ne peux mĂȘme pas imaginer comment cela peut ĂȘtre fait en PHP.


All Articles