You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kafka.apache.org by sh...@apache.org on 2024/01/23 02:19:15 UTC
(kafka) branch 3.7 updated: KAFKA-16144: skip checkQuorum for only 1 voter case (#15235)
This is an automated email from the ASF dual-hosted git repository.
showuon pushed a commit to branch 3.7
in repository https://gitbox.apache.org/repos/asf/kafka.git
The following commit(s) were added to refs/heads/3.7 by this push:
new aaf3a2f72f7 KAFKA-16144: skip checkQuorum for only 1 voter case (#15235)
aaf3a2f72f7 is described below
commit aaf3a2f72f7e03f25c97495936204051e8b75bb6
Author: Luke Chen <sh...@gmail.com>
AuthorDate: Tue Jan 23 11:17:53 2024 +0900
KAFKA-16144: skip checkQuorum for only 1 voter case (#15235)
When there's only 1 voter, there will be no fetch request from other voters. In this case, we should still not expire the checkQuorum timer because there's just 1 voter.
Reviewers: Mickael Maison <mi...@gmail.com>, Federico Valeri <fe...@gmail.com>, José Armando García Sancio <js...@apache.org>
---
.../src/main/java/org/apache/kafka/raft/LeaderState.java | 4 ++++
.../java/org/apache/kafka/raft/KafkaRaftClientTest.java | 16 ++++++++++++++++
.../test/java/org/apache/kafka/raft/LeaderStateTest.java | 16 ++++++++++++++++
3 files changed, 36 insertions(+)
diff --git a/raft/src/main/java/org/apache/kafka/raft/LeaderState.java b/raft/src/main/java/org/apache/kafka/raft/LeaderState.java
index 14e327208f7..cfa50d402a2 100644
--- a/raft/src/main/java/org/apache/kafka/raft/LeaderState.java
+++ b/raft/src/main/java/org/apache/kafka/raft/LeaderState.java
@@ -101,6 +101,10 @@ public class LeaderState<T> implements EpochState {
* @return the remainingMs before the checkQuorumTimer expired
*/
public long timeUntilCheckQuorumExpires(long currentTimeMs) {
+ // if there's only 1 voter, it should never get expired.
+ if (voterStates.size() == 1) {
+ return Long.MAX_VALUE;
+ }
checkQuorumTimer.update(currentTimeMs);
long remainingMs = checkQuorumTimer.remainingMs();
if (remainingMs == 0) {
diff --git a/raft/src/test/java/org/apache/kafka/raft/KafkaRaftClientTest.java b/raft/src/test/java/org/apache/kafka/raft/KafkaRaftClientTest.java
index 6d714daa11b..51a5938220f 100644
--- a/raft/src/test/java/org/apache/kafka/raft/KafkaRaftClientTest.java
+++ b/raft/src/test/java/org/apache/kafka/raft/KafkaRaftClientTest.java
@@ -539,6 +539,22 @@ public class KafkaRaftClientTest {
context.assertResignedLeader(epoch, localId);
}
+ @Test
+ public void testLeaderShouldNotResignLeadershipIfOnlyOneVoters() throws Exception {
+ int localId = 0;
+ Set<Integer> voters = Utils.mkSet(localId);
+
+ RaftClientTestContext context = new RaftClientTestContext.Builder(localId, voters).build();
+ assertEquals(OptionalInt.of(localId), context.currentLeader());
+
+ // checkQuorum timeout is expired without receiving fetch request from other voters, but since there is only 1 voter,
+ // the leader should not get resigned
+ context.time.sleep(context.checkQuorumTimeoutMs);
+ context.client.poll();
+
+ assertFalse(context.client.quorum().isResigned());
+ }
+
@Test
public void testElectionTimeoutAfterUserInitiatedResign() throws Exception {
int localId = 0;
diff --git a/raft/src/test/java/org/apache/kafka/raft/LeaderStateTest.java b/raft/src/test/java/org/apache/kafka/raft/LeaderStateTest.java
index 97d290fa347..12cb2ffef20 100644
--- a/raft/src/test/java/org/apache/kafka/raft/LeaderStateTest.java
+++ b/raft/src/test/java/org/apache/kafka/raft/LeaderStateTest.java
@@ -489,6 +489,22 @@ public class LeaderStateTest {
assertEquals(0, state.timeUntilCheckQuorumExpires(time.milliseconds()));
}
+ @Test
+ public void testCheckQuorumWithOneVoter() {
+ int observer = 1;
+ // Only 1 voter quorum
+ LeaderState<?> state = newLeaderState(mkSet(localId), 0L);
+ assertEquals(Long.MAX_VALUE, state.timeUntilCheckQuorumExpires(time.milliseconds()));
+
+ // When checkQuorum timeout not exceeded and got no fetch request from voter, it should not expire the timer
+ time.sleep(checkQuorumTimeoutMs);
+ assertEquals(Long.MAX_VALUE, state.timeUntilCheckQuorumExpires(time.milliseconds()));
+
+ // received fetch requests from 1 observer node, the timer still return Long.MAX_VALUE.
+ state.updateCheckQuorumForFollowingVoter(observer, time.milliseconds());
+ assertEquals(Long.MAX_VALUE, state.timeUntilCheckQuorumExpires(time.milliseconds()));
+ }
+
@Test
public void testNoOpForNegativeRemoteNodeId() {
MockTime time = new MockTime();