Skip to content

Commit bf18378

Browse files
committed
Merge remote-tracking branch 'origin/4.3'
* origin/4.3: deprecate calling createChildContext without the format parameter [EventDispatcher] Fix interface name used in error messages [FrameworkBundle] Add cache configuration for PropertyInfo Update dependencies in the main component Drop useless executable bit [Doctrine][PropertyInfo] Detect if the ID is writeable Add transport in subscriber's phpdoc [Validator] Autovalidation: skip readonly props [DI] default to service id - *not* FQCN - when building tagged locators [Cache] Log a more readable error message when saving into cache fails Update WorkflowEvents.php [Messenger] On failure retry, make message appear received from original sender [Messenger] remove send_and_handle option which can be achieved with SyncTransport Fixing tests - passing pdo is not wrapped for some reason in dbal Changing how RoutableMessageBus fallback bus works [Serializer] Fix BC break: DEPTH_KEY_PATTERN must be public [FrameworkBundle] Fixed issue when a parameter container a '%' Fix the interface incompatibility of EventDispatchers [TwigBundle] fixed Mailer integration in Twig [Form] Add intl/choice_translation_locale option to TimezoneType
2 parents a2f7691 + 9263aa2 commit bf18378

22 files changed

+458
-162
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ CHANGELOG
66

77
* [BC BREAK] `SendersLocatorInterface` has an additional method:
88
`getSenderByAlias()`.
9+
* Removed argument `?bool &$handle = false` from `SendersLocatorInterface::getSenders`
910
* A new `ListableReceiverInterface` was added, which a receiver
1011
can implement (when applicable) to enable listing and fetching
1112
individual messages by id (used in the new "Failed Messages" commands).

Command/AbstractFailedMessagesCommand.php

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\Console\Helper\Dumper;
1616
use Symfony\Component\Console\Style\SymfonyStyle;
1717
use Symfony\Component\Messenger\Envelope;
18+
use Symfony\Component\Messenger\Stamp\RedeliveryStamp;
1819
use Symfony\Component\Messenger\Stamp\SentToFailureTransportStamp;
1920
use Symfony\Component\Messenger\Stamp\TransportMessageIdStamp;
2021
use Symfony\Component\Messenger\Transport\Receiver\MessageCountAwareInterface;
@@ -59,8 +60,10 @@ protected function displaySingleMessage(Envelope $envelope, SymfonyStyle $io)
5960
{
6061
$io->title('Failed Message Details');
6162

62-
/** @var SentToFailureTransportStamp $sentToFailureTransportStamp */
63+
/** @var SentToFailureTransportStamp|null $sentToFailureTransportStamp */
6364
$sentToFailureTransportStamp = $envelope->last(SentToFailureTransportStamp::class);
65+
/** @var RedeliveryStamp|null $lastRedeliveryStamp */
66+
$lastRedeliveryStamp = $envelope->last(RedeliveryStamp::class);
6467

6568
$rows = [
6669
['Class', \get_class($envelope->getMessage())],
@@ -70,25 +73,34 @@ protected function displaySingleMessage(Envelope $envelope, SymfonyStyle $io)
7073
$rows[] = ['Message Id', $id];
7174
}
7275

76+
$flattenException = null === $lastRedeliveryStamp ? null : $lastRedeliveryStamp->getFlattenException();
7377
if (null === $sentToFailureTransportStamp) {
7478
$io->warning('Message does not appear to have been sent to this transport after failing');
7579
} else {
7680
$rows = array_merge($rows, [
77-
['Failed at', $sentToFailureTransportStamp->getSentAt()->format('Y-m-d H:i:s')],
78-
['Error', $sentToFailureTransportStamp->getExceptionMessage()],
79-
['Error Class', $sentToFailureTransportStamp->getFlattenException() ? $sentToFailureTransportStamp->getFlattenException()->getClass() : '(unknown)'],
81+
['Failed at', null === $lastRedeliveryStamp ? '' : $lastRedeliveryStamp->getRedeliveredAt()->format('Y-m-d H:i:s')],
82+
['Error', null === $lastRedeliveryStamp ? '' : $lastRedeliveryStamp->getExceptionMessage()],
83+
['Error Class', null === $flattenException ? '(unknown)' : $flattenException->getClass()],
8084
['Transport', $sentToFailureTransportStamp->getOriginalReceiverName()],
8185
]);
8286
}
8387

8488
$io->table([], $rows);
8589

90+
/** @var RedeliveryStamp[] $redeliveryStamps */
91+
$redeliveryStamps = $envelope->all(RedeliveryStamp::class);
92+
$io->writeln(' Message history:');
93+
foreach ($redeliveryStamps as $redeliveryStamp) {
94+
$io->writeln(sprintf(' * Message failed and redelivered to the <info>%s</info> transport at <info>%s</info>', $redeliveryStamp->getSenderClassOrAlias(), $redeliveryStamp->getRedeliveredAt()->format('Y-m-d H:i:s')));
95+
}
96+
$io->newLine();
97+
8698
if ($io->isVeryVerbose()) {
8799
$io->title('Message:');
88100
$dump = new Dumper($io);
89101
$io->writeln($dump($envelope->getMessage()));
90102
$io->title('Exception:');
91-
$io->writeln($sentToFailureTransportStamp->getFlattenException()->getTraceAsString());
103+
$io->writeln(null === $flattenException ? '(no data)' : $flattenException->getTraceAsString());
92104
} else {
93105
$io->writeln(' Re-run command with <info>-vv</info> to see more message & error details.');
94106
}

Command/FailedMessagesRetryCommand.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ private function runInteractive(SymfonyStyle $io, bool $shouldForce)
154154
}
155155

156156
// avoid success message if nothing was processed
157-
if (1 < $count) {
157+
if (1 <= $count) {
158158
$io->success('All failed messages have been handled or removed!');
159159
}
160160
}

Command/FailedMessagesShowCommand.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
use Symfony\Component\Console\Output\ConsoleOutputInterface;
1919
use Symfony\Component\Console\Output\OutputInterface;
2020
use Symfony\Component\Console\Style\SymfonyStyle;
21-
use Symfony\Component\Messenger\Stamp\SentToFailureTransportStamp;
21+
use Symfony\Component\Messenger\Stamp\RedeliveryStamp;
2222
use Symfony\Component\Messenger\Transport\Receiver\ListableReceiverInterface;
2323

2424
/**
@@ -83,14 +83,14 @@ private function listMessages(SymfonyStyle $io, int $max)
8383

8484
$rows = [];
8585
foreach ($envelopes as $envelope) {
86-
/** @var SentToFailureTransportStamp $sentToFailureTransportStamp */
87-
$sentToFailureTransportStamp = $envelope->last(SentToFailureTransportStamp::class);
86+
/** @var RedeliveryStamp|null $lastRedeliveryStamp */
87+
$lastRedeliveryStamp = $envelope->last(RedeliveryStamp::class);
8888

8989
$rows[] = [
9090
$this->getMessageId($envelope),
9191
\get_class($envelope->getMessage()),
92-
null === $sentToFailureTransportStamp ? '' : $sentToFailureTransportStamp->getSentAt()->format('Y-m-d H:i:s'),
93-
null === $sentToFailureTransportStamp ? '' : $sentToFailureTransportStamp->getExceptionMessage(),
92+
null === $lastRedeliveryStamp ? '' : $lastRedeliveryStamp->getRedeliveredAt()->format('Y-m-d H:i:s'),
93+
null === $lastRedeliveryStamp ? '' : $lastRedeliveryStamp->getExceptionMessage(),
9494
];
9595
}
9696

EventListener/SendFailedMessageToFailureTransportListener.php

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
1717
use Symfony\Component\Messenger\Exception\HandlerFailedException;
1818
use Symfony\Component\Messenger\MessageBusInterface;
19+
use Symfony\Component\Messenger\Stamp\DelayStamp;
1920
use Symfony\Component\Messenger\Stamp\ReceivedStamp;
2021
use Symfony\Component\Messenger\Stamp\RedeliveryStamp;
21-
use Symfony\Component\Messenger\Stamp\SentStamp;
2222
use Symfony\Component\Messenger\Stamp\SentToFailureTransportStamp;
2323
use Symfony\Component\Messenger\Stamp\TransportMessageIdStamp;
2424

@@ -51,11 +51,8 @@ public function onMessageFailed(WorkerMessageFailedEvent $event)
5151
$envelope = $event->getEnvelope();
5252

5353
// avoid re-sending to the failed sender
54-
foreach ($envelope->all(SentStamp::class) as $sentStamp) {
55-
/** @var SentStamp $sentStamp */
56-
if ($sentStamp->getSenderAlias() === $this->failureSenderAlias) {
57-
return;
58-
}
54+
if (null !== $envelope->last(SentToFailureTransportStamp::class)) {
55+
return;
5956
}
6057

6158
// remove the received stamp so it's redelivered
@@ -67,8 +64,9 @@ public function onMessageFailed(WorkerMessageFailedEvent $event)
6764
$flattenedException = \class_exists(FlattenException::class) ? FlattenException::createFromThrowable($throwable) : null;
6865
$envelope = $envelope->withoutAll(ReceivedStamp::class)
6966
->withoutAll(TransportMessageIdStamp::class)
70-
->with(new SentToFailureTransportStamp($throwable->getMessage(), $event->getReceiverName(), $flattenedException))
71-
->with(new RedeliveryStamp(0, $this->failureSenderAlias));
67+
->with(new SentToFailureTransportStamp($event->getReceiverName()))
68+
->with(new DelayStamp(0))
69+
->with(new RedeliveryStamp(0, $this->failureSenderAlias, $throwable->getMessage(), $flattenedException));
7270

7371
if (null !== $this->logger) {
7472
$this->logger->info('Rejected message {class} will be sent to the failure transport {transport}.', [

Handler/MessageSubscriberInterface.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,14 @@ interface MessageSubscriberInterface extends MessageHandlerInterface
3232
* yield FirstMessage::class => ['priority' => 0];
3333
* yield SecondMessage::class => ['priority => -10];
3434
*
35-
* It can also specify a method, a priority and/or a bus per message:
35+
* It can also specify a method, a priority, a bus and/or a transport per message:
3636
*
3737
* yield FirstMessage::class => ['method' => 'firstMessageMethod'];
3838
* yield SecondMessage::class => [
3939
* 'method' => 'secondMessageMethod',
4040
* 'priority' => 20,
4141
* 'bus' => 'my_bus_name',
42+
* 'from_transport' => 'your_transport_name',
4243
* ];
4344
*
4445
* The benefit of using `yield` instead of returning an array is that you can `yield` multiple times the
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Messenger\Middleware;
13+
14+
use Symfony\Component\Messenger\Envelope;
15+
use Symfony\Component\Messenger\Stamp\ReceivedStamp;
16+
use Symfony\Component\Messenger\Stamp\SentToFailureTransportStamp;
17+
18+
/**
19+
* @author Ryan Weaver <ryan@symfonycasts.com>
20+
*
21+
* @experimental in 4.3
22+
*/
23+
class FailedMessageProcessingMiddleware implements MiddlewareInterface
24+
{
25+
public function handle(Envelope $envelope, StackInterface $stack): Envelope
26+
{
27+
// look for "received" messages decorated with the SentToFailureTransportStamp
28+
/** @var SentToFailureTransportStamp|null $sentToFailureStamp */
29+
$sentToFailureStamp = $envelope->last(SentToFailureTransportStamp::class);
30+
if (null !== $sentToFailureStamp && null !== $envelope->last(ReceivedStamp::class)) {
31+
// mark the message as "received" from the original transport
32+
// this guarantees the same behavior as when originally received
33+
$envelope = $envelope->with(new ReceivedStamp($sentToFailureStamp->getOriginalReceiverName()));
34+
}
35+
36+
return $stack->next()->handle($envelope, $stack);
37+
}
38+
}

Middleware/SendMessageMiddleware.php

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ public function handle(Envelope $envelope, StackInterface $stack): Envelope
5252
'class' => \get_class($envelope->getMessage()),
5353
];
5454

55-
$handle = false;
5655
$sender = null;
5756

5857
try {
@@ -65,7 +64,7 @@ public function handle(Envelope $envelope, StackInterface $stack): Envelope
6564

6665
// dispatch event unless this is a redelivery
6766
$shouldDispatchEvent = null === $redeliveryStamp;
68-
foreach ($this->getSenders($envelope, $handle, $redeliveryStamp) as $alias => $sender) {
67+
foreach ($this->getSenders($envelope, $redeliveryStamp) as $alias => $sender) {
6968
if (null !== $this->eventDispatcher && $shouldDispatchEvent) {
7069
$event = new SendMessageToTransportsEvent($envelope);
7170
$this->eventDispatcher->dispatch($event);
@@ -76,14 +75,9 @@ public function handle(Envelope $envelope, StackInterface $stack): Envelope
7675
$this->logger->info('Sending message "{class}" with "{sender}"', $context + ['sender' => \get_class($sender)]);
7776
$envelope = $sender->send($envelope->with(new SentStamp(\get_class($sender), \is_string($alias) ? $alias : null)));
7877
}
79-
80-
// on a redelivery, only send back to queue: never call local handlers
81-
if (null !== $redeliveryStamp) {
82-
$handle = false;
83-
}
8478
}
8579

86-
if (null === $sender || $handle) {
80+
if (null === $sender) {
8781
return $stack->next()->handle($envelope, $stack);
8882
}
8983
} catch (\Throwable $e) {
@@ -100,14 +94,14 @@ public function handle(Envelope $envelope, StackInterface $stack): Envelope
10094
/**
10195
* * @return iterable|SenderInterface[]
10296
*/
103-
private function getSenders(Envelope $envelope, &$handle, ?RedeliveryStamp $redeliveryStamp): iterable
97+
private function getSenders(Envelope $envelope, ?RedeliveryStamp $redeliveryStamp): iterable
10498
{
10599
if (null !== $redeliveryStamp) {
106100
return [
107101
$redeliveryStamp->getSenderClassOrAlias() => $this->sendersLocator->getSenderByAlias($redeliveryStamp->getSenderClassOrAlias()),
108102
];
109103
}
110104

111-
return $this->sendersLocator->getSenders($envelope, $handle);
105+
return $this->sendersLocator->getSenders($envelope);
112106
}
113107
}

RoutableMessageBus.php

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,15 @@
2828
class RoutableMessageBus implements MessageBusInterface
2929
{
3030
private $busLocator;
31+
private $fallbackBus;
3132

3233
/**
3334
* @param ContainerInterface $busLocator A locator full of MessageBusInterface objects
3435
*/
35-
public function __construct(ContainerInterface $busLocator)
36+
public function __construct(ContainerInterface $busLocator, MessageBusInterface $fallbackBus = null)
3637
{
3738
$this->busLocator = $busLocator;
39+
$this->fallbackBus = $fallbackBus;
3840
}
3941

4042
public function dispatch($envelope, array $stamps = []): Envelope
@@ -43,14 +45,28 @@ public function dispatch($envelope, array $stamps = []): Envelope
4345
throw new InvalidArgumentException('Messages passed to RoutableMessageBus::dispatch() must be inside an Envelope');
4446
}
4547

46-
/** @var BusNameStamp $busNameStamp */
48+
return $this->getMessageBus($envelope)->dispatch($envelope, $stamps);
49+
}
50+
51+
private function getMessageBus(Envelope $envelope): MessageBusInterface
52+
{
53+
/** @var BusNameStamp|null $busNameStamp */
4754
$busNameStamp = $envelope->last(BusNameStamp::class);
48-
$busName = null !== $busNameStamp ? $busNameStamp->getBusName() : MessageBusInterface::class;
55+
56+
if (null === $busNameStamp) {
57+
if (null === $this->fallbackBus) {
58+
throw new InvalidArgumentException(sprintf('Envelope is missing a BusNameStamp and no fallback message bus is configured on RoutableMessageBus.'));
59+
}
60+
61+
return $this->fallbackBus;
62+
}
63+
64+
$busName = $busNameStamp->getBusName();
4965

5066
if (!$this->busLocator->has($busName)) {
5167
throw new InvalidArgumentException(sprintf('Bus named "%s" does not exist.', $busName));
5268
}
5369

54-
return $this->busLocator->get($busName)->dispatch($envelope, $stamps);
70+
return $this->busLocator->get($busName);
5571
}
5672
}

Stamp/RedeliveryStamp.php

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\Messenger\Stamp;
1313

14+
use Symfony\Component\Debug\Exception\FlattenException;
15+
1416
/**
1517
* Stamp applied when a messages needs to be redelivered.
1618
*
@@ -20,14 +22,20 @@ class RedeliveryStamp implements StampInterface
2022
{
2123
private $retryCount;
2224
private $senderClassOrAlias;
25+
private $redeliveredAt;
26+
private $exceptionMessage;
27+
private $flattenException;
2328

2429
/**
2530
* @param string $senderClassOrAlias Alias from SendersLocator or just the class name
2631
*/
27-
public function __construct(int $retryCount, string $senderClassOrAlias)
32+
public function __construct(int $retryCount, string $senderClassOrAlias, string $exceptionMessage = null, FlattenException $flattenException = null)
2833
{
2934
$this->retryCount = $retryCount;
3035
$this->senderClassOrAlias = $senderClassOrAlias;
36+
$this->exceptionMessage = $exceptionMessage;
37+
$this->flattenException = $flattenException;
38+
$this->redeliveredAt = new \DateTimeImmutable();
3139
}
3240

3341
public function getRetryCount(): int
@@ -36,12 +44,27 @@ public function getRetryCount(): int
3644
}
3745

3846
/**
39-
* Needed for this class to serialize through Symfony's serializer.
47+
* The target sender this should be redelivered to.
4048
*
4149
* @internal
4250
*/
4351
public function getSenderClassOrAlias(): string
4452
{
4553
return $this->senderClassOrAlias;
4654
}
55+
56+
public function getExceptionMessage(): ?string
57+
{
58+
return $this->exceptionMessage;
59+
}
60+
61+
public function getFlattenException(): ?FlattenException
62+
{
63+
return $this->flattenException;
64+
}
65+
66+
public function getRedeliveredAt(): \DateTimeInterface
67+
{
68+
return $this->redeliveredAt;
69+
}
4770
}

0 commit comments

Comments
 (0)