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)

Reply via email to