Skip to content

Conversation

@valtzu
Copy link
Contributor

@valtzu valtzu commented Nov 2, 2025

Q A
Branch? 7.4
Bug fix? no
New feature? yes
Deprecations? no
License MIT

Sometimes you only know if a message should be retried or not when dispatching the message, e.g. RunCommandMessage with one command should be retried, but not with another command. I'm proposing to add a stamp & decorator strategy to make that possible.

Examples

$this->bus->dispatch(new RunCommandMessage('do:not:retry'), [new RetryStrategyStamp(retryable: false)]);
$this->bus->dispatch(new RunCommandMessage('always:retry'), [new RetryStrategyStamp(retryable: true)]);
$this->bus->dispatch(new RunCommandMessage('always:retry:custom-delay'), [new RetryStrategyStamp(retryable: true, waitingTime: 10_000)]);
$this->bus->dispatch(new RunCommandMessage('retry:custom-delay'), [new RetryStrategyStamp(waitingTime: 10_000)]);

or e.g. if a single message class should never be retried:

#[AsMessage]
class NeverRetryThisMessage implements DefaultStampsProviderInterface
{
    // ...

    public function getDefaultStamps(): array
    {
        return [new RetryStrategyStamp(retryable: false)];
    }
}

It is worth noting that RecoverableMessageInterface & UnrecoverableExceptionInterface would still have higher priority than this stamp.


This is somewhat related to #62056 & could be useful for #50462 to allow chain-specific retry behavior.

@carsonbot carsonbot added this to the 7.4 milestone Nov 2, 2025
@carsonbot carsonbot changed the title [Messenger][FrameworkBundle] Add RetryStrategyStamp [FrameworkBundle][Messenger] Add RetryStrategyStamp Nov 2, 2025
@valtzu valtzu force-pushed the dynamic-retry-strategy branch from 1fa3c11 to adec243 Compare November 2, 2025 15:58
*/
final class DynamicRetryStrategy implements RetryStrategyInterface
{
public function __construct(private RetryStrategyInterface $fallbackStrategy)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public function __construct(private RetryStrategyInterface $fallbackStrategy)
public function __construct(private readonly RetryStrategyInterface $fallbackStrategy)

Comment on lines +21 to +35
public function __construct(
private ?bool $retryable = null,
private ?int $waitingTime = null,
) {
}

public function isRetryable(): ?bool
{
return $this->retryable;
}

public function getWaitingTime(): ?int
{
return $this->waitingTime;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public function __construct(
private ?bool $retryable = null,
private ?int $waitingTime = null,
) {
}
public function isRetryable(): ?bool
{
return $this->retryable;
}
public function getWaitingTime(): ?int
{
return $this->waitingTime;
}
public function __construct(
public readonly ?bool $retryable = null,
public readonly ?int $waitingTime = null,
) {
}

Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about the name: this doesn't configure the strategy - just the retriability
Also, when retryable = true I'm not sure this makes sense
NoRetryStamp might be more appropriate for disabling retries.

But then: you example about RunCommandMessage makes me think this is a concern that belongs to the message - not to the stamp. The question is: from the pov of messenger, should a failure of the command mean that the message wasn't handled successfully? It depends if the failure comes from the command being unable to run because eg it failed to start (missing binary, etc) or because the message yields to an non-zero exit code. Which is the case you want to skip.

Alternative idea: make this a concern of RunCommandMessage (and RunProcessMessage) - eg with status codes to consider OK from the pov of messenger.

@valtzu
Copy link
Contributor Author

valtzu commented Nov 9, 2025

Not sure about the name: this doesn't configure the strategy - just the retriability

It also configures the delay, not only retryability.
About name – I agree that with current name you may think you would configure the service id of retry strategy or something like that. I would not mind splitting this into two separate stamps.

But then: you example about RunCommandMessage makes me think this is a concern that belongs to the message - not to the stamp. The question is: from the pov of messenger, should a failure of the command mean that the message wasn't handled successfully? It depends if the failure comes from the command being unable to run because eg it failed to start (missing binary, etc) or because the message yields to an non-zero exit code. Which is the case you want to skip.

Alternative idea: make this a concern of RunCommandMessage (and RunProcessMessage) - eg with status codes to consider OK from the pov of messenger.

retryability may be defined by the fact if the message is dispatched as a standalone action, or as a part of message chain. F.e. you may never want to retry a single message within a chain, but retry the whole chain instead – yet, when dispatching the message as a standalone (not part of a chain), retry it like normal. Hence I don't think it's possible to know within the message handler if the message should be retried or not – unless you include something like retryable in the message itself; which in my opinion does not sound ideal either as you'd have to implement that in every message class & handler.


Overall this is PR is more just an idea & a tiny step to get closer to implementing message chains (#50462) and not something I'm after as-is, though it may solve some other use-cases also (like #62056).

@nicolas-grekas nicolas-grekas modified the milestones: 7.4, 8.1 Nov 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants