Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QUESTION] Laravel Octane support #489

Open
filakhtov opened this issue May 6, 2021 · 8 comments
Open

[QUESTION] Laravel Octane support #489

filakhtov opened this issue May 6, 2021 · 8 comments

Comments

@filakhtov
Copy link
Contributor

Hey team 馃憢馃徎 ,

As the title indicates, I would like to check what are the plans (if any) to support Laravel Octane and a concept of long running web worker processes.

As things stand today, Doctrine ORM v.2.x closes an entity manager any time an SQL error occurs. It was designed that way to keep internal state consistent avoid data corruption. It has been a known issue forever and almost five years ago Symfony maintainers suggested to make the entity manager resettable in doctrine/orm#5933.

Unfortunately, it is not possible to achieve what was proposed in a backwards compatible way, so it was scheduled for Doctrine 3.x, and looking at the milestone/roadmap for Doctrine 3.x, I would say it is unlikely coming any time soon, meaning we are stuck with 2.x for years to come.

This problem further amplified with Laravel Octane and using PSR-7 based web server implementation, Swoole or RoadRunner, because they start a long running worker process that will process multiple requests and aim to reuse as many
dependencies as possible to improve performance and reduce request bootstrap time.

As an example, Symfony has solved this by introducing a proxy (see symfony/symfony#19203) for entity manager. I know that a partial solution exists for this library as well, in a form of IlluminateRegistry::resetManager(), however it has issues, such as losing extensions.

With the above context, my question are:

  • are there any plans to introduce proper support for Laravel Octane and long running process model?
  • are you open to discuss possible solutions and working together towards getting there?

Thanks in advance for reading through this issue and providing any feedback!

@eigan
Copy link
Member

eigan commented May 7, 2021

Hi @filakhtov 馃憢馃徎

As of now laravel-doctrine do not have any maintainer that moves the project forward. Almost all contributions are provided from non team members. We are just making sure that pull requests are merged and released. It might appear that we do not even use laravel-doctrine but the reason (for me at least) is that we do not keep up with the laravel packages and features.

So, no there is no plans, however I am active and will answer questions. Sadly I have no time to implement or look for solutions.

I would like to invite you to our slack channel, but seems like our invite-page is down.

@aftabnaveed
Copy link

@filakhtov why not create a pull request? We are using https://github.com/swooletw/laravel-swoole in our project and the way we deal with the ORM Exceptions is that we just reload the swoole worker using Server::reload() that just re-initialises the worker and thus the EntityManager another workaround we did was that on each request we clean it using EntityManager::clean method.

@filakhtov
Copy link
Contributor Author

@aftabnaveed I would have done this for active project, but given the project does not have any committed maintainers and visionaries who is moving this library forward I am very hesitant to use it in production systems.

@aftabnaveed
Copy link

@filakhtov We heavily rely on this for our e-commerce websites and has a team of devs using it. I am happy to help maintain where appropriate. @eigan seems to be very active and helping where possible.

@josenicomaia
Copy link
Member

The slack invite page is my fault. I am fixing it tomorrow.

@seifane-wise
Copy link

seifane-wise commented Feb 25, 2022

@filakhtov Have you tried putting a listener on Octane ?

I'm thinking something like this :

class ResetEntityManager
{
    public function handle(RequestReceived $event): void
    {
        if (!App::getFacadeApplication()->get('em')->isOpen()) {
            $service = new DoctrineServiceProvider(App::getFacadeApplication());
            $service->register();
        }
        EntityManager::clear();
    }
}

And calling it in octane.php.

...
    'listeners' => [
        WorkerStarting::class => [
            EnsureUploadedFilesAreValid::class,
            EnsureUploadedFilesCanBeMoved::class,
        ],

        RequestReceived::class => [
            ...Octane::prepareApplicationForNextOperation(),
            ...Octane::prepareApplicationForNextRequest(),
            Support\Doctrine\Octane\Listeners\ResetEntityManager::class
            //
        ],

        RequestHandled::class => [
...

I am still experimenting with octane but that should solve most of the issues regarding resetting and flushing of the EntityManager (That I can think of).

@superbiche
Copy link

Tried @seifane-wise 's approach but changed the listeners just a bit, so clearing the EntityManager happens after the request is handled:

class EnsureEntityManagerIsOpen
{
    public function handle(RequestReceived|RequestTerminated $event): void
    {
        if (!$event->app->get('em')->isOpen()) {
            $service = new DoctrineServiceProvider($event->app);
            $service->register();
        }
    }
}
class ClearEntityManager
{
    public function handle(): void
    {
        EntityManager::clear();
    }
}
'listeners' => [
    RequestReceived::class => [
        ...Octane::prepareApplicationForNextOperation(),
        ...Octane::prepareApplicationForNextRequest(),
        \App\Doctrine\Octane\Listeners\EnsureEntityManagerIsOpen::class,
        //
    ],
    RequestTerminated::class => [
        // FlushUploadedFiles::class,
        \App\Doctrine\Octane\Listeners\ClearEntityManager::class,
     ],
     TaskReceived::class => [
         ...Octane::prepareApplicationForNextOperation(),
         \App\Doctrine\Octane\Listeners\EnsureEntityManagerIsOpen::class,
     ],
     TaskTerminated::class => [
        \App\Doctrine\Octane\Listeners\ClearEntityManager::class,
     ],

This seems to do the trick when closing manually the EntityManager in RequestTerminated. I didn't make a lot of tests for now but this looks promising.

I guess Swoole users also need to add EnsureEntityManagerIsOpen listener to TickReceived and ClearEntityManager to TickTerminated.

@filakhtov
Copy link
Contributor Author

I just wanted to loop back and say that we have achieved what we wanted by creating a wrapper on top of the EntityManager and provided convenience methods to replace wrapped instance after each request. Just to mention, this has its own implications with losing Doctrine extensions, because those are loaded in the service provider as a separate step.

We have, however, completely abandoned the idea using long running processes with PHP, because the ecosystem isn't just there yet. We have encountered numerous issues with various third party libraries that are written with the assumption of running under traditional PHP model and leaving residual state that later bleeds between requests. As such we deemed that this model is not feasible at scale and will require significant investments in auditing every single dependency we have in the project today, any time its updated or every new dependency introduced for any residual state that could manifest in plethora of different possible ways.

I do believe that PHP could achieve maturity in this area in years to come, but at its current state it's just not ready.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants