-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Messenger] Pass envelope to message handler as second __invoke parameter #42005
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
Conversation
Hey! I see that this is your first PR. That is great! Welcome! Symfony has a contribution guide which I suggest you to read. In short:
Review the GitHub status checks of your pull request and try to solve the reported issues. If some tests are failing, try to see if they are failing because of this change. When two Symfony core team members approve this change, it will be merged and you will become an official Symfony contributor! I am going to sit back now and wait for the reviews. Cheers! Carsonbot |
shouldnt you solve this more generically at the middleware layer? or make it part of your message payload? |
It is impossible: the handler will dispatch another message and there is currently no way to get the envelope stamps in the handler. It is also impossible to reference a previous message from another dispatched message without some very ugly solutions like keeping a static map of And adding it to the message body is also bad because the trace ID is for debugging/monitoring purposes and has no value for the core functionality of the respective handlers. That said, I am all ears for alternative solutions. Explained in code, this is what a handler does: class SomeHandler implements HandlerInterface {
private MessageBusInterface $bus;
public function __construct(MessageBusInterface $bus) {
$this->bus = $bus;
}
public function __invoke(SomeMessage $message) {
// do something with $message
$this->bus->dispatch(new AnotherMessage(...));
}
} I've implemented also a $this->bus->dispatch(
Envelope::wrap(new AnotherMessage(...))
->with(new TraceIdStamp($traceId))
); |
I think you can solve your problem by just type hint for an envelope in your handler. Ie: class FooHandler {
public function __invoke(Envelope $envelope) {
$message->getMessage();
// Do work
// Have access to all stamps and your X-Trace-Id
}
} Of course, with a handler like this you cannot rely on autoconfigure, you need to write DI config yourself to map a message to a handler. |
This doesn't work since public function handle(Envelope $envelope, StackInterface $stack): Envelope
{
$handler = null;
$message = $envelope->getMessage();
// ...
try {
$handler = $handlerDescriptor->getHandler();
$handledStamp = HandledStamp::fromDescriptor($handlerDescriptor, $handler($message)); So even if I manually wire a handler to consume my message class it's still not getting the envelope since the handlers are not handled as services during |
I mean I can write my own handling middleware and jam it before the default one but that seems like extreme overkill. |
IIUC a middleware could track state yes, something like handle($envelope, $stack) {
if ($this->currentTrace) {
$envelope->with(new Badge($this->currentTrace))
return $stack()->next()->handle($envelope);
}
$this->currentTrace = $envelope->last(Badge::class);
try {
return $stack()->next()->handle($envelope);
} finally {
$this->currentTrace = null;
}
} note im not particularly against providing envelope as second arg, other than maybe-semantics. |
@ro0NL I've tried that as well but it's not what I need -- that would tag anything coming down a specific middleware and I need messages from a specific request tied together. Practically speaking I need this:
In the above stateful-middleware approach what happens is:
So far the only two solutions (aside from adding the second invoke param) I see are either:
Adding the second invoke param still feels like the best solution and semantically I don't think it's that big of an issue. If we're copying the concept or IRL mail with envelopes, you will usually have the envelope together with the mail, right? |
im not sure i understand. Who's dispatching message C then? As i currently see it: |
There is no controller (and the cake is a lie) -- this is all happening inside a The worker's job is simple: in a loop it will:
Now, say some external system dispatches message A with The worker will: Loop 1
Loop 2
Loop 3
Basically, after one message with a trace ID any future messages without a trace ID will get stamped which is wrong. In the three loops above, messages A and B are related and should have the same trace ID but message C is not. In other words, a stateful middleware relies on the idea that messages dispatched in a handler will be processed immediately after (in a series) and that is not a good guarantee. I imagine running multiple workers would be problematic, synchronizing the None of these problems exist when the envelope is available in the handler. |
im still trying to follow (sorry, i have no practical experience, but this is on my list to play with for logging purposes :)). However i dont like the idea of dealling with trace IDs at the handler level personally :/ with the approach from #42005 (comment) In loop 1, after message A leaves we clear the state (ie. the finally block) |
in real life, if you are a stamp collector, you need the envelop. Hence im not really against this. But in real life also we dont usualy link the envelop with the message once opened, which is a sementical concern. |
I think it would be easiest to try and write a solution so you can see what I mean. Just take any AMQP-compatible queue (I'm using RabbitMQ) and try to pass a custom header from one message to another that you dispatch. I also need this for logging purposes so that we can identify when in a chain of events something went wrong but we have some handlers which dispatch other messages and, well, here we are. Regarding the real life example, a couple alternative ones would be:
Semantically, I think we're not losing much... |
@sroze when could we hope for a review/decision? |
Looks like #42873 also fixes this. So this could be closed when that one is merged. |
d358688
to
73ebb9e
Compare
Signed-off-by: Alexander M. Turek <[email protected]>
73ebb9e
to
9c7cdfc
Compare
Turns out the approach from @ro0NL works: any dispatches inside a handler are executed immediately and middleware instances are bus-specific so doing a stateful middleware which wipes the IDs post-handling works. I'm closing this as my problem is resolved and the use case is gone. Cheers! |
When calling message handlers
__invoke()
method, pass in the envelope as a second parameter. This ensures we're backwards compatible (doesn't break existing handlers) but any which might want to use the envelope can do so.My use case for this is a trace ID we want to use for tracking requests across events with AMQP:
X-Trace-Id
, controller receives this and dispatches a message with anx-trace-id
AMQP headerx-trace-id
header