Siyao Meng created HDDS-17573:
---------------------------------

             Summary: OM follower that finishes a checkpoint install is never 
reloaded by Ratis when appends it accepted earlier reach its log after the 
notification
                 Key: HDDS-17573
                 URL: https://issues.apache.org/jira/browse/HDDS-17573
             Project: Apache Ozone
          Issue Type: Bug
            Reporter: Siyao Meng
         Attachments: TestBugMC4InstallNeverAcknowledged.java

h3. Mechanism
A follower OM installs a leader checkpoint when Ratis calls 
{{OzoneManagerStateMachine.notifyInstallSnapshotFromLeader}}. 
{{OzoneManager.installSnapshotFromLeader}} downloads and installs the 
checkpoint asynchronously and completes the future with the checkpoint 
TermIndex; it does not check whether the follower's log already reaches the 
leader's first index. In Ratis 3.3.1, {{SnapshotInstallationHandler}} keeps 
{{inProgressInstallSnapshotIndex}} set while the install runs, and 
{{RaftServerImpl.checkInconsistentAppendEntries}} rejects every append 
meanwhile ("installation is in progress") with the follower's log next index. A 
finished install is used (state machine reload, SNAPSHOT_INSTALLED) only when 
the next install notification arrives; that handling comes from RATIS-1481. The 
leader sends one only while the follower's next index is below the leader's 
first log index ({{LogAppender.shouldInstallSnapshot}}).

An append the follower accepted before the notification can reach its log only 
afterwards: with append compose (Ratis 3.2.0 and later) each accepted append is 
written after the previous one is flushed, and its consistency check does not 
run again. Once it lands, the follower reports a next index above the leader's 
first index, the leader stops notifying, and the finished install is never 
used. The comment in {{OzoneManager.installSnapshotFromLeader}} ("The follower 
refuses appendEntries while this install is in progress, so its commit index is 
fixed") does not hold either: the commit index also rises when that append 
lands.

h3. Trigger
# Follower F accepts appends from leader L1 while its log flush is slow, so the 
accepted entries are not in its log yet. F misses L1's last entries.
# Another OM L2 takes a snapshot and purges its log (default 
{{ozone.om.ratis.log.purge.upto.snapshot.index=true}}) past F's log end but not 
past what F accepted, and leadership moves to L2 (admin transfer or election).
# L2 sees F below its first log index and notifies F, F starts the install.
# F's flush completes, the accepted entries land, and F's log next index passes 
L2's first index.

h3. Impact
* F finishes the install but Ratis never reloads its state machine and F 
refuses every append from L2. It stays out of the quorum until it restarts, a 
leader purges past its next index again (about 1M transactions with the default 
{{ozone.om.ratis.log.purge.gap}}), or a leader whose first log index is above 
F's next index takes over (argued). An idle cluster does not recover.
* The cluster stays available with the other two OMs, but has no failure margin 
left.
* The trigger is rare: it needs a follower log stall that spans a leadership 
change and the new leader's purge, with the purge point falling inside the 
stalled range.
* The OM level effect is argued from the code, not run in an OM cluster. 
HDDS-16932 reaches the same Ratis state for SCM when leadership moves during 
the download; OM is exposed to that trigger as well, and since it needs no slow 
disk it is probably the more likely route for OM (argued). The SCM side fix 
suggested there (fail the future when the leader changed) does not carry over 
to OM, because OM swaps the DB before it completes the future. Here the state 
is reached when the leader that notified stays leader, through an append the 
follower accepted before the notification.

h3. Reproduction
PASS on unmodified source (the defect is present), 3 of 3 runs, about 22 s 
each. [^TestBugMC4InstallNeverAcknowledged.java] 
({{hadoop-ozone/ozone-manager}}) runs three in-process Ratis 3.3.1 servers over 
gRPC with OM's install settings (notification only, purge up to the snapshot 
index), small log segments and purge gap 1. Its state machine installs the 
leader's snapshot asynchronously and returns its TermIndex, as OM does. F's log 
flush is stalled once through the state machine's {{flush}} callback (standing 
in for a slow fsync), L1's last appends to F are held with the Ratis 
{{RaftServerImpl.appendEntries}} test hook, L2 takes a snapshot through the 
snapshot management API and leadership is transferred to it. Observed in one 
run: F accepted up to index 79 with log next index 21; L2's first index after 
purge 65; L2 notified F; after the flush was released F replied {{nextIndex=80, 
followerCommit=79}} (17 before). Other runs differ by a few indices before the 
release; the outcome was the same: F's install returned (t:1, i:80); after 30 
more writes L2 had committed 145 while F stayed at applied index 79, with no 
reload, no SNAPSHOT_INSTALLED and no further notification.

h3. Suggested fix
The fix belongs in Ratis: use a finished state machine install on the append 
path too. When an append or heartbeat arrives and the install has finished, 
reload the state machine as the notification path does, clear the in progress 
index and reply with the new next index. That also closes HDDS-16932 and the 
case where the state machine returns null. A check in OM alone cannot close it: 
failing the install future before the DB swap when the follower's log already 
contains the leader's first TermIndex is safe but only narrows the window, 
because the write can still land afterwards, and after the swap OM cannot fail 
the future without applying entries the checkpoint already contains.

As a possible mitigation only, turning append compose off for OM 
({{ozone.om.ha.raft.server.log.append-entries.compose.enabled=false}}) keeps 
accepted entries in the follower's log at once: with it, the reproduction's 
precondition fails (the follower's log next index is right after what it 
accepted, so the window does not open). This was tried in two runs of the 
reproduction only, is not a fix (it leaves the short window between the check 
and the append), its throughput cost is not measured, and it does not cover 
HDDS-16932's trigger.

h3. Patch
No patch. The reproduction is attached.

Found by TLA+ model checking and code review of the OM and SCM Ratis state 
machine integration (leader readiness, snapshot install and log failure 
handling) under HDDS-15926, on commit 424482eb9cfc4b835e1bf73bcdaa9e7d70df59f0. 
Checked against HDDS and RATIS issues and apache/ozone pull requests for 
duplicates before filing. Generated with Specula (Claude Opus 5.5).



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to