Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[Messenger] Added documentation about DispatchAfterCurrentBusMiddleware #10015

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
wants to merge 13 commits into from
Prev Previous commit
Next Next commit
Minor updates
  • Loading branch information
Nyholm committed Apr 8, 2019
commit aba0edf038302a9fbcf2292a6167bcd42f4d4640
63 changes: 45 additions & 18 deletions 63 messenger/message-recorder.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ a different bus (if the application has `multiple buses </messenger/multiple_bus
Any errors or exceptions that occur during this process can have unintended consequences,
such as:

- If using the ``DoctrineTransactionMiddleware`` and a dispatched message to the same bus
and an exception is thrown, then any database transactions in the original handler will
be rolled back.
- If using the ``DoctrineTransactionMiddleware`` and a dispatched message throws an exception,
then any database transactions in the original handler will be rolled back.
- If the message is dispatched to a different bus, then the dispatched message can still
be handled even if the original handler encounters an exception.
Nyholm marked this conversation as resolved.
Show resolved Hide resolved

Expand All @@ -32,29 +31,28 @@ will not be created because the ``DoctrineTransactionMiddleware`` will rollback
Doctrine transaction, in which the user has been created.
Nyholm marked this conversation as resolved.
Show resolved Hide resolved

**Problem 2:** If an exception is thrown when saving the user to the database, the welcome
email is still sent.
email is still sent because it is handled asynchronously.

``HandleMessageInNewTransaction`` Middleware
--------------------------------------------
``DispatchAfterCurrentBusMiddleware`` Middleware
------------------------------------------------

For many applications, the desired behavior is to have any messages dispatched by the handler
to `only` be handled after the handler finishes. This can be by using the
``HandleMessageInNewTransaction`` middleware and adding a ``Transaction`` stamp to
`the message Envelope </components/messenger#adding-metadata-to-messages-envelopes>`_.
This middleware enables us to add messages to a separate transaction that will only be
dispatched *after* the current message handler finishes.
``DispatchAfterCurrentBusMiddleware`` middleware and adding a ``DispatchAfterCurrentBusStamp``
stamp to `the message Envelope </components/messenger#adding-metadata-to-messages-envelopes>`_.

Referencing the above example, this means that the ``UserSignedUp`` event would not be handled
until *after* the ``SignUpUserHandler`` had completed and the new ``User`` was persisted to the
database. If the ``SignUpUserHandler`` encounters an exception, the ``UserSignedUp`` event will
never be handled and if an exception is thrown while sending the welcome email, the Doctrine
transaction will not be rolled back.

To enable this, you need to add the ``handle_message_in_new_transaction``
middleware. Make sure it is registered before ``DoctrineTransactionMiddleware``
The ``dispatch_after_current_bus`` middleware is enabled by default. It is configured as the
first middleware on all busses. When doing a highly custom or special configuration, then make
sure ``dispatch_after_current_bus`` is registered before ``doctrine_transaction``
in the middleware chain.

**Note:** The ``handle_message_in_new_transaction`` middleware must be loaded for *all* of the
**Note:** The ``dispatch_after_current_bus`` middleware must be loaded for *all* of the
buses. For the example, the middleware must be loaded for both the command and event buses.

.. configuration-block::
Expand All @@ -70,13 +68,11 @@ buses. For the example, the middleware must be loaded for both the command and e
messenger.bus.command:
middleware:
- validation
- handle_message_in_new_transaction
- doctrine_transaction
messenger.bus.event:
default_middleware: allow_no_handlers
middleware:
- validation
- handle_message_in_new_transaction
- doctrine_transaction
Nyholm marked this conversation as resolved.
Show resolved Hide resolved


Expand All @@ -89,7 +85,7 @@ buses. For the example, the middleware must be loaded for both the command and e
use App\Messenger\Event\UserSignedUp;
use Doctrine\ORM\EntityManagerInterface;
Nyholm marked this conversation as resolved.
Show resolved Hide resolved
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Stamp\Transaction;
use Symfony\Component\Messenger\Stamp\DispatchAfterCurrentBusStamp;
use Symfony\Component\Messenger\MessageBusInterface;

class SignUpUserHandler
Expand All @@ -108,13 +104,44 @@ buses. For the example, the middleware must be loaded for both the command and e
$user = new User($command->getUuid(), $command->getName(), $command->getEmail());
$this->em->persist($user);
Nyholm marked this conversation as resolved.
Show resolved Hide resolved

// The Transaction stamp marks the event message to be handled
// The DispatchAfterCurrentBusStamp marks the event message to be handled
// only if this handler does not throw an exception.

$event = new UserSignedUp($command->getUuid());
$this->eventBus->dispatch(
(new Envelope($event))
->with(new Transaction())
->with(new DispatchAfterCurrentBusStamp())
);
Nyholm marked this conversation as resolved.
Show resolved Hide resolved
}
}

.. code-block:: php

namespace App\Messenger\EventSubscriber;

use App\Entity\User;
use App\Messenger\Event\UserSignedUp;
use Doctrine\ORM\EntityManagerInterface;
Nyholm marked this conversation as resolved.
Show resolved Hide resolved

class WhenUserSignedUpThenSendWelcomeEmail
{
private $em;
private $mailer;

public function __construct(MyMailer $mailer, EntityManagerInterface $em)
{
$this->mailer = $mailer;
$this->em = $em;
}

public function __invoke(UserSignedUp $eent)
Nyholm marked this conversation as resolved.
Show resolved Hide resolved
{
$user = $this->em->getRepository(User::class)->find(new User($event->getUuid()));

$this->mailer->sendWelcomeEmail($user);
}
}

**Note:** If ``WhenUserSignedUpThenSendWelcomeEmail`` throws an exception, that exception
Nyholm marked this conversation as resolved.
Show resolved Hide resolved
will be wrapped into a ``DelayedMessageHandlingException``. Using ``DelayedMessageHandlingException::getExceptions``
will give you all exceptions that are thrown while handing a message with the ``DispatchAfterCurrentBusStamp``.
Morty Proxy This is a proxified and sanitized view of the page, visit original site.