-
-
Notifications
You must be signed in to change notification settings - Fork 9.7k
[Messenger] Allow fine-grained control in log severity of failures #44120
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -64,4 +64,36 @@ function ($exception) use ($exceptionClassName) { | |
) | ||
); | ||
} | ||
|
||
/** | ||
* Determine if failure was unrecoverable. | ||
* | ||
* If ALL nested Exceptions are an instance of UnrecoverableExceptionInterface the failure is unrecoverable | ||
*/ | ||
public function isUnrecoverable(): bool | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure this business logic should belong to this I suggest using the existing |
||
{ | ||
foreach ($this->exceptions as $nestedException) { | ||
if (!$nestedException instanceof UnrecoverableExceptionInterface) { | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
/** | ||
* Determine if failure was recoverable. | ||
* | ||
* If one or more nested Exceptions is an instance of RecoverableExceptionInterface the failure is recoverable | ||
*/ | ||
public function isRecoverable(): bool | ||
{ | ||
foreach ($this->exceptions as $nestedException) { | ||
if ($nestedException instanceof RecoverableExceptionInterface) { | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\Messenger\Retry; | ||
|
||
use Psr\Log\LogLevel; | ||
use Symfony\Component\Messenger\Envelope; | ||
use Symfony\Component\Messenger\Exception\HandlerFailedException; | ||
use Symfony\Component\Messenger\Exception\RecoverableExceptionInterface; | ||
use Symfony\Component\Messenger\Exception\UnrecoverableExceptionInterface; | ||
use Symfony\Component\Messenger\Stamp\RedeliveryStamp; | ||
|
||
/** | ||
* Trait for retry strategies containing default log severity rules. | ||
*/ | ||
trait RetryRecoveryBehaviorTrait | ||
{ | ||
/** | ||
* @param \Throwable|null $throwable The cause of the failed handling | ||
*/ | ||
public function isRetryable(Envelope $message, \Throwable $throwable = null): bool | ||
{ | ||
if (null !== $throwable) { | ||
// A failure is either unrecoverable, recoverable or neutral | ||
if ($this->isUnrecoverable($throwable)) { | ||
return false; | ||
} | ||
|
||
if ($this->isRecoverable($throwable)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the |
||
return true; | ||
} | ||
} | ||
|
||
$retries = RedeliveryStamp::getRetryCountFromEnvelope($message); | ||
|
||
return $retries < $this->maxRetries; | ||
} | ||
|
||
/** | ||
* @return string The \Psr\Log\LogLevel log severity | ||
*/ | ||
public function getLogSeverity(Envelope $message, \Throwable $throwable = null): string | ||
{ | ||
return $this->isRetryable($message, $throwable) | ||
? LogLevel::WARNING | ||
: LogLevel::CRITICAL; | ||
} | ||
|
||
/** | ||
* Determine if exception was unrecoverable. | ||
* | ||
* Unrecoverable exceptions should never be retried | ||
*/ | ||
private function isUnrecoverable(\Throwable $throwable): bool | ||
{ | ||
return ($throwable instanceof HandlerFailedException) | ||
? $throwable->isUnrecoverable() | ||
: $throwable instanceof UnrecoverableExceptionInterface; | ||
} | ||
|
||
/** | ||
* Determine if exception was recoverable. | ||
* | ||
* Recoverable exceptions should always be retried | ||
*/ | ||
private function isRecoverable(\Throwable $throwable): bool | ||
{ | ||
return ($throwable instanceof HandlerFailedException) | ||
? $throwable->isRecoverable() | ||
: $throwable instanceof RecoverableExceptionInterface; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ | |
* @author Fabien Potencier <fabien@symfony.com> | ||
* @author Grégoire Pineau <lyrixx@lyrixx.info> | ||
* @author Ryan Weaver <ryan@symfonycasts.com> | ||
* @author Joris Steyn <symfony@j0r1s.nl> | ||
*/ | ||
interface RetryStrategyInterface | ||
{ | ||
|
@@ -31,4 +32,9 @@ public function isRetryable(Envelope $message, \Throwable $throwable = null): bo | |
* @return int The time to delay/wait in milliseconds | ||
*/ | ||
public function getWaitingTime(Envelope $message, \Throwable $throwable = null): int; | ||
|
||
/** | ||
* @return string The \Psr\Log\LogLevel log severity | ||
*/ | ||
public function getLogSeverity(Envelope $message, \Throwable $throwable = null): string; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that's a BC break you must provide migration path in lower versions And, I've not convinced this method should be part of this interface |
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be reverted.
You moved the logic about Recoverability from
SendFailedMessageForRetryListener
to theMultiplierRetryStrategy
but here, the retryretryStrategy
is anRetryStrategyInterface
. If people inject another instance, the behavior will not be preserved.