symfony/mercure-bundle

MercureBundle crashes when the request takes to long / to high load?

Closed this issue · 6 comments

Under heavy load the published crashes with a timeout...

{
    "class": "Symfony\\Component\\HttpClient\\Exception\\TransportException",
    "message": "Reading from the response stream reached the idle timeout.",
    "code": 0,
    "file": "/.../vendor/symfony/http-client/Chunk/ErrorChunk.php:56",
    "trace": [
        "/.../vendor/symfony/http-client/Response/CurlResponse.php:125",
        "/.../vendor/symfony/http-client/Response/ResponseTrait.php:97",
        "/.../vendor/symfony/mercure/src/Publisher.php:62",
        "/.../src/Service/Doctrine/PublishMercureCollectionUpdatesListener.php:196",
        "/.../src/Service/Doctrine/PublishMercureCollectionUpdatesListener.php:115",
        "/.../vendor/symfony/doctrine-bridge/ContainerAwareEventManager.php:55",
        "/.../vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:3397",
        "/.../vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:432",
        "/.../vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:359",
        "/.../var/cache/prod/ContainerVraVAlR/EntityManager_9a5be93.php:83",
        "/.../src/Controller/ApiOperations/AnswerSet/SubmitAnswerSet.php:144",
        "/.../vendor/symfony/http-kernel/HttpKernel.php:151",
        "/.../vendor/symfony/http-kernel/HttpKernel.php:68",
        "/.../vendor/symfony/http-kernel/Kernel.php:198",
        "/.../public/index.php:28"
    ]

It looks like your hub isn't responding. Then it's "intended" that the bundle throws.

I know, but I can't have it like that... So how can I "hack" it so it doesnt throw?

I'm trying to override it, but the "final" tag is complicating things...

I would suggest to use the messenger integration (in async mode), then it will crash "silently", and the worker will retry later.

Another solution is just to use try/catch where you call the publisher (you can tweak the timeout etc by passing options to the Symfony HttpClient).

Okay, thanks to my previous hack I managed to "silence" the error to just log a warning instead... for the next user, I'm hooking into API-Platforms Mercure publish logic:

But I don't really understand why "everything" have to be final these days, it makes it much harder to tweak things, like the Publisher that just have a single __invoke function... why final?

    App\Service\Doctrine\PublishMercureCollectionUpdatesListener:
        tags: [{name: doctrine.event_subscriber, connection: default, priority: 150}]
        decorates: 'api_platform.doctrine.listener.mercure.publish'
        arguments:
            $formats: "%api_platform.formats%"
            $messageBus: null
            $publisher: "@mercure.hub.default.publisher"
            $logger: "@logger"

Marking final classes in public libraries makes the maintenance way easier for us (and limits the number of potential BC breaks when you upgrade). Also, using composition instead of inheritance is considered a best practice these days.

Yup, I understand that as a guideline,

And I agree about composition over inheritance, and API-surface etc.

But If I need to change the way Publisher pushes messages, adding information or similar, I need a place to hook in.

And overriding 'api_platform.doctrine.listener.mercure.publish' just because it's not final is not ideal. Ofcourse, that means I can publish my own Mercure-Bundle to handle the integrations, and it depends on how flexible you want this library to be. I would love to contribute to MercureBundle instead of competing (I'm still working on how to publish updates to collection in a good way my current way is failing with a timeout because of way to much unnecessary traffic!).

I just don't understand why a simple class like Publisher needs to be final, especially since it just need a invoke method that takes a Update object...

anyway, thanks for helping out so fast, I really appreciate it :)