在守护进程中使用主义ODM的问题

我想谈谈我在一个相对较小的PHP项目中使用Doctrine ODM的经验,该项目的主要代码库集中在守护进程中。通常,我们如何将教义ODM固定到Yii2。我马上警告您-这个故事将非常繁琐,并且只有在Daemon进程中使用Doctrine ODM时遇到问题的人才会很感兴趣。


我们将在哪里拧


大约6年前,他们将任务委托给我:将一个完整Yii1的代码分割为一段代码,再转换为一个单独的服务。说到做到。该框架没有选择很长时间,除了Yii我什么都不知道,然后他们宣布即将发布Yii2,该版本当时看起来非常时尚/流行/年轻,但是了解该框架的第一个版本使我们可以降低进入第二个版本的门槛。 MongoDB也很流行,我们已经很熟悉它,并且Yii1使用了单独的扩展,但是Yii2它是开箱即用的。


最初,微服务非常简单:有必要从用户那里接收文件并将其存储在持久性存储中,在数据库中保留这些文件的记录,发布有关这些文件的信息并响应通过API的相关请求链接到它们。


随着时间的流逝,这种微服务开始增长:视频处理,病毒检查,跨不同区域的分布式存储(CDN命名)出现了。时间过得很快,该项目积极发展,甚至进行了重构,但是在某些时候,由于新趋势和专业发展,与该项目进行交互时也会出现一些不适感。从事该项目的愿望越来越少,我认真考虑。


感到不适的原因之一是无法方便地使用嵌入式文档。不便之处在于我不得不处理本地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();
    });
}


通常,会话机制非常方便。至少他解决了我们所有的问题。当然,最好也使用声明式方法来管理会话,例如在Spring Framework中所做的那样。这将以冗余代码的形式显着减少开销,但是目前我什至无法想象如何在PHP中完成此工作。


All Articles