Skip to content

ra_server_proc: Fix handling of local query replies #517

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 12, 2025

Conversation

dumbbell
Copy link
Collaborator

Why

The reply of local query was formatted as:

{reply, From, Reply}

However, the code was called in two different contexts:

  1. When the local query was executed immediately, the reply tuple was interpreted by gen_statem and thus the reply was sent by the local Ra server process. Exactly what we wanted.

  2. When the local query execution depended on a condition and might have to be delayed, it could be executed right away (and the reply tuple was interpreted by gen_statem), or the execution could be delayed and at the time of execution, the reply tuple could be interpreted as a Ra effect.

Unfortunately, the same tuple has a very different meaning depending on who interprets it:

  • gen_statem will send the reply regardless of the Raft state of the Ra server process.
  • The Ra effect will only send the reply if the Ra server process is acting as the leader.

The delayed query was always processed by the Ra server that received it, regardless of its state. This means that if the Ra server is not a leader and the reply tuple is interpreted as a Ra effect, the caller will never get an answer.

This led to some timeouts in Khepri and nasty bugs in RabbitMQ. In particular, it caused the peer_discovery_classic_config_SUITE to fail quite frequently in RabbitMQ CI.

How

First, the patch ensures the reply tuple is always interpreted as a Ra effect.

Then, it sets the {member, ...} replier option in the reply effect to the Ra server that executes the query. This way, the reply is always emitted emitted and by the correct Ra server, regardless of its Raft state.

@dumbbell dumbbell requested a review from kjnilsson March 10, 2025 18:51
@dumbbell dumbbell self-assigned this Mar 10, 2025
@kjnilsson kjnilsson added this to the 2.16.3 milestone Mar 11, 2025
@dumbbell dumbbell force-pushed the fix-delayed-query-handling branch from 1cb588f to a8b8a75 Compare March 11, 2025 09:56
[Why]
The reply of local query was formatted as:

    {reply, From, Reply}

However, the code was called in two different contexts:

1. When the local query was executed immediately, the reply tuple was
   interpreted by `gen_statem` and thus the reply was sent by the local
   Ra server process. Exactly what we wanted.

2. When the local query execution depended on a condition and might have to be
   delayed, it could be executed right away (and the reply tuple was
   interpreted by `gen_statem`), or the execution could be delayed and
   at the time of execution, the reply tuple could be interpreted as a
   Ra effect.

Unfortunately, the same tuple has a very different meaning depending on
who interprets it:
* `gen_statem` will send the reply regardless of the Raft state of the
  Ra server process.
* The Ra effect will only send the reply if the Ra server process is
  acting as the leader.

The delayed query was always processed by the Ra server that received
it, regardless of its state. This means that if the Ra server is not a
leader and the reply tuple is interpreted as a Ra effect, the caller
will never get an answer.

This led to some timeouts in Khepri and nasty bugs in RabbitMQ. In
particular, it caused the `peer_discovery_classic_config_SUITE` to fail
quite frequently in RabbitMQ CI.

[How]
First, the patch ensures the reply tuple is always interpreted as a Ra effect.

Then, it sets the `{member, ...}` replier option in the reply effect to
the Ra server that executes the query. This way, the reply is always
emitted emitted and by the correct Ra server, regardless of its Raft
state.

V2: Modify the existing testcase to make sure a local query sent to the
    follower that should block reaches the follower before it can
    fulfill it. Indeed, previously, that test against the follower would
    be serially after the same test against the leader. Therefore, the
    blocking would happen with the leader and when it was the turn of
    the follower, it would be able to execute the query right away.

    Now, both tests are executed in parallel, with timers to ensure the
    follower gets the query first, way before the command it is waiting
    for is sent.

    This modified testcase was tested without the fix and it
    demonstrated the problem successfully; in other words, the testcase
    failed 100% of the time without the fix.
@dumbbell dumbbell force-pushed the fix-delayed-query-handling branch from a8b8a75 to 0100c43 Compare March 11, 2025 14:30
@kjnilsson kjnilsson merged commit 05ab1d8 into main Mar 12, 2025
3 checks passed
@dumbbell dumbbell deleted the fix-delayed-query-handling branch March 12, 2025 15:28
dumbbell added a commit to rabbitmq/khepri that referenced this pull request Mar 13, 2025
Release notes:
* https://github.com/rabbitmq/ra/releases/tag/v2.16.1
* https://github.com/rabbitmq/ra/releases/tag/v2.16.2
* https://github.com/rabbitmq/ra/releases/tag/v2.16.3

In particular, it fixes a bug with the handling of delayed queries'
reply (rabbitmq/ra#517). This caused some timeouts, especially in
`khepri_machine:fence/2`.
@dumbbell dumbbell moved this to Shipped in 4.1.x in Khepri as the default metadata store Apr 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Development

Successfully merging this pull request may close these issues.

2 participants