php-pm / php-pm-httpkernel

HttpKernel adapter for use of Symfony and Laravel frameworks with PHP-PM

Repository from Github https://github.comphp-pm/php-pm-httpkernelRepository from Github https://github.comphp-pm/php-pm-httpkernel

Cannot capture some exceptions

chapuzzo opened this issue · comments

We've been trying to configure php-pm and this adapter with our symfony 4 app for some time.
Our current problem is that some exceptions get out of our control, eg:

    /**
     * @Route("/mark-invoice-as-final/{id}")
     */
    public function markInvoiceAsFinal(string $id)
    {
        try {
            $this->invoiceService->makeFinal($id);

            return new Response('success');
        } catch (\Exception | \Error $e) {
            return new Response('error');
        }
    }

When I reach that route with xxx (clearly not an uuid) instead of showing the appropriate error the server returns
Slave returned an invalid HTTP response. Maybe the script has called exit() prematurely?

Why are those errors not going through the try/catch set up?
Is there some config or setup issue that coud cause that?

This is our docker log stack trace:

An exception was thrown by the bridge. Forcing restart of the worker. The exception was: Doctrine\DBAL\Types\ConversionException: Could not convert database value "xxx" to Doctrine Type uuid in /var/www/vhosts/my-cool-project.com/facturacion/vendor/doctrine/dbal/lib/Doctrine/DBAL/Types/ConversionException.php:55
facturacion_1  | Stack trace:
facturacion_1  | #0 /var/www/vhosts/my-cool-project.com/facturacion/vendor/ramsey/uuid-doctrine/src/UuidType.php(94): Doctrine\DBAL\Types\ConversionException::conversionFailed('xxx', 'uuid')
facturacion_1  | #1 /var/www/vhosts/my-cool-project.com/facturacion/vendor/symfony/doctrine-bridge/DataCollector/DoctrineDataCollector.php(149): Ramsey\Uuid\Doctrine\UuidType->convertToDatabaseValue('xxx', Object(Doctrine\DBAL\Platforms\MariaDb1027Platform))
facturacion_1  | #2 /var/www/vhosts/my-cool-project.com/facturacion/vendor/symfony/doctrine-bridge/DataCollector/DoctrineDataCollector.php(125): Symfony\Bridge\Doctrine\DataCollector\DoctrineDataCollector->sanitizeQuery('default', Array)
facturacion_1  | #3 /var/www/vhosts/my-cool-project.com/facturacion/vendor/symfony/doctrine-bridge/DataCollector/DoctrineDataCollector.php(62): Symfony\Bridge\Doctrine\DataCollector\DoctrineDataCollector->sanitizeQueries('default', Array)
facturacion_1  | #4 /var/www/vhosts/my-cool-project.com/facturacion/vendor/doctrine/doctrine-bundle/DataCollector/DoctrineDataCollector.php(42): Symfony\Bridge\Doctrine\DataCollector\DoctrineDataCollector->collect(Object(Symfony\Component\HttpFoundation\Request), Object(Symfony\Component\HttpFoundation\Response), NULL)
facturacion_1  | #5 /var/www/vhosts/my-cool-project.com/facturacion/vendor/symfony/http-kernel/Profiler/Profiler.php(166): Doctrine\Bundle\DoctrineBundle\DataCollector\DoctrineDataCollector->collect(Object(Symfony\Component\HttpFoundation\Request), Object(Symfony\Component\HttpFoundation\Response), NULL)
facturacion_1  | #6 /var/www/vhosts/my-cool-project.com/facturacion/vendor/symfony/http-kernel/EventListener/ProfilerListener.php(91): Symfony\Component\HttpKernel\Profiler\Profiler->collect(Object(Symfony\Component\HttpFoundation\Request), Object(Symfony\Component\HttpFoundation\Response), NULL)
facturacion_1  | #7 /var/www/vhosts/my-cool-project.com/facturacion/vendor/symfony/event-dispatcher/Debug/WrappedListener.php(104): Symfony\Component\HttpKernel\EventListener\ProfilerListener->onKernelResponse(Object(Symfony\Component\HttpKernel\Event\FilterResponseEvent), 'kernel.response', Object(Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher))
facturacion_1  | #8 /var/www/vhosts/my-cool-project.com/facturacion/vendor/symfony/event-dispatcher/EventDispatcher.php(212): Symfony\Component\EventDispatcher\Debug\WrappedListener->__invoke(Object(Symfony\Component\HttpKernel\Event\FilterResponseEvent), 'kernel.response', Object(Symfony\Component\EventDispatcher\EventDispatcher))
facturacion_1  | #9 /var/www/vhosts/my-cool-project.com/facturacion/vendor/symfony/event-dispatcher/EventDispatcher.php(44): Symfony\Component\EventDispatcher\EventDispatcher->doDispatch(Array, 'kernel.response', Object(Symfony\Component\HttpKernel\Event\FilterResponseEvent))
facturacion_1  | #10 /var/www/vhosts/my-cool-project.com/facturacion/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php(141): Symfony\Component\EventDispatcher\EventDispatcher->dispatch('kernel.response', Object(Symfony\Component\HttpKernel\Event\FilterResponseEvent))
facturacion_1  | #11 /var/www/vhosts/my-cool-project.com/facturacion/vendor/symfony/http-kernel/HttpKernel.php(187): Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher->dispatch('kernel.response', Object(Symfony\Component\HttpKernel\Event\FilterResponseEvent))
facturacion_1  | #12 /var/www/vhosts/my-cool-project.com/facturacion/vendor/symfony/http-kernel/HttpKernel.php(169): Symfony\Component\HttpKernel\HttpKernel->filterResponse(Object(Symfony\Component\HttpFoundation\Response), Object(Symfony\Component\HttpFoundation\Request), 1)
facturacion_1  | #13 /var/www/vhosts/my-cool-project.com/facturacion/vendor/symfony/http-kernel/HttpKernel.php(66): Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object(Symfony\Component\HttpFoundation\Request), 1)
facturacion_1  | #14 /var/www/vhosts/my-cool-project.com/facturacion/vendor/symfony/http-kernel/Kernel.php(188): Symfony\Component\HttpKernel\HttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
facturacion_1  | #15 /vendor/php-pm/httpkernel-adapter/Bridges/HttpKernel.php(87): Symfony\Component\HttpKernel\Kernel->handle(Object(Symfony\Component\HttpFoundation\Request))
facturacion_1  | #16 /vendor/php-pm/php-pm/src/ProcessSlave.php(433): PHPPM\Bridges\HttpKernel->handle(Object(React\Http\Io\ServerRequest))
facturacion_1  | #17 /vendor/php-pm/php-pm/src/ProcessSlave.php(393): PHPPM\ProcessSlave->handleRequest(Object(React\Http\Io\ServerRequest))
facturacion_1  | #18 /vendor/react/http/src/Io/MiddlewareRunner.php(49): PHPPM\ProcessSlave->onRequest(Object(React\Http\Io\ServerRequest))
facturacion_1  | #19 /vendor/react/http/src/Io/MiddlewareRunner.php(54): React\Http\Io\MiddlewareRunner->call(Object(React\Http\Io\ServerRequest), 3)
facturacion_1  | #20 /vendor/react/http/src/Middleware/RequestBodyParserMiddleware.php(34): React\Http\Io\MiddlewareRunner->React\Http\Io\{closure}(Object(React\Http\Io\ServerRequest))
facturacion_1  | #21 /vendor/react/http/src/Io/MiddlewareRunner.php(59): React\Http\Middleware\RequestBodyParserMiddleware->__invoke(Object(React\Http\Io\ServerRequest), Object(Closure))
facturacion_1  | #22 /vendor/react/http/src/Io/MiddlewareRunner.php(54): React\Http\Io\MiddlewareRunner->call(Object(React\Http\Io\ServerRequest), 2)
facturacion_1  | #23 /vendor/react/http/src/Middleware/RequestBodyBufferMiddleware.php(44): React\Http\Io\MiddlewareRunner->React\Http\Io\{closure}(Object(React\Http\Io\ServerRequest))
facturacion_1  | #24 /vendor/react/http/src/Io/MiddlewareRunner.php(59): React\Http\Middleware\RequestBodyBufferMiddleware->__invoke(Object(React\Http\Io\ServerRequest), Object(Closure))
facturacion_1  | #25 /vendor/react/http/src/Io/MiddlewareRunner.php(54): React\Http\Io\MiddlewareRunner->call(Object(React\Http\Io\ServerRequest), 1)
facturacion_1  | #26 /vendor/react/http/src/Middleware/LimitConcurrentRequestsMiddleware.php(91): React\Http\Io\MiddlewareRunner->React\Http\Io\{closure}(Object(React\Http\Io\ServerRequest))
facturacion_1  | #27 /vendor/react/http/src/Io/MiddlewareRunner.php(59): React\Http\Middleware\LimitConcurrentRequestsMiddleware->__invoke(Object(React\Http\Io\ServerRequest), Object(Closure))
facturacion_1  | #28 /vendor/react/http/src/Io/MiddlewareRunner.php(40): React\Http\Io\MiddlewareRunner->call(Object(React\Http\Io\ServerRequest), 0)
facturacion_1  | #29 /vendor/react/http/src/StreamingServer.php(237): React\Http\Io\MiddlewareRunner->__invoke(Object(React\Http\Io\ServerRequest))
facturacion_1  | #30 /vendor/react/http/src/StreamingServer.php(176): React\Http\StreamingServer->handleRequest(Object(React\Socket\Connection), Object(React\Http\Io\ServerRequest))
facturacion_1  | #31 /vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(123): React\Http\StreamingServer->React\Http\{closure}(Object(React\Http\Io\ServerRequest), '')
facturacion_1  | #32 /vendor/react/http/src/Io/RequestHeaderParser.php(59): Evenement\EventEmitter->emit('headers', Array)
facturacion_1  | #33 /vendor/react/http/src/Io/RequestHeaderParser.php(47): React\Http\Io\RequestHeaderParser->parseAndEmitRequest(569)
facturacion_1  | #34 /vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(123): React\Http\Io\RequestHeaderParser->feed('GET /api/make-f...')
facturacion_1  | #35 /vendor/react/stream/src/Util.php(71): Evenement\EventEmitter->emit('data', Array)
facturacion_1  | #36 /vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(123): React\Stream\Util::React\Stream\{closure}('GET /api/make-f...')
facturacion_1  | #37 /vendor/react/stream/src/DuplexResourceStream.php(193): Evenement\EventEmitter->emit('data', Array)
facturacion_1  | #38 /vendor/react/event-loop/src/StreamSelectLoop.php(238): React\Stream\DuplexResourceStream->handleData(Resource id #635)
facturacion_1  | #39 /vendor/react/event-loop/src/StreamSelectLoop.php(205): React\EventLoop\StreamSelectLoop->waitForStreamActivity(NULL)
facturacion_1  | #40 /vendor/php-pm/php-pm/src/ProcessSlave.php(352): React\EventLoop\StreamSelectLoop->run()
facturacion_1  | #41 /tmp/dbgSHBcoy(36): PHPPM\ProcessSlave->run()
facturacion_1  | #42 {main}

From a quick glance at the stack trace I don't think this exception is thrown in your controller, but from the profiler, so you cannot catch it there.

If you want to catch everything, regardless od where it's thrown, you can override your Kernel's handle() function and add a try-catch block there.

commented

Seems your exception is thrown here https://github.com/php-pm/php-pm-httpkernel/blob/master/Bridges/HttpKernel.php#L87 and should be handled here https://github.com/php-pm/php-pm/blob/master/src/ProcessSlave.php#L392.
Are you using latest versions of both packages? Should it be \Error or \Throwable? Seems we're not handling \Error?

@andig its not handled because handleRequest calls $this->shutdown on exceptions (https://github.com/php-pm/php-pm/blob/master/src/ProcessSlave.php#L443).

That said, I'm not sure if we should do anything here. If the application does not handle the exception and throws it to the bridge we already do the best we can do (log it and kill the worker to let it reload on the next request). IHMO for this case they should override the app Kernel handle() function and add a catch block there to handle it.