Why is Symfony messenger sending a message twice before rejecting?

98 Views Asked by At

I have implemented a Message Transport protocol in a Symfony-Project, that implements the TransportInterface. While playing around with it and learning how it works, I came across a weird issue that I like to elaborate on and understand more:

When a message is missing a corresponding message handler, the Message Transport will first be called with the method send, followed by a method call to reject.

I would assume, that the method reject will be executed only. Why is the message being sent once more? I figured out, that it has nothing to do with the setting of max_retries.

My custom Message Transport is doing the following:

  1. get: it reads the message from a custom table inside the get-method.
  2. send: it just logs "send" into the console with the corresponding envelope
  3. reject: it just logs "reject" into the console with the corresponding envelope

What I am doing for testing is:

  1. I write manually a message into the message-table, which automatically causes the get method to retrieve the corresponding message
  2. messenger tries to find a suitable message handler and finds nothing because there is none yet
  3. messenger is printing to the console:

07:58:06 INFO [messenger] Received message App\Message\IngestMessage ["class" => "App\Message\IngestMessage"]

07:58:06 WARNING [messenger] Error thrown while handling message App\Message\IngestMessage. Sending for retry #1 using 1000 ms delay. Error: "No handler for message "App\Message\IngestMessage"." ["class" => "App\Message\IngestMessage","retryCount" => 1,"delay" => 1000,"error" => "No handler for message "App\Message\IngestMessage".","exception" => Symfony\Component\Messenger\Exception\NoHandlerForMessageException^ { …}]

  1. then it logs "SEND" to the console because the send-method will be executed which is doing console.log("SEND")
  2. it logs "REJECT" to the console because the reject-method will be executed which is doing console.log("REJECT")

Code:

messenger.yml:

framework:
    messenger:
        transports:
             async: '%env(MESSENGER_TRANSPORT_DSN)%'
             ingest:
                dsn: '%env(MESSENGER_TRANSPORT_DSN_INGEST)%'
                retry_strategy:
                  max_retries: 5
                  delay: 1000
                  multiplier: 2
                  max_delay: 0
        routing:
             'App\Message\IngestMessage': ingest

IngestDataTransport.php:

class IngestDataTransport implements TransportInterface
{

    public function __construct(private readonly DataBucketManager $dataBucketManager)
    {}

    public function get(): iterable
    {
        $dto = $this->dataBucketManager->pop();

        echo ".";
        if(!$dto) {
            return [];
        }

        dump("GET", $dto);

        $event = IngestMessage::fromDataBucketDTO($dto);
        $envelop = new Envelope($event);

        return [$envelop];
    }

    public function ack(Envelope $envelope): void
    {
        // TODO: Implement ack() method.
        dump('ACK');
    }

    public function reject(Envelope $envelope): void
    {
        // TODO: Implement reject() method.
        dump('REJECT', $envelope->getMessage());
    }

    public function send(Envelope $envelope): Envelope
    {
        dump('SEND', $envelope->getMessage());

        return $envelope;
    }
}

Once I implement a corresponding message handler, neither the SEND-method nor the REJECT-method is executed, which makes total sense. However, why comes reject with a send? It would always duplicate a failed message.

0

There are 0 best solutions below