Siyao Meng created HDDS-16832:
---------------------------------

             Summary: OM that started as a single node fails every checkpoint 
install from the first OM bootstrapped onto it with a NullPointerException
                 Key: HDDS-16832
                 URL: https://issues.apache.org/jira/browse/HDDS-16832
             Project: Apache Ozone
          Issue Type: Bug
            Reporter: Siyao Meng
         Attachments: 
CR-6-add-first-bootstrapped-om-to-snapshot-provider.patch, 
TestBugCR6OldOMCannotInstallCheckpointFromFirstNewOM.java

h3. Mechanism
An OM that starts with no peers has no {{OmRatisSnapshotProvider}}. 
{{OzoneManager.addOMNodeToPeers}} creates it when the first OM is bootstrapped, 
from {{peerNodesMap}} as it is before the new node is put in, and only the 
{{else}} branch (the provider already exists) calls {{addNewPeerNode}}. The 
provider keeps its own copy of the map, so the first bootstrapped OM is never 
in it. OMs bootstrapped later are added.

{{OmRatisSnapshotProvider.downloadSnapshot}} uses the result of 
{{peerNodesMap.get(leaderNodeID)}} without a null check. When the first 
bootstrapped OM is leader and notifies the original OM to install a snapshot, 
the lookup returns null and a {{NullPointerException}} leaves 
{{OzoneManager.installSnapshotFromLeader}}, which catches only {{IOException}}. 
Ratis logs "Failed to notify StateMachine to InstallSnapshot" and the leader 
sends the notification again. Nothing adds the missing peer later, because 
{{OzoneManager.updatePeerList}} only adds nodes that the Ratis server's peer 
list does not have yet.

The {{addOMNodeToPeers}} block is the same in the release tags from 1.2.0 to 
2.2.1 (read from the tagged source, not run there). It came in with HDDS-4330.

h3. Trigger
# One OM runs with a service ID as a single node ring.
# Two more OMs are bootstrapped. The existing OM is not restarted, which the OM 
HA documentation says is not needed.
# The first bootstrapped OM becomes leader.
# The original OM falls behind the leader's purged log while its process stays 
up (a partition or a long pause), so the leader notifies it to install a 
snapshot.

h3. Impact
The original OM cannot catch up while the first bootstrapped OM leads. It stays 
a member of the ring but no longer counts towards the commit majority, so the 
ring cannot tolerate the loss of the other follower. Nothing reports this 
except the ERROR lines in the original OM's log. There is no data loss. 
Restarting the original OM is a workaround, because the provider is then built 
from the configuration.

Run on a mini cluster with a short Raft log (purge gap and snapshot threshold 
of 50): the original OM stayed at applied index 8 while the leader was at 102, 
with 100 to 200 failed install attempts per second (2138 in 20 seconds in one 
run, 3994 in 21 seconds in another), each one an ERROR line on the original OM. 
A write with all three OMs up succeeded. With the second bootstrapped OM 
stopped, a write did not complete within 20 seconds. After the original OM was 
stopped and started again as a new {{OzoneManager}} from its configuration and 
storage directory, it installed a checkpoint from the same leader and a write 
succeeded.

Not exercised: default log settings (snapshot every 400,000 transactions, so 
the OM has to miss far more), a real network partition, a secure cluster, and 
the conversion of an OM that had no service ID.

h3. Reproduction
PASS in 2 of 2 runs on unmodified source. 
[^TestBugCR6OldOMCannotInstallCheckpointFromFirstNewOM.java] 
({{hadoop-ozone/integration-test}}) starts a mini cluster with one OM, 
bootstraps two OMs, transfers leadership to the first and writes through the 
client. The original OM is made to miss the transactions without a restart by 
rejecting the {{appendEntries}} and {{installSnapshot}} calls it receives, 
through the Ratis {{CodeInjectionForTesting}} hooks at the start of these 
handlers. This stands for a partition and changes no state. The hooks are then 
removed and the test observes the failed attempts, the two writes and the 
restart. A passing test means the defect is present.

h3. Suggested fix
The attached patch removes the cause. A null check in 
{{OmRatisSnapshotProvider.downloadSnapshot}} that throws an {{IOException}} 
naming the unknown leader would in addition send any future missing peer 
through the handled error path. It is not in the patch, since it makes no 
install succeed.

h3. Patch
[^CR-6-add-first-bootstrapped-om-to-snapshot-provider.patch], against 
7fcf31294859d5d31163e016b5a9a63f21c6edd2. It also applies to master at 
1cc6423590f (not built there).

{{OzoneManager.addOMNodeToPeers}} now calls {{addNewPeerNode}} for the new OM 
whether the provider already existed or was just created. For an OM that starts 
with peers nothing changes.

Covered by an addition to the existing 
{{TestAddRemoveOzoneManager.testBootstrap}}: after the two OMs are 
bootstrapped, leadership moves to the first new OM, the old OM downloads a 
checkpoint from it through its snapshot provider, and leadership moves back. 
Without the change it fails with the {{NullPointerException}} above. With it 
{{TestAddRemoveOzoneManager}}, {{TestOMInstallSnapshotDuringBootstrapping}} and 
{{TestOzoneManagerSnapshotProvider}} pass (10 tests), as do 
{{TestOzoneManagerStateMachine}}, {{TestOmRatisSnapshotProvider}} and 
{{TestOzoneManagerRatisServer}} (74 tests), and checkstyle is clean. With the 
patch the reproduction class fails at the assertion that expects the original 
OM to stay behind, with 0 failed install attempts.

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]

Reply via email to