Skip to content

fix(microservices): reply on the request channel when redis wildcards are enabled - #17866

Merged
kamilmysliwiec merged 2 commits into
nestjs:masterfrom
fadiroot:fix/redis-wildcard-reply-channel
Sep 24, 2026
Merged

kamilmysliwiec merged 2 commits into
nestjs:masterfrom
fadiroot:fix/redis-wildcard-reply-channel

Conversation

@fadiroot

Copy link
Copy Markdown

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Other... Please describe:

What is the current behavior?

Issue Number: N/A

With wildcards: true, ServerRedis listens for pmessage, which ioredis emits as (pattern, channel, message). The handler is registered on the pattern (e.g. users.*) and is looked up correctly, but the reply for a request-response message is also published to users.*.reply.

Redis does not expand globs on PUBLISH, and ClientRedis subscribes to the reply channel of the concrete channel it published to (users.created.reply), so the reply never reaches the client and every client.send() against a wildcard handler hangs until it times out. Events are unaffected because they do not reply.

Repro:

// server
@MessagePattern('users.*')
handle() { return 'ok'; }

// client
await lastValueFrom(client.send('users.created', {})); // never resolves

What is the new behavior?

The reply is published to the channel the request came from (users.created.reply), which is what ServerMqtt already does for its wildcard topics. Without wildcards the channel and the pattern are the same value, so nothing changes there.

A unit test covers the wildcard case in server-redis.spec.ts.

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

… are enabled

ioredis emits "pmessage" with (pattern, channel, message). With wildcards
enabled the handler is registered on the pattern ("users.*"), so looking
the handler up by the pattern is correct, but the reply was published to
"users.*.reply" as well. Redis does not expand globs on PUBLISH, while
ClientRedis subscribes to the reply channel of the concrete channel it
published to ("users.created.reply"), so the response never arrived and
every request-response call against a wildcard handler timed out.

Publish the reply to the channel the request came from, which is what the
mqtt server already does. Without wildcards both values are the same
channel, so nothing changes there.
@fadiroot
fadiroot force-pushed the fix/redis-wildcard-reply-channel branch from 830eac7 to 7316961 Compare September 24, 2026 09:59
@kamilmysliwiec
kamilmysliwiec merged commit 26ada7f into nestjs:master Sep 24, 2026
3 checks passed
kamilmysliwiec added a commit that referenced this pull request Oct 13, 2026
…ds (#18125)

* fix(microservices): ignore a redis server's own replies under wildcards

With wildcards enabled, ServerRedis psubscribes to the handler pattern
(e.g. "users.*") and, since #17866, replies on the concrete request
channel plus ".reply" ("users.x.reply"). A Redis glob "*" matches dots
too, so the server received its own reply. The deserializer mapped the
{ response, isDisposed, id } packet to an event on "users.*", and the
request handler ran a second time with the reply as its data.

handleMessage now skips a message on a ".reply" channel that has no
handler of its own when the raw packet has an id and no pattern, which
is the shape of every reply this server publishes. Requests, events and
messages without an id on such channels are still dispatched.

* fix(microservices): decide own redis replies by the subscription

Moves the check into isOwnReply(). It now looks at the subscription pattern rather than whether the reply channel has a handler, so a "*.reply" subscription still receives replies and "users.*" skips them even when "users.x.reply" has a handler.

---------

Co-authored-by: Kamil Myśliwiec <mail@kamilmysliwiec.com>
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.

3 participants