Skip to content

Commit 2740b07

Browse files
author
Zhiyang.Wang1
committed
DefaultErrorHandler#handleBatchAndReturnRemaining recovered invalid
DefaultErrorHandler#handleBatchAndReturnRemaining recovered invalid and infinite loop when kafka listener threw BatchListenerFailedException and error record is last one
1 parent b1e7623 commit 2740b07

File tree

2 files changed

+154
-91
lines changed

2 files changed

+154
-91
lines changed

spring-kafka/src/main/java/org/springframework/kafka/listener/FailedBatchProcessor.java

Lines changed: 31 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,10 @@
1616

1717
package org.springframework.kafka.listener;
1818

19-
import java.time.Duration;
2019
import java.util.ArrayList;
2120
import java.util.Collections;
2221
import java.util.HashMap;
2322
import java.util.HashSet;
24-
import java.util.Iterator;
2523
import java.util.List;
2624
import java.util.Map;
2725
import java.util.Set;
@@ -120,7 +118,7 @@ public void setReclassifyOnExceptionChange(boolean reclassifyOnExceptionChange)
120118
@Override
121119
protected void notRetryable(Stream<Class<? extends Exception>> notRetryable) {
122120
if (this.fallbackBatchHandler instanceof ExceptionClassifier handler) {
123-
notRetryable.forEach(ex -> handler.addNotRetryableExceptions(ex));
121+
notRetryable.forEach(handler::addNotRetryableExceptions);
124122
}
125123
}
126124

@@ -178,7 +176,6 @@ protected <K, V> ConsumerRecords<K, V> handle(Exception thrownException, Consume
178176
else {
179177
return String.format("Record not found in batch, index %d out of bounds (0, %d); "
180178
+ "re-seeking batch", index, data.count() - 1);
181-
182179
}
183180
});
184181
fallback(thrownException, data, consumer, container, invokeListener);
@@ -201,11 +198,9 @@ private int findIndex(ConsumerRecords<?, ?> data, ConsumerRecord<?, ?> record) {
201198
return -1;
202199
}
203200
int i = 0;
204-
Iterator<?> iterator = data.iterator();
205-
while (iterator.hasNext()) {
206-
ConsumerRecord<?, ?> candidate = (ConsumerRecord<?, ?>) iterator.next();
207-
if (candidate.topic().equals(record.topic()) && candidate.partition() == record.partition()
208-
&& candidate.offset() == record.offset()) {
201+
for (ConsumerRecord<?, ?> datum : data) {
202+
if (datum.topic().equals(record.topic()) && datum.partition() == record.partition()
203+
&& datum.offset() == record.offset()) {
209204
break;
210205
}
211206
i++;
@@ -220,29 +215,25 @@ private <K, V> ConsumerRecords<K, V> seekOrRecover(Exception thrownException, @N
220215
if (data == null) {
221216
return ConsumerRecords.empty();
222217
}
223-
Iterator<?> iterator = data.iterator();
224-
List<ConsumerRecord<?, ?>> toCommit = new ArrayList<>();
225218
List<ConsumerRecord<?, ?>> remaining = new ArrayList<>();
226219
int index = indexArg;
227-
while (iterator.hasNext()) {
228-
ConsumerRecord<?, ?> record = (ConsumerRecord<?, ?>) iterator.next();
220+
Map<TopicPartition, OffsetAndMetadata> offsets = new HashMap<>();
221+
for (ConsumerRecord<?, ?> datum : data) {
229222
if (index-- > 0) {
230-
toCommit.add(record);
223+
offsets.compute(new TopicPartition(datum.topic(), datum.partition()),
224+
(key, val) -> ListenerUtils.createOffsetAndMetadata(container, datum.offset() + 1));
231225
}
232226
else {
233-
remaining.add(record);
227+
remaining.add(datum);
234228
}
235229
}
236-
Map<TopicPartition, OffsetAndMetadata> offsets = new HashMap<>();
237-
toCommit.forEach(rec -> offsets.compute(new TopicPartition(rec.topic(), rec.partition()),
238-
(key, val) -> ListenerUtils.createOffsetAndMetadata(container, rec.offset() + 1)));
239230
if (offsets.size() > 0) {
240231
commit(consumer, container, offsets);
241232
}
242233
if (isSeekAfterError()) {
243234
if (remaining.size() > 0) {
244235
SeekUtils.seekOrRecover(thrownException, remaining, consumer, container, false,
245-
getFailureTracker()::recovered, this.logger, getLogLevel());
236+
getFailureTracker(), this.logger, getLogLevel());
246237
ConsumerRecord<?, ?> recovered = remaining.get(0);
247238
commit(consumer, container,
248239
Collections.singletonMap(new TopicPartition(recovered.topic(), recovered.partition()),
@@ -254,35 +245,33 @@ private <K, V> ConsumerRecords<K, V> seekOrRecover(Exception thrownException, @N
254245
return ConsumerRecords.empty();
255246
}
256247
else {
257-
if (indexArg == 0) {
258-
return (ConsumerRecords<K, V>) data; // first record just rerun the whole thing
259-
}
260-
else {
261-
try {
262-
if (getFailureTracker().recovered(remaining.get(0), thrownException, container,
263-
consumer)) {
264-
remaining.remove(0);
265-
}
266-
}
267-
catch (Exception e) {
248+
try {
249+
if (getFailureTracker().recovered(remaining.get(0), thrownException, container,
250+
consumer)) {
251+
remaining.remove(0);
268252
}
269-
Map<TopicPartition, List<ConsumerRecord<K, V>>> remains = new HashMap<>();
270-
remaining.forEach(rec -> remains.computeIfAbsent(new TopicPartition(rec.topic(), rec.partition()),
271-
tp -> new ArrayList<ConsumerRecord<K, V>>()).add((ConsumerRecord<K, V>) rec));
272-
return new ConsumerRecords<>(remains);
273253
}
254+
catch (Exception e) {
255+
}
256+
if (remaining.isEmpty()) {
257+
return ConsumerRecords.empty();
258+
}
259+
Map<TopicPartition, List<ConsumerRecord<K, V>>> remains = new HashMap<>();
260+
remaining.forEach(rec -> remains.computeIfAbsent(new TopicPartition(rec.topic(), rec.partition()),
261+
tp -> new ArrayList<>()).add((ConsumerRecord<K, V>) rec));
262+
return new ConsumerRecords<>(remains);
274263
}
275264
}
276265

277-
private void commit(Consumer<?, ?> consumer, MessageListenerContainer container, Map<TopicPartition, OffsetAndMetadata> offsets) {
266+
private void commit(Consumer<?, ?> consumer, MessageListenerContainer container,
267+
Map<TopicPartition, OffsetAndMetadata> offsets) {
278268

279-
boolean syncCommits = container.getContainerProperties().isSyncCommits();
280-
Duration timeout = container.getContainerProperties().getSyncCommitTimeout();
281-
if (syncCommits) {
282-
consumer.commitSync(offsets, timeout);
269+
ContainerProperties properties = container.getContainerProperties();
270+
if (properties.isSyncCommits()) {
271+
consumer.commitSync(offsets, properties.getSyncCommitTimeout());
283272
}
284273
else {
285-
OffsetCommitCallback commitCallback = container.getContainerProperties().getCommitCallback();
274+
OffsetCommitCallback commitCallback = properties.getCommitCallback();
286275
if (commitCallback == null) {
287276
commitCallback = LOGGING_COMMIT_CALLBACK;
288277
}
@@ -304,8 +293,8 @@ private BatchListenerFailedException getBatchListenerFailedException(Throwable t
304293
throwable = throwable.getCause();
305294
checked.add(throwable);
306295

307-
if (throwable instanceof BatchListenerFailedException) {
308-
target = (BatchListenerFailedException) throwable;
296+
if (throwable instanceof BatchListenerFailedException batchListenerFailedException) {
297+
target = batchListenerFailedException;
309298
break;
310299
}
311300
}

0 commit comments

Comments
 (0)