ChenXi created KAFKA-21129:
------------------------------
Summary: KRaft controller failover caused by an off-by-one in
non-atomic batch appending
Key: KAFKA-21129
URL: https://issues.apache.org/jira/browse/KAFKA-21129
Project: Kafka
Issue Type: Bug
Components: controller
Affects Versions: 4.3.1
Reporter: ChenXi
QuorumController.appendRecords
(metadata/src/main/java/org/apache/kafka/controller/QuorumController.java:918-933)
splits a non-atomic result into batches, but its
boundary check is off by one. When records.size() is an exact multiple of
maxRecordsPerBatch, the final iteration calls the appender with an empty
subList(size, size).
{code:java}
int startIndex = 0, numBatches = 0;
while (true) {
numBatches++;
int endIndex = startIndex + maxRecordsPerBatch;
if (endIndex > records.size()) {
long offset = appender.apply(records.subList(startIndex,
records.size()));
...
return offset;
} else {
appender.apply(records.subList(startIndex, endIndex));
}
startIndex += maxRecordsPerBatch;
}{code}
The production appender passes this empty list to RaftClient.prepareAppend, and
KafkaRaftClient.append rejects empty batches with IllegalArgumentException
("Append failed because there are no records").
Since appendRecords only converts ApiException, the exception escapes the event
handler and is treated as an unexpected fault, so the active controller
renounces leadership and the client receives UnknownServerException.
The preceding full batches may already have been appended, so the change can
take effect even though the client sees a failure.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)