fix(microservices): close the kafka request-response span exactly once - #17797
Merged
kamilmysliwiec merged 2 commits intoSep 17, 2026
Conversation
follow-up to nestjs#17794, which moved the end hook onto the response stream for mqtt, nats, redis, tcp and rmq but left the kafka publisher untouched `ServerKafka` still runs `onProcessingEndHook` from `sendMessage`, which `Server#send` invokes once per emitted packet, so the span is mismanaged in three ways: - a handler returning a multi-value observable closes the span once per published reply - `combineStreamsAndThrowIfRetriable` rejects on a `KafkaRetriableException`, so `this.send` is never reached and the span stays open; kafkajs then redelivers the message, and every retry leaks another span - the `NO_MESSAGE_HANDLER` path publishes without ever running a start hook, so that end hook call is unpaired all three are fixed the way nestjs#17781 and nestjs#17794 did it: the hook moves out of `sendMessage` and onto the reply stream's `finalize` teardown, guarded by the shared `Server#createProcessingEndHookRunner`, and the handler call is wrapped in try/catch so a retriable rejection closes the span before it is rethrown as on the other transports the span now closes once processing has settled rather than after the last reply has been produced
7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR Checklist
PR Type
What is the current behavior?
Issue Number: N/A
Follow-up to #17794, which moved the processing end hook onto the response
stream for mqtt, nats, redis, tcp and rmq, but left the kafka publisher
untouched.
ServerKafkastill runsonProcessingEndHookfromsendMessage, whichServer#sendinvokes once per emitted packet, so the span is mismanaged inthree ways:
published reply — a stream of three responses closes it three times
combineStreamsAndThrowIfRetriablerejects on aKafkaRetriableException,so
this.sendis never reached and the span stays open. kafkajs thenredelivers the message, so every retry leaks another span
NO_MESSAGE_HANDLERpath publishes without ever running a start hook,so that end hook call is unpaired (the same unpaired call fix(microservices): close the request-response span exactly once #17794 removed
from mqtt, nats and redis)
What is the new behavior?
All three are fixed the way #17781 and #17794 did it: the hook moves out of
sendMessageand onto the reply stream'sfinalizeteardown, guarded by theshared
Server#createProcessingEndHookRunner, and the handler call is wrappedin try/catch so a retriable rejection closes the span before it is rethrown.
Error propagation is unchanged — the
KafkaRetriableExceptionstill reacheskafkajs so the message is redelivered.
Six specs were added for
handleMessageon the request-response path. Threeof them fail against the current code and pin the bugs above (3 end hook calls
for a three-value stream, 0 for a retriable rejection, 1 unpaired call with no
handler); the other three are regression guards for paths that already worked.
Does this PR introduce a breaking change?
Other information
One behavioural note worth flagging: the span now closes once processing has
settled rather than after the last reply has actually been produced to Kafka.
That is what #17794 already does on the other transports, so this brings kafka
in line with them, but it does differ from the previous kafka-only timing.