peterxcli opened a new pull request, #1567:
URL: https://github.com/apache/ratis/pull/1567

   ## What changes were proposed in this pull request?
   
   RATIS-2487 added a clause to `LogAppender.shouldInstallSnapshot` that 
triggers an install snapshot when the leader cannot provide the previous entry 
for appendEntries:
   
   ```java
   return followerNextIndex == leaderStartIndex &&
       followerNextIndex > RaftLog.LEAST_VALID_LOG_INDEX &&
       getPrevious(followerNextIndex) == null;
   ```
   
   However, `LogAppenderBase.newAppendEntriesRequest` (and `assertProtos`) 
explicitly allow sending appendEntries **without** the previous entry when 
`followerNextIndex == follower.getSnapshotIndex() + 1`. Since the appender run 
loop consults `shouldNotifyToInstallSnapshot()` before building an append 
request, that exemption is unreachable and the leader gets stuck in an infinite 
loop:
   
   1. The follower's `nextIndex` equals the leader's log start index (the 
previous entry is purged), and the leader state machine's latest snapshot index 
does not exactly equal `leaderStartIndex - 1`, so `getPrevious` returns null. 
This is the normal situation for DB-backed state machines (e.g. Ozone OM/SCM) 
whose `getLatestSnapshot()` advances with the applied index.
   2. The leader sends an InstallSnapshotNotification for the first available 
log entry.
   3. The follower replies `ALREADY_INSTALLED` with its snapshot index `== 
leaderStartIndex - 1` (`SnapshotInstallationHandler`: `snapshotIndex + 1 >= 
firstAvailableLogIndex`).
   4. The leader records `follower.snapshotIndex`, sets `nextIndex = 
leaderStartIndex`, and goes back to step 2.
   
   The loop is tight (observed ~100 notification RPCs per second). The 
follower's matchIndex never advances, so `TransferLeadership` always times out; 
in a 3-node group with one node down, the leader can never commit its 
placeholder entry, i.e. the group loses write availability.
   
   This PR adds the same exemption to `shouldInstallSnapshot`: when 
`followerNextIndex == follower.getSnapshotIndex() + 1`, do not install a 
snapshot — appendEntries can proceed without the previous entry. This restores 
the recovery path that worked before RATIS-2487 (verified against Ratis 3.2.1 
behavior, where the `ALREADY_INSTALLED` reply leads to a successful append).
   
   This was found while upgrading Apache Ozone to Ratis 3.3.0 
([HDDS-16196](https://issues.apache.org/jira/browse/HDDS-16196), 
apache/ozone#11032): `TestOzoneManagerHAWithStoppedNodes` wedges consistently 
with the leader looping `notifyInstallSnapshot` / `ALREADY_INSTALLED` while 
`TransferLeadership` reports `NOT_UP_TO_DATE(followerMatchIndex = 158 < 
leaderLastEntry.getIndex() = 167)`.
   
   ## What is the link to the Apache JIRA
   
   https://issues.apache.org/jira/browse/RATIS-2500
   
   ## How was this patch tested?
   
   New unit tests in `TestLogAppenderDefault`; 
`noInstallSnapshotWhenFollowerSnapshotCoversPreviousEntry` fails without the 
fix and passes with it. Existing tests pass. The failure scenario was 
reproduced with Ozone's `TestOzoneManagerHAWithStoppedNodes` on Ratis 3.3.0.
   
   Generated-by: Claude Code (claude-fable-5)
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to