Skip to content

Commit d6a0aaf

Browse files
committed
KAFKA-20253: Extend heartbeat auth failure test to verify full recovery cycle
- Verify rejoin completes after authentication recovers - Verify a post-recovery heartbeat succeeds without another rejoin - Ensure the prepared heartbeat response is consumed
1 parent ee29e34 commit d6a0aaf

1 file changed

Lines changed: 27 additions & 3 deletions

File tree

clients/src/test/java/org/apache/kafka/clients/consumer/internals/AbstractCoordinatorTest.java

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,12 +1297,16 @@ public void testAuthenticationErrorInHeartbeatThreadTriggersRejoin() throws Exce
12971297
mockClient.prepareResponse(syncGroupResponse(Errors.NONE));
12981298

12991299
final AuthenticationException authError = new AuthenticationException("test auth failure");
1300+
// A matcher exception leaves the response queued, so fail only the first heartbeat.
1301+
final AtomicBoolean authErrorThrown = new AtomicBoolean(false);
13001302

13011303
mockClient.prepareResponse(body -> {
1302-
if (body instanceof HeartbeatRequest)
1304+
if (!(body instanceof HeartbeatRequest))
1305+
return false;
1306+
if (authErrorThrown.compareAndSet(false, true))
13031307
throw authError;
1304-
return false;
1305-
}, heartbeatResponse(Errors.UNKNOWN_SERVER_ERROR));
1308+
return true;
1309+
}, heartbeatResponse(Errors.NONE));
13061310

13071311
coordinator.ensureActiveGroup();
13081312
assertFalse(coordinator.rejoinNeededOrPending(),
@@ -1323,6 +1327,26 @@ public void testAuthenticationErrorInHeartbeatThreadTriggersRejoin() throws Exce
13231327
assertTrue(coordinator.rejoinNeededOrPending(),
13241328
"Expected the heartbeat thread to request a rejoin after an AuthenticationException " +
13251329
"so the next poll() restarts the heartbeat machinery via ensureActiveGroup()");
1330+
1331+
mockClient.prepareResponse(joinGroupFollowerResponse(2, memberId, leaderId, Errors.NONE));
1332+
mockClient.prepareResponse(syncGroupResponse(Errors.NONE));
1333+
1334+
coordinator.ensureActiveGroup();
1335+
1336+
assertFalse(coordinator.rejoinNeededOrPending(), "Coordinator should have rejoined the group");
1337+
assertEquals(2, coordinator.generation().generationId);
1338+
1339+
mockClient.prepareResponse(body -> body instanceof HeartbeatRequest, heartbeatResponse(Errors.NONE));
1340+
mockTime.sleep(HEARTBEAT_INTERVAL_MS);
1341+
1342+
// Ensure the response was consumed instead of passing before a heartbeat was sent.
1343+
TestUtils.waitForCondition(() -> {
1344+
coordinator.pollHeartbeat(mockTime.milliseconds());
1345+
return !mockClient.hasPendingResponses() && !coordinator.heartbeat().hasInflight();
1346+
}, 3000, "Heartbeat was not sent and completed after recovering from the authentication error");
1347+
1348+
assertFalse(coordinator.rejoinNeededOrPending(),
1349+
"A successful post-recovery heartbeat should not trigger another rejoin");
13261350
}
13271351

13281352
@Test

0 commit comments

Comments
 (0)