Add RecordMetadata to ProducerListener.onError

When sending a record with no partition, and an error occurs, the
`RecordMetadata` might contain the assigned partition if the error occurs
after partition assignment; this may be useful information for the
application.

* Add @Nullable
This commit is contained in:
Gary Russell
2020-09-21 10:14:36 -04:00
committed by GitHub
parent be0cc7c012
commit 321b18f6c9
6 changed files with 46 additions and 8 deletions

View File

@@ -385,6 +385,7 @@ public class KafkaTemplate<K, V> implements KafkaOperations<K, V>, ApplicationCo
@Override
public ListenableFuture<SendResult<K, V>> send(ProducerRecord<K, V> record) {
Assert.notNull(record, "'record' cannot be null");
return doSend(record);
}
@@ -601,7 +602,7 @@ public class KafkaTemplate<K, V> implements KafkaOperations<K, V>, ApplicationCo
}
future.setException(new KafkaProducerException(producerRecord, "Failed to send", exception));
if (KafkaTemplate.this.producerListener != null) {
KafkaTemplate.this.producerListener.onError(producerRecord, exception);
KafkaTemplate.this.producerListener.onError(producerRecord, metadata, exception);
}
KafkaTemplate.this.logger.debug(exception, () -> "Failed to send: " + producerRecord);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2019 the original author or authors.
* Copyright 2018-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -74,8 +74,14 @@ public class CompositeProducerListener<K, V> implements ProducerListener<K, V> {
}
@Override
@Deprecated
public void onError(ProducerRecord<K, V> producerRecord, Exception exception) {
this.delegates.forEach(d -> d.onError(producerRecord, exception));
}
@Override
public void onError(ProducerRecord<K, V> producerRecord, RecordMetadata recordMetadata, Exception exception) {
this.delegates.forEach(d -> d.onError(producerRecord, recordMetadata, exception));
}
}

View File

@@ -18,8 +18,10 @@ package org.springframework.kafka.support;
import org.apache.commons.logging.LogFactory;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.clients.producer.RecordMetadata;
import org.springframework.core.log.LogAccessor;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
/**
@@ -65,7 +67,7 @@ public class LoggingProducerListener<K, V> implements ProducerListener<K, V> {
}
@Override
public void onError(ProducerRecord<K, V> record, Exception exception) {
public void onError(ProducerRecord<K, V> record, @Nullable RecordMetadata recordMetadata, Exception exception) {
LOGGER.error(exception, () -> {
StringBuffer logOutput = new StringBuffer();
logOutput.append("Exception thrown when sending a message");
@@ -79,7 +81,9 @@ public class LoggingProducerListener<K, V> implements ProducerListener<K, V> {
}
logOutput.append(" to topic ").append(record.topic());
if (record.partition() != null) {
logOutput.append(" and partition ").append(record.partition());
logOutput.append(" and partition ").append(recordMetadata != null
? recordMetadata.partition()
: record.partition());
}
logOutput.append(":");
return logOutput.toString();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2019 the original author or authors.
* Copyright 2015-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@ package org.springframework.kafka.support;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.clients.producer.RecordMetadata;
import org.springframework.lang.Nullable;
/**
* Listener for handling outbound Kafka messages. Exactly one of its methods will be invoked, depending on whether
* the write has been acknowledged or not.
@@ -49,8 +51,28 @@ public interface ProducerListener<K, V> {
* Invoked after an attempt to send a message has failed.
* @param producerRecord the failed record
* @param exception the exception thrown
* @deprecated in favor of {@link #onError(ProducerRecord, RecordMetadata, Exception)}.
*/
@Deprecated
default void onError(ProducerRecord<K, V> producerRecord, Exception exception) {
}
/**
* Invoked after an attempt to send a message has failed.
* @param producerRecord the failed record
* @param recordMetadata The metadata for the record that was sent (i.e. the partition
* and offset). If an error occurred, metadata will contain only valid topic and maybe
* the partition. If the partition is not provided in the ProducerRecord and an error
* occurs before partition is assigned, then the partition will be set to
* RecordMetadata.UNKNOWN_PARTITION.
* @param exception the exception thrown
* @since 2.6.2
*/
@SuppressWarnings("deprecation")
default void onError(ProducerRecord<K, V> producerRecord, @Nullable RecordMetadata recordMetadata,
Exception exception) {
onError(producerRecord, exception);
}
}

View File

@@ -53,6 +53,7 @@ import org.apache.kafka.clients.producer.RecordMetadata;
import org.apache.kafka.common.Metric;
import org.apache.kafka.common.MetricName;
import org.apache.kafka.common.PartitionInfo;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.errors.TimeoutException;
import org.apache.kafka.common.header.Header;
import org.apache.kafka.common.serialization.Serializer;
@@ -276,7 +277,9 @@ public class KafkaTemplateTests {
}
@Override
public void onError(ProducerRecord<Integer, String> producerRecord, Exception exception) {
public void onError(ProducerRecord<Integer, String> producerRecord, RecordMetadata metadata,
Exception exception) {
assertThat(producerRecord).isNotNull();
assertThat(exception).isNotNull();
onErrorDelegateCalls.incrementAndGet();
@@ -298,7 +301,8 @@ public class KafkaTemplateTests {
//Drain the topic
KafkaTestUtils.getSingleRecord(consumer, INT_KEY_TOPIC);
pf.destroy();
cpl.onError(records.get(0), new RuntimeException("x"));
cpl.onError(records.get(0), new RecordMetadata(new TopicPartition(INT_KEY_TOPIC, -1), 0L, 0L, 0L, 0L, 0, 0),
new RuntimeException("x"));
assertThat(onErrorDelegateCalls.get()).isEqualTo(2);
}

View File

@@ -309,7 +309,8 @@ public interface ProducerListener<K, V> {
void onSuccess(ProducerRecord<K, V> producerRecord, RecordMetadata recordMetadata);
void onError(ProducerRecord<K, V> producerRecord, Exception exception);
void onError(ProducerRecord<K, V> producerRecord, RecordMetadata recordMetadata,
Exception exception);
}
----