Skip to content

Failed messages which get retried are not sent to the failure transport again after a UnrecoverableMessageHandlingException occurs #35459

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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
chq81 opened this issue Jan 24, 2020 · 5 comments

Comments

@chq81
Copy link

chq81 commented Jan 24, 2020

Symfony version(s) affected: 4.4.2 upwards and 5.0.*

Description
The current implementation of the failure handling of messages prevents re-tried failed messages through the "bin/console messenger:failed:retry"-Command to be sent to the failure transport again when an UnrecoverableMessageHandlingException is thrown in the handler.

How to reproduce
If a message fails and is put into the failure transport, a SentToFailureMessageStamp is added. As this stamp is not deleted from the message headers upon retrying, it will never be put into the failure transport a second time, at least when an UnrecoverableMessageHandlingException occurs.
It will get sent to the failure transport when it is supposed to be retried through a retry mechanism, as the SendFailedMessageForRetryListener sends the failed message back into the failure transport, as this is the receiver which is added to the worker at launch time.

@chq81
Copy link
Author

chq81 commented Jan 24, 2020

This issue is related to this one: #35449

I found this issue which explains the reason the failed message does not get sent into the failure transport once it fails again without being retried: #33685

Can someone clarify why we do not want retried messages which are failing again being sent to the failure transport again? For example I have remote API which is down, the message gets retried for a number of times, then fails. I try it again, lets say a day later, it gets retried once, then I lose the message entirely. Would be really thankful for someone giving me feedback on that.

@Nyholm
Copy link
Member

Nyholm commented Jan 26, 2020

The UnrecoverableMessageHandlingException implements UnrecoverableExceptionInterface. Reading from that interface's description:

Marker interface for exceptions to indicate that handling a message will continue to fail.
If something goes wrong while handling a message that's received from a transport and the message should not be retried, a handler can throw such an exception.

So the definition of using this exception is that they should not be retired because it is impossible to recover.

For your example with the remote API, that does not seam to be a "Unrecoverable Message Handling Exception", we just need to wait a while until the API comes back up. So make sure you dont throw a UnrecoverableMessageHandlingException.

@Nyholm Nyholm closed this as completed Jan 26, 2020
@chq81
Copy link
Author

chq81 commented Jan 29, 2020

@Nyholm Thanks for answering. Currently, when a message is handled the first time and a UnrecoverableMessageHandlingException occurs, the message is still sent to the failure transport. Reading your explanation, I understand that the message should never reach the failure transport, as it can be retried from there. Shouldn't that be changed then?

@Nyholm
Copy link
Member

Nyholm commented Jan 29, 2020

Currently, when a message is handled the first time and a UnrecoverableMessageHandlingException occurs, the message is still sent to the failure transport.

Are you sure about that?

@chq81
Copy link
Author

chq81 commented Jan 29, 2020

Yes, I am.
The message does not get retried, that's true, as the mechanism in the SendFailedMessageForRetryListener prevents that.

private function shouldRetry(\Throwable $e, Envelope $envelope, RetryStrategyInterface $retryStrategy): bool
    {
        // if ALL nested Exceptions are an instance of UnrecoverableExceptionInterface we should not retry
        if ($e instanceof HandlerFailedException) {
            $shouldNotRetry = true;
            foreach ($e->getNestedExceptions() as $nestedException) {
                if (!$nestedException instanceof UnrecoverableExceptionInterface) {
                    $shouldNotRetry = false;
                    break;
                }
            }
            if ($shouldNotRetry) {
                return false;
            }
        }

        if ($e instanceof UnrecoverableExceptionInterface) {
            return false;
        }

        return $retryStrategy->isRetryable($envelope);
    }

But the SendFailedMessageToFailureTransportListener still acts on it, as it is currently not interested in the type of the exception.

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

No branches or pull requests

4 participants