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.
[Why]
The testcase tries to replicate the steps described in issue #12934.
[How]
It uses intermediate Erlang nodes between the common_test control node
and the RabbitMQ nodes, using `peer` standard_io communication. The goal
is to make sure the common_test control node doesn't interfere with the
nodes the RabbitMQ nodes can see, despite the blocking of the Erlang
distribution connection.
So far, I couldn't reproduce the problem reported in #12934. @mkuratczyk
couldn't either, so it might have been fixed as a side effect of another
change...
References #12934.