Fix routing for empty binding key to topic exchange (backport #16271)#16309
Open
mergify[bot] wants to merge 1 commit intov4.3.xfrom
Open
Fix routing for empty binding key to topic exchange (backport #16271)#16309mergify[bot] wants to merge 1 commit intov4.3.xfrom
mergify[bot] wants to merge 1 commit intov4.3.xfrom
Conversation
* Fix routing for empty binding key to topic exchange Fixes https://github.com/rabbitmq/rabbitmq-server/discussions/16221 ## What? Fix a bug in the Khepri v4 topic trie projection where messages published with an empty routing key to a topic exchange were incorrectly routed to queues bound to other topic exchanges with an empty binding key. ## Why? The MQTT 5.0 spec states: > All Topic Names and Topic Filters MUST be at least one character long In contrast, the AMQP 0.9.1 spec state: > The routing key used for a topic exchange MUST consist of **zero** or more words delimited by dots. Each word may contain the letters A-Z and a-z and digits 0-9. The routing pattern follows the same rules as the routing key with the addition that * matches a single word, and # matches zero or more words Hence zero words, i.e. empty routing keys and empty binding keys, are expliclity allowed for the topic exchange type. In the Khepri v4 projection, the topic trie used a global `root` atom as the initial node ID for all topic exchanges. When a binding was created with an empty binding key (`<<>>`), `split_topic_key_binary/1` returned an empty list (`[]`). The trie traversal stopped immediately at the root, meaning the binding was inserted into the `rabbit_khepri_topic_binding_v4` ETS table with the global `root` atom as the `LeafNodeId`. Because the `LeafNodeId` lacked any exchange-specific context, all empty bindings across all topic exchanges were attached to the exact same global `root` node. Consequently, when a message was published with an empty routing key to any topic exchange, the routing logic (`trie_bindings/3`) would scan the ETS table starting at the global `root` node and incorrectly match bindings belonging to completely unrelated topic exchanges. ## How? To fix this and ensure exchange isolation, the conceptual root of the trie was changed from the global `root` atom to an exchange-specific tuple: `{root, XSrc}` (where `XSrc` is `{VHost, ExchangeName}`). - `rabbit_khepri:trie_follow_down_create/3` and `trie_follow_down_get_path/3` now initialize their trie traversal with `{root, XSrc}`. - `rabbit_db_topic_exchange:trie_match/5` now initiates routing from `{root, {VHost, XName}}`. This structural change guarantees that empty bindings are isolated per exchange in the bindings ETS table, completely resolving the cross-exchange leakage while fully supporting AMQP 0-9-1's allowance for empty binding keys without breaking backward compatibility. * Add new v5 projection for topic exchange routing This commit introduces a new feature flag, `topic_binding_projection_v5`, and a corresponding Khepri projection version (v5) to deploy the exchange isolation fix for empty binding keys. * Fix style * Add 4.3.1 release notes * Exercise one more code path #16271 #16221 --------- Co-authored-by: Michael Klishin <michaelklishin@icloud.com> (cherry picked from commit 6971ed6)
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.
Fixes #16221
What?
Fix a bug in the Khepri v4 topic trie projection where messages published with an empty routing key to a topic exchange were incorrectly routed to queues bound to other topic exchanges with an empty binding key.
Why?
The MQTT 5.0 spec states:
In contrast, the AMQP 0.9.1 spec state:
Hence zero words, i.e. empty routing keys and empty binding keys, are expliclity allowed for the topic exchange type.
In the Khepri v4 projection, the topic trie used a global
rootatom as the initial node ID for all topic exchanges. When a binding was created with an empty binding key (<<>>),split_topic_key_binary/1returned an empty list ([]). The trie traversal stopped immediately at the root, meaning the binding was inserted into therabbit_khepri_topic_binding_v4ETS table with the globalrootatom as theLeafNodeId.Because the
LeafNodeIdlacked any exchange-specific context, all empty bindings across all topic exchanges were attached to the exact same globalrootnode. Consequently, when a message was published with an empty routing key to any topic exchange, the routing logic (trie_bindings/3) would scan the ETS table starting at the globalrootnode and incorrectly match bindings belonging to completely unrelated topic exchanges.How?
To fix this and ensure exchange isolation, the conceptual root of the trie was changed from the global
rootatom to an exchange-specific tuple:{root, XSrc}(whereXSrcis{VHost, ExchangeName}).rabbit_khepri:trie_follow_down_create/3andtrie_follow_down_get_path/3now initialize their trie traversal with{root, XSrc}.rabbit_db_topic_exchange:trie_match/5now initiates routing from{root, {VHost, XName}}.This structural change guarantees that empty bindings are isolated per exchange in the bindings ETS table, completely resolving the cross-exchange leakage while fully supporting AMQP 0-9-1's allowance for empty binding keys without breaking backward compatibility.
TODOs
This is an automatic backport of pull request Fix routing for empty binding key to topic exchange #16271 done by Mergify.