## What?
Refuse or detach the link instead of ending the session for many
link-level errors, including the following:
* Source queue or target exchange doesn't exist during attach
* Trying to consume from an exclusive queue on a different connection
* Delivery of message to a target queue fails
* Wrong delivery-id
* Wrong settled flag
* Queue declaration fails for dynamic queues
* Publishing to internal exchange
## Why?
Because many errors are scoped to a single terminus, detaching just that link
preserves the rest of the session’s links - avoiding needless disruption of
other traffic. AMQP 1.0’s error model is hierarchical; RabbitMQ should escalate to
ending the session only for session-level faults or if the client keeps
using a destroyed link.
## How?
Refuse link as per figure 2.33
Sometimes you want a plugin to act as a library
and not an application. That is, for its modules
to be available at compile time and on a running
node but, say, the actual runtime parameter
handling and supervision of shovels to be
handled by another plugin.
Since we do not currently have a concept of
"library plugins" or "library dependencies",
this approach demonstrates one example of how
some plugins can be used as libraries.
Then wait for elections to complete before shutting further
members down.
This should help avoid election storms when enabling maintenance
mode.
Transfer khepri before queues to ensure meta data store is
ready to accept pid updates.
Some other state related tweaks.
In some environments it may be tricky to run a command
before RabbitMQ is started. Kubernetes For example, upgrading
to Containerd 2.0 accidentally lowered the value of available
descriptors in GKE
(https://cloud.google.com/kubernetes-engine/docs/troubleshooting/known-issues#containerd-ulimit-reduced).
While this particular issue is resolved in newer GKE versions,
it was a good example of why a variable like this would be helpful.
Kubernetes-level solutions (on the node or with a daemonset) would
require a different level of access for users.
[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.
[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.