0

I'm using the Symfony Messenger component in a Symfony 4.4 application. I'm processing the messages async through RabbitMQ and storing the failed ones in a database through the Doctrine transport.

And this is the messenger config:

framework:
    messenger:
        failure_transport: failed

        buses:
            command_bus:
                middleware:
                    - doctrine_ping_connection

        transports:
            failed: 'doctrine://default?queue_name=failed'
            async_priority_high:
                dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
                retry_strategy:
                    delay: 2000
                    max_retries: 5
                    multiplier: 2
                options:
                    exchange:
                        name: high
                    queues:
                        messages_high: ~

            async_priority_low:
                dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
                retry_strategy:
                    delay: 3000
                    max_retries: 3
                    multiplier: 2
                options:
                    exchange:
                        name: low
                    queues:
                        messages_low: ~

        routing:
            'App\SampleMessageButHighPriority': async_priority_high
            'App\SampleMessageInterface': async_priority_low
            'App\OtherMessage': async_priority_low

Here is a sample handler that handler messages imlpementing the SampleMessageInterface interface.

final class SampleMessageHandler implements MessageHandlerInterface
{
    private ProjectRepository $projectRepository;

    public function __construct(ProjectRepository $projectRepository)
    {
        $this->projectRepository = $projectRepository;
    }

    public function __invoke(SampleMessageInterface $message): void
    {
        $project = $this->projectRepository->find($message->getProjectId()->toString());

        if ($project === null) {
            return;
        }

        $this->someProcessor->__invoke($project);
    }
}

Everything is working before facing any message failure. The problem starts showing after failing when trying to retry or show the failed messages. Let's try the php bin/console messenger:failed:show command:

Result:

In PhpSerializer.php line 64:
                                                                               
  Cannot instantiate interface App\SampleMessageInterface                                                            

I guess that Symfony needs to unserialize the failed message, previously serialized and stored in the database, but can't do it because it's an interface.

How can I solve this? Is there any way to serialize the failed messages using the class implementation, not the interface?

Nove
  • 165
  • 2
  • 8
  • Please do not give code into picture. use \`\`\`code\`\`\` to post your code formated – vincent PHILIPPE Aug 19 '20 at 07:12
  • I'm sorry about that, I've just updated my question. Thank you for letting me know. – Nove Aug 19 '20 at 07:17
  • https://github.com/opengento/magento2-gdpr/issues/43 Here it say this is a cache issue. Maybe you have already try to clean the cache ? And why did you use ``final`` key word ? – vincent PHILIPPE Aug 19 '20 at 07:22
  • Doc : https://symfony.com/doc/current/messenger.html#creating-a-message-handler Didn't use final key word – vincent PHILIPPE Aug 19 '20 at 07:28
  • Possible help next config: ``` messenger serializer: default_serializer: "messenger.transport.symfony_serializer" ``` Worked for me. In 4.2 there was it, but after in 4.4 (maybe earlier, didn't check), it changed to phpSerializer as a default. So just set it and should work – Nikita_kharkov_ua Mar 30 '21 at 10:57

1 Answers1

1

Failed messages are stored serialized in the database. When you retry or show these messages they are deserialized.

Simply replace interface SampleMessageInterface to class SampleMessage.

Leprechaun
  • 759
  • 5
  • 14
  • But then how can I benefit from creating only one handler to handle all the ```SampleMessageInterface``` messages? Or it's impossible, and I should start creating one handler for each ```SampleMessageInterface``` implementation? – Nove Aug 20 '20 at 11:55
  • You can, but look like there is a bug in messenger's failed transport feature. Could you please check the database with failed messages and say what is `type` in `headers` column? Is it `SampleMessageInterface` or something else? – Leprechaun Aug 20 '20 at 12:54
  • It's ```SampleMessageInterface```, so I guess this is why it's not able to unserialize to the dispatched message imlpementation. I don't know if it's a bug or the expected behavior. – Nove Aug 20 '20 at 13:08
  • I think it's a bug. Shouldn't be an issue to serialize with the actual class name, not interface name. – Leprechaun Aug 20 '20 at 13:44
  • 1
    I've just reported it, let's see what Symfony people say. Thanks! – Nove Aug 21 '20 at 07:36