Siyao Meng created HDDS-16929:
---------------------------------

             Summary: SCM follower stops applying the Raft log, while reporting 
RUNNING, if the transaction buffer init fails after a checkpoint install
                 Key: HDDS-16929
                 URL: https://issues.apache.org/jira/browse/HDDS-16929
             Project: Apache Ozone
          Issue Type: Bug
            Reporter: Siyao Meng
         Attachments: 
MC-5-fail-reinitialize-when-transaction-buffer-init-fails.patch, 
TestBugMC5BufferInitFailureStaleApplied.java

h3. Mechanism
{{SCMStateMachine.reinitialize}} installs the downloaded checkpoint and then 
calls {{transactionBuffer.init()}} and {{setLastAppliedTermIndex}} inside a try 
block whose catch only logs "Failed to unpause". If {{init()}} throws, the 
state machine still resumes with the transaction info and latest snapshot from 
before the install. Ratis {{StateMachineUpdater.reload}} then takes that old 
snapshot index (-1 for a new SCM) as the applied index. Ratis purged the 
follower's log up to the checkpoint index when it acknowledged the install, so 
the entry after the applied index no longer exists and the updater never 
applies anything again. The leader does not send another install notification, 
because the follower's log is consistent with its own.

h3. Trigger
# A follower SCM installs the leader's checkpoint through the normal Ratis 
install notification (for example a new SCM bootstrapping, or a lagging 
follower with {{ozone.scm.ha.ratis.log.purge.enabled}}).
# {{SCMHADBTransactionBufferImpl.init}} fails during {{reinitialize}}, for 
example a read error on the transaction info key of the newly installed DB.

The second step is unlikely: {{installCheckpoint}} reads and decodes the same 
key from the checkpoint moments earlier, so it needs a transient read error at 
that instant. In the reproduction it is injected.

h3. Impact
* The follower reports RUNNING but never applies another entry and never starts 
its datanode server. It is missing every container created after the install.
* A graceful stop of that SCM never completes, because {{StateMachineUpdater}} 
waits to reach the commit index; the updater thread also spins on one core. It 
has to be killed. Argued, not run: after a restart it opens the installed DB 
and catches up, unless it was elected leader in the meantime, because the flush 
on becoming leader ({{SCMStateMachine.notifyLeaderChanged}}) writes the stale 
transaction info into that DB.
* Argued, not run for this trigger: its log is complete, so it can be elected 
leader, and then no SCM is a ready leader. I ran that outcome for the same 
applied index below log start state reached through a failed checkpoint copy.

h3. Reproduction
PASS on unmodified source. [^TestBugMC5BufferInitFailureStaleApplied.java] 
({{hadoop-ozone/integration-test}}) uses the cluster shape of 
{{TestSCMInstallSnapshotWithHA}} (three SCMs, one started late, so it installs 
the leader's checkpoint through Ratis; log purge enabled, purge gap 5, snapshot 
threshold 5) and injects one {{RocksDatabaseException}} into the transaction 
info read that {{SCMHADBTransactionBufferImpl.init}} does inside 
{{reinitialize}}, with a Mockito spy on the follower's {{SCMMetadataStore}}. 
Result: lifecycle RUNNING, DB transaction index 202, Ratis applied index -1, 
log start 203, commit 238, 16 of 16 later containers missing, datanode server 
not started, about 4.9 s of updater CPU in 5 s, graceful stop still running 
after 20 s. A passing test means the defect is present.

h3. Patch
[^MC-5-fail-reinitialize-when-transaction-buffer-init-fails.patch], against 
b0aa6475b7856d2c7f467dd3db12b9a1dd116f63. It also applies to master at 
52812559dad. It conflicts textually with the patch attached to HDDS-16923 in 
{{SCMStateMachine}} and {{TestSCMStateMachine}}; whichever lands second needs a 
rebase.

The patch removes the catch, so the failure propagates from {{reinitialize}} 
like a failure of {{installCheckpoint}} already does. Ratis then closes the 
server and SCM shuts down; on restart it opens the installed DB, whose 
transaction info is the checkpoint index, and catches up. With the patch the 
reproduction fails at its first assertion because the follower SCM stops, and a 
restart of that follower applied up to the leader's index and started its 
datanode server (local variant of the reproduction, not attached).

Covered by {{testReinitializeFailsWhenTransactionBufferInitFails}} and 
{{testReinitializeResumesAtInstalledCheckpoint}} in the existing 
{{TestSCMStateMachine}}. Without the change the first fails with "Expected 
java.io.IOException to be thrown, but nothing was thrown." With it 
{{TestSCMStateMachine}}, {{TestSCMHAManagerImpl}} and 
{{TestSCMHADBTransactionBufferImpl}} (31 tests) and the mini cluster suites 
{{TestSCMInstallSnapshotWithHA}} and {{TestSCMInstallSnapshot}} (5 tests) pass, 
checkstyle is clean.

Found by TLA+ model checking and code review of the SCM Ratis snapshot and 
checkpoint install paths under HDDS-15926, on commit 
b0aa6475b7856d2c7f467dd3db12b9a1dd116f63. Checked against HDDS and RATIS 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