[
https://issues.apache.org/jira/browse/HDDS-16839?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Siyao Meng updated HDDS-16839:
------------------------------
Attachment: CR-3-wait-for-membership-after-failed-bootstrap-request.patch
> A new OM treats any failed bootstrap reply as final and exits, although the
> leader may still be adding it or may already have logged the change
> -----------------------------------------------------------------------------------------------------------------------------------------------
>
> Key: HDDS-16839
> URL: https://issues.apache.org/jira/browse/HDDS-16839
> Project: Apache Ozone
> Issue Type: Bug
> Reporter: Siyao Meng
> Priority: Minor
> Attachments:
> CR-3-wait-for-membership-after-failed-bootstrap-request.patch,
> TestBugCR3BootstrapFailsButChangeCommits.java
>
>
> h3. Mechanism
> {{OzoneManager.bootstrap}} sends one {{BootstrapOMRequest}} and ends the
> process on any exception. The outcome of the request and the outcome of the
> membership change are not the same thing in two cases.
> # The leader answers {{success=false}} whenever
> {{OzoneManagerRatisServer.addOMToRatisRing}} throws, which includes losing
> leadership after the configuration entry that adds the new OM is already in
> the Raft log. The next leader can still commit that entry.
> # {{OMInterServiceProtocolClientSideImpl}} retries the request through the
> failover proxy when the RPC itself fails (for example on
> {{ozone.om.client.rpc.timeout}} or a lost connection). The first request
> keeps running on the leader, so the retry is answered with
> {{RATIS_BOOTSTRAP_ERROR}} "Reconfiguration is already in progress", which is
> not retried.
> In both cases the new OM logs "Failed to Bootstrap OM." and exits. It never
> looks at the Raft configuration it receives, although it is told about the
> committed change through
> {{OzoneManagerStateMachine.notifyConfigurationChanged}} like every other
> member.
> h3. Trigger
> Case 1 (run with timing help from the Ratis test hook, see Reproduction):
> # Three OMs. The new OM is started with {{ozone om --bootstrap}} and catches
> up.
> # The leader logs the configuration entry. One follower and the new OM are
> slow, so the entry cannot be committed, the leader loses its majority and
> steps down.
> # The pending change is answered with a failure and the new OM exits.
> # The slow follower recovers, a new leader is elected and commits the entry.
> Without the hook the leader has to lose leadership or stop between appending
> the first configuration entry of the change, which it does only after the new
> OM has caught up, and committing the last one. That is two commit round
> trips, a few milliseconds per bootstrap in a healthy ring (from the code).
> The test hook holds this window open.
> Case 2 (run without any hook): the first bootstrap request of the new OM
> times out on the new OM while the leader is still adding it, with
> {{ozone.om.client.rpc.timeout}} lowered to 200 ms in the test. The retry
> fails with "Reconfiguration is already in progress" and the new OM gives up
> about a quarter of a second after its first request reached the leader. With
> the default timeout of 15 minutes this needs a leader that takes longer than
> that to bring the new OM up to date, or a connection failure during that time
> (argued, not run).
> h3. Impact
> * Case 1: the bootstrap command fails and the process of the new OM ends, but
> the new OM is a voter in the committed Raft configuration of every existing
> OM. A ring of three becomes four voters with three running, so it tolerates
> no further failure until somebody notices and starts the OM. Starting it
> normally (without {{--bootstrap}}) is the way out; I did not run that step.
> * Case 2: a bootstrap that the leader was in the middle of completing is
> reported as failed. What the leader does after the new OM is gone was not run.
> h3. Reproduction
> PASS on unmodified source. [^TestBugCR3BootstrapFailsButChangeCommits.java]
> ({{hadoop-ozone/integration-test}}) shows case 1 with the real OMs of a mini
> cluster and its normal bootstrap. It uses the Ratis test hook
> {{CodeInjectionForTesting}} to delay the handling of AppendEntries on one
> follower and, from the entry that adds it, on the new OM. Only timing is
> changed. The leader steps down with {{LOST_MAJORITY_HEARTBEATS}}, the
> bootstrap fails with {{RATIS_BOOTSTRAP_ERROR}} "Failed to add OM
> omNode-bootstrap-1 in Ratis ring.", and one to two seconds later, after a new
> leader is elected, all three existing OMs have a stable Raft configuration
> with four voters that include the new OM, whose process has ended. A passing
> test means the defect is present.
> Case 2 is the first regression test in the patch.
> h3. Suggested fix
> The attached patch changes only the new OM. It narrows the window and does
> not close it.
> * If the change is committed later than the wait, the new OM still exits. In
> case 2 it now exits at the end of the wait instead of at once, so a leader
> that finishes just then leaves the voter of case 1 (from the code, not run).
> * In case 2 at the default {{ozone.om.client.rpc.timeout}} the first request
> has already run for 15 minutes when the retry is refused, so the patch helps
> only if the leader finishes within the wait that follows. Raising
> {{ozone.om.client.rpc.timeout}} in the config of the new OM avoids the retry
> in the first place (from the code, not run).
> * The patch can also report success too early. The condition reads only the
> state of the new OM, and it depends on the leader handing its current
> configuration to a new peer before the log. Ratis 3.3.1 does that with the
> snapshot notification the OM uses, once for each peer the leader starts to
> bring up to date; it is a property of that implementation, not of the Ratis
> API. A process that is started again with an empty disk while the leader is
> still bringing the earlier process of the same OM up to date gets the log
> from the start without that configuration. If its node ID was a member before
> and the leader's log still has those entries, the wait can end for the old
> configuration while the leader has not logged the new change. Run on a mini
> cluster: the second process reported a completed bootstrap while the latest
> entry in the leader's log was the old removal. Forced in that run: one log
> entry per request ({{ozone.om.ratis.log.appender.queue.num-elements=1}}), the
> second process started 0.6 s after the first had given up, and the old
> removal held back with the Ratis test hook. Not run without those. The leader
> is still adding the OM at that point, so the report is early, and wrong only
> if that attempt fails afterwards.
> The alternative to prefer changes the leader side.
> {{OzoneManagerRatisServer.addOMToRatisRing}} adds the new OM to the peer list
> without looking whether it is already a member. If it answered a repeated add
> of a member with success (Ratis already answers a request that changes
> nothing with success, once no change is in progress), a bounded retry in
> {{OzoneManager.bootstrap}} would take the answer from the leader, and the new
> OM would have nothing to infer. From the code, not run and not implemented
> here. The attached patch is what the new OM can do alone.
> h3. Patch
> [^CR-3-wait-for-membership-after-failed-bootstrap-request.patch], against
> 7fcf31294859d5d31163e016b5a9a63f21c6edd2. It also applies to master at
> 1cc6423590f (not built there).
> After a failed request {{OzoneManager.bootstrap}} logs a warning with the
> cause and the bound, and waits for at most
> {{ozone.om.ratis.server.failure.timeout.duration}} (default 120s) until the
> latest Raft configuration the new OM knows has the OM itself as a voter or
> listener, has been applied locally, and is newer than the configuration the
> OM had before the request and not older than any configuration it has been
> notified of. If that happens the OM logs a second warning and continues its
> startup, otherwise it fails as before. A bootstrap that fails for good is
> therefore reported up to that much later, which is noted in the description
> of the key in {{ozone-default.xml}}. No request or protocol change.
> The last two parts of the condition are for a node ID that was a member
> before. A new OM with an empty disk can replay old configurations that have
> it. The replay starts after the last transaction of the checkpoint it gets
> from the leader, so this needs a ring without writes since that membership
> (from the code, not run with a write). The leader's current configuration,
> which Ratis 3.3.1 hands to the new OM before the log, is dropped again by
> Ratis when the log is replayed in more than one request.
> {{OzoneManagerStateMachine}} therefore keeps the highest configuration index
> it has been notified of.
> Run: with a condition on the Ratis peer list of the OM, such an OM reports a
> completed bootstrap for a change that is not committed. With a condition on
> the latest configuration being applied alone it does so when the old log
> reaches it one entry per request
> ({{ozone.om.ratis.log.appender.queue.num-elements=1}}). With the default of
> 1024 the leader sent the old entries in one request and the start failed
> correctly.
> Covered in the existing {{TestAddRemoveOzoneManager}}:
> * {{testBootstrapWhenRequestFailsWhileOMIsAdded}} (case 2, a voter and then a
> listener). Without the change it fails with "Failed to Bootstrap OM. Error
> Code: RATIS_BOOTSTRAP_ERROR, Error Message: Reconfiguration is already in
> progress".
> * {{testBootstrapFailsWhenOnlyOldConfigurationHasOM}}: an OM is bootstrapped,
> decommissioned and bootstrapped again with an empty disk, its request fails,
> and the log entries from its removal on are held back with the Ratis test
> hook. The bootstrap has to fail after the wait with the original exception,
> while the latest configuration of the new OM is the old one that has it.
> With the change the ten tests of {{TestAddRemoveOzoneManager}} pass and
> checkstyle is clean. With the patch the reproduction fails at its first
> assertion because the bootstrap returns normally: the new OM logs the second
> warning one to two seconds after the failed reply, once the new leader has
> committed the change.
> Found by code review of the OM HA membership change paths (bootstrap,
> decommission and leader transfer), as part of the TLA+ verification effort
> under HDDS-15926, on commit 7fcf31294859d5d31163e016b5a9a63f21c6edd2. Checked
> against HDDS issues and apache/ozone pull requests for duplicates before
> filing. The attached patch is a proposal for review. Generated with Specula
> (Claude Opus 5.5).
--
This message was sent by Atlassian Jira
(v8.20.10#820010)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]