You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ratis.apache.org by sz...@apache.org on 2026/10/01 16:57:02 UTC
(ratis) branch master updated: RATIS-2723. LogAppender purge tests may fail after a leader change. (#1619)
This is an automated email from the ASF dual-hosted git repository.
szetszwo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ratis.git
The following commit(s) were added to refs/heads/master by this push:
new dd518d91e RATIS-2723. LogAppender purge tests may fail after a leader change. (#1619)
dd518d91e is described below
commit dd518d91e74f381dff894b3c13a3fc68a5a914a9
Author: slfan1989 <55...@users.noreply.github.com>
AuthorDate: Fri Oct 2 00:56:57 2026 +0800
RATIS-2723. LogAppender purge tests may fail after a leader change. (#1619)
---
.../java/org/apache/ratis/LogAppenderTests.java | 64 ++++++++++++++++------
1 file changed, 46 insertions(+), 18 deletions(-)
diff --git a/ratis-server/src/test/java/org/apache/ratis/LogAppenderTests.java b/ratis-server/src/test/java/org/apache/ratis/LogAppenderTests.java
index 8a8731daf..77b23a8b8 100644
--- a/ratis-server/src/test/java/org/apache/ratis/LogAppenderTests.java
+++ b/ratis-server/src/test/java/org/apache/ratis/LogAppenderTests.java
@@ -231,11 +231,8 @@ public abstract class LogAppenderTests<CLUSTER extends MiniRaftCluster>
final RaftProperties prop = getProperties();
RaftServerConfigKeys.Log.setPurgeGap(prop, 1);
RaftServerConfigKeys.Log.setSegmentSizeMax(prop, SizeInBytes.valueOf("1KB"));
- runWithNewCluster(3, cluster -> {
- final long startIndexAfterPurge = setupPurgedLeaderLog(cluster);
- // Test when followerNextIndex < leader's logStartIndex
- runTestNewAppendEntriesRequestAfterPurge(cluster, startIndexAfterPurge - 1);
- });
+ // Test when followerNextIndex < leader's logStartIndex.
+ runWithNewCluster(3, cluster -> runTestNewAppendEntriesRequestAfterPurge(cluster, true));
}
@Test
@@ -243,23 +240,55 @@ public abstract class LogAppenderTests<CLUSTER extends MiniRaftCluster>
final RaftProperties prop = getProperties();
RaftServerConfigKeys.Log.setPurgeGap(prop, 1);
RaftServerConfigKeys.Log.setSegmentSizeMax(prop, SizeInBytes.valueOf("1KB"));
- runWithNewCluster(3, cluster -> {
- final long startIndexAfterPurge = setupPurgedLeaderLog(cluster);
- // Test when followerNextIndex == leader's logStartIndex, but the previous index is already purged
- runTestNewAppendEntriesRequestAfterPurge(cluster, startIndexAfterPurge);
- });
+ // Test when followerNextIndex == leader's logStartIndex, but the previous index is already purged.
+ runWithNewCluster(3, cluster -> runTestNewAppendEntriesRequestAfterPurge(cluster, false));
}
- private long setupPurgedLeaderLog(CLUSTER cluster) throws Exception {
- final RaftServer.Division leader = waitForLeader(cluster);
- final RaftLog leaderLog = leader.getRaftLog();
+ private void runTestNewAppendEntriesRequestAfterPurge(CLUSTER cluster,
+ boolean followerBehindStartIndex) throws Exception {
+ final int maxAttempts = 3;
+ for (int attempt = 1; attempt <= maxAttempts; attempt++) {
+ try (RaftClient client = cluster.createClient(waitForLeader(cluster).getId())) {
+ for (SimpleMessage msg : generateMsgs(5)) {
+ client.io().send(msg);
+ }
+ }
- try (RaftClient client = cluster.createClient(leader.getId())) {
- for (SimpleMessage msg : generateMsgs(5)) {
- client.io().send(msg);
+ // Sending messages may change the leader. Use the same leader and term for purge and verification.
+ final RaftServer.Division leader = waitForLeader(cluster);
+ final long term = leader.getInfo().getCurrentTerm();
+ try {
+ final long startIndexAfterPurge = setupPurgedLeaderLog(leader);
+ // Verify only if the node is still the leader in the term used for purge.
+ if (isLeaderInTerm(leader, term)) {
+ runTestNewAppendEntriesRequestAfterPurge(leader,
+ followerBehindStartIndex ? startIndexAfterPurge - 1 : startIndexAfterPurge);
+ // Leadership may change during verification; accept the result only if it is still valid.
+ if (isLeaderInTerm(leader, term)) {
+ return;
+ }
+ }
+ } catch (InterruptedException e) {
+ throw e;
+ } catch (Exception | AssertionError e) {
+ if (isLeaderInTerm(leader, term)) {
+ throw e;
+ }
+ LOG.info("Leader {} changed during purge verification in term {}", leader.getId(), term, e);
}
+ LOG.info("Leader {} changed in term {} during purge test (attempt {}/{})",
+ leader.getId(), term, attempt, maxAttempts);
}
+ Assertions.fail("Leader changed during all " + maxAttempts + " purge test attempts");
+ }
+ // Check both role and term: the same server may step down and become leader again in a later term.
+ private static boolean isLeaderInTerm(RaftServer.Division leader, long term) {
+ return leader.getInfo().isLeader() && leader.getInfo().getCurrentTerm() == term;
+ }
+
+ private long setupPurgedLeaderLog(RaftServer.Division leader) throws Exception {
+ final RaftLog leaderLog = leader.getRaftLog();
final long lastLogIndex = leaderLog.getLastEntryTermIndex().getIndex();
LOG.info("Leader log lastIndex={}, startIndex={}", lastLogIndex, leaderLog.getStartIndex());
Assertions.assertTrue(lastLogIndex > 5, "Need enough log entries for the test");
@@ -281,9 +310,8 @@ public abstract class LogAppenderTests<CLUSTER extends MiniRaftCluster>
return startIndexAfterPurge;
}
- void runTestNewAppendEntriesRequestAfterPurge(CLUSTER cluster,
+ void runTestNewAppendEntriesRequestAfterPurge(RaftServer.Division leader,
long targetNextIndex) throws Exception {
- final RaftServer.Division leader = waitForLeader(cluster);
final RaftLog leaderLog = leader.getRaftLog();
final long startIndexAfterPurge = leaderLog.getStartIndex();