Skip to content

Conversation

@dumbbell
Copy link
Collaborator

Why

Topic permissions depend on an exchange, in addition to a user and a vhost like other permissions.

This fixes a bug where an exchange imported after a topic permission that depends on it caused the following crash when Khepri is used:

{case_clause,{error,{khepri,mismatching_node, #{node_name => <<"exchange_name">>, node_props => #{payload_version => 1}, node_path => [rabbitmq,vhosts,<<"/">>,exchanges, <<"exchange_name">>], condition => {if_node_exists,false}, node_is_target => true}}}}

The crash comes from the fact that the exchange code expect to either create the tree node in Khepri for that exchange, or there is an existing tree node holding an exchange tree node. Here, there was a tree node created implicitly when the topic permission was stored, but that tree node didn't have an exchange record (because the exchange was not imported yet).

How

We simply swap the import of topic permissions and exchanges.

In addition to that, we also relax the conditions used to write a new exchange record in Khepri. This is not strictly required, but this makes the code more robust (see the second commit in the branch).

[Why] Topic permissions depend on an exchange, in addition to a user and a vhost like other permissions. This fixes a bug where an exchange imported after a topic permission that depends on it caused the following crash when Khepri is used: {case_clause,{error,{khepri,mismatching_node, #{node_name => <<"exchange_name">>, node_props => #{payload_version => 1}, node_path => [rabbitmq,vhosts,<<"/">>,exchanges, <<"exchange_name">>], condition => {if_node_exists,false}, node_is_target => true}}}} The crash comes from the fact that the exchange code expect to either create the tree node in Khepri for that exchange, or there is an existing tree node holding an exchange tree node. Here, there was a tree node created implicitly when the topic permission was stored, but that tree node didn't have an exchange record (because the exchange was not imported yet). [How] We simply swap the import of topic permissions and exchanges.
…hepri [Why] When the module wanted to create an exchange in Khepri, it used `rabbit_khepri:create/2` which ensures that the tree node doesn't exist before. If the tree node exists, it expects it to contain an exchange record which is returned to the caller, indicating an exchange with that name already exists. However, there are several other resources stored under an exchange tree node, like bindings and topic permissions. In particular, during a definitions import, topic permissions used to be imported before exchanges. This caused a crash because the write of the topic permission automatically created a parent tree node for the exchange it dpends on, but without an exchange record obviously (see previous commit). [How] As an addition improvement to the previous commit, we change the conditions: instead of matching on the fact the tree node doesn't exist, the module also accepts that the tree node exists but has no payload. Under any of these conditions, the exchange is considered to be new and written to Khepri.
@dumbbell dumbbell marked this pull request as ready for review August 20, 2025 11:57
@dumbbell dumbbell merged commit 1156232 into main Aug 20, 2025
560 of 561 checks passed
@dumbbell dumbbell deleted the fix-topic-permissions-definitions-import branch August 20, 2025 11:57
@Zerpet Zerpet added this to the 4.1.4 milestone Aug 20, 2025
dumbbell added a commit that referenced this pull request Aug 20, 2025
rabbit_definitions: Import topic permissions after exchanges (backport #14407)
@michaelklishin michaelklishin modified the milestones: 4.1.4, 4.2.0 Aug 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

4 participants