I am trying to achieve exactly once functionality but getting KafkaException with message as "org.apache.kafka.common.KafkaException: TransactionalId db13196c-6974-48b0-9835-aed40cec4ca4: Invalid transition attempted from state COMMITTING_TRANSACTION to state ABORTING_TRANSACTION". Below is my code for KafkaConfig and Producer:
@Configuration
@EnableKafka
public class KafkaConfig {
@Value("${spring.kafka.bootstrap-servers}")
private String bootstrapAddress;
private ProducerFactory<String, String> getProducerFactory() {
DefaultKafkaProducerFactory<String, String> factory =
new DefaultKafkaProducerFactory<>(getProducerConfigMap());
factory.setTransactionIdPrefix(KafkaEventConstants.TRANSACTION_ID_PREFIX);
return factory;
}
@Bean
public KafkaTemplate<String, String> getKafkaTemplate() {
return new KafkaTemplate<>(getProducerFactory());
}
private Map<String, Object> getProducerConfigMap() {
String randomProducerID = UUID.randomUUID().toString();
Map<String, Object> config = new HashMap<>();
config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress);
config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
config.put("enable.idempotence", "true");
config.put("transactional.id", randomProducerID);
return config;
}
@Bean
public KafkaProducer<String, String> producerFactory() {
KafkaProducer<String, String> producer = new KafkaProducer<>(getProducerConfigMap());
producer.initTransactions();
return producer;
}
}
Producer:
kafkaTemplate.executeInTransaction(
kafkaOperations -> {
kafkaPublisher.pushKafkaNotification(
topic, kafkaNotification.getUserId(), new JSONObject(kafkaNotification).toString());
return true;
});
public void pushKafkaNotification(
String topic, String partitionKey, String serializedKafkaNotification) {
try {
producer.beginTransaction();
ProducerRecord<String, String> producerRecord =
new ProducerRecord<String, String>(topic, partitionKey, serializedKafkaNotification);
producer.send(
producerRecord,
new Callback() {
@Override
public void onCompletion(RecordMetadata metadata, Exception exception) {
if (exception != null) {
log.error(
"Callback : Failed to push event to kafka for partition key, notification {} {}",
partitionKey,
serializedKafkaNotification,
exception);
} else {
log.info(
"Kafka Success Callback : Event pushed successfully to Kafka for partition key, notification {}, {}",
partitionKey,
serializedKafkaNotification);
}
}
});
producer.commitTransaction();
} catch (Exception e) {
producer.abortTransaction();
metricLogger.errorMetricLogging(SERVICE_NAME, ErrorMetrics.DLQ_PUBLISH_ERROR.getCode());
log.error("Exception while pushing notification to DLQ = {}", serializedKafkaNotification, e);
}
}