fix: scope dispatch plans per queue and serialize RPC replies through the consumer channel gate#288
Merged
Merged
Conversation
…he channel gate, and make publish-path type caches thread-safe
Owner
Author
|
Successfully created backport PR for |
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.
This fixes three defects in the RabbitMQ consume/publish paths.
1. Consuming the same message type on two queues ran every queue's consumers on every delivery. Execution plans were assembled into a single registry shared by all queues on the bus, keyed only by message URN, and dispatch ran all handlers in the plan with no per-queue filter. With queues A and B both consuming
OrderCreated, a delivery on A also ran B's consumers — N× execution per publish, and cross-queue retry policies/partition lanes applied to the wrong queue's consumers. The bus now builds one plan cache per queue (fresh per start attempt, preserving the startup-retry idempotency), so a worker's plan lookups resolve only the handlers its own queue registered. The coreMessageExecutionRegistrykeeps its accumulate-per-instance behavior — the in-memory test harness depends on it, since it dispatches each produced message exactly once — and its docs now spell out which model a transport should use. Its request-consumer uniqueness check is now keyed per (queue, URN), matching what its exception message always claimed.2. RPC reply publishes bypassed the consumer channel's write gate. Every write on a worker's shared channel (ack, nack, retry republish, fault publish) serializes through the worker's channel gate — except request/reply responses, which received the raw
IChanneland published directly. On an RPC queue withConcurrencyLimit > 1, a multi-frame reply publish could interleave with a parallel ack on the same channel; RabbitMQ channels are documented as not safe for concurrent use. Handler dispatch now receives a gate-wrapped publish delegate instead of the channel, so replies serialize with the settles in the same domain, and handlers can no longer perform arbitrary channel operations.3. The message-type/URN caches raced on the publish hot path. Singleton publishers lazily register unconfigured message types per operation into plain
Dictionaryfields. Concurrent first publishes of distinct types — a normal startup burst — corrupted the maps (reproduced: "Operations that change non-concurrent collections must have exclusive access" and lost URN-index writes). The caches are nowConcurrentDictionarywithGetOrAdd, converging concurrent first lookups on a single configuration instance while keeping the hot read path allocation-free and preserving explicit-configuration overwrite semantics.New regression tests cover per-queue dispatch isolation (two queues, one type: each consumer runs exactly once per fanned-out publish and never on the other queue's delivery), queue-scoped plan lookup, per-queue RPC uniqueness, reply serialization under parallel dispatch (a recording channel asserts no interleaved writes), and concurrent first-lookup stress on the configuration cache. All six fail on the previous code. Existing bus, worker, and test-harness suites pass unmodified; no public API changes.
Backport to v1.0: recommended — internal-only fixes for defects present since 1.0.0.