title: Transactional Kafka Producer
body: <p>I am trying to make make my kafka producer transactional.
  I am sending 10 messages .If any error occurs no message should be sent to kafka i.e none or all.</p>
  
  <p>I am using Spring Boot KafkaTemplate.</p>
  
  <pre class="lang-java prettyprint-override"><code>@Configuration
  @EnableKafka
  public class KakfaConfiguration {
  
      @Bean
      public ProducerFactory&lt;String, String&gt; producerFactory() {
          Map&lt;String, Object&gt; config = new HashMap&lt;&gt;();
  
          // props.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "SSL");
          // props.put(SslConfigs.SSL_TRUSTSTORE_LOCATION_CONFIG,
          // appProps.getJksLocation());
          // props.put(SslConfigs.SSL_TRUSTSTORE_PASSWORD_CONFIG,
          // appProps.getJksPassword());
          config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
          config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
          config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
          config.put(ProducerConfig.ACKS_CONFIG, acks);
          config.put(ProducerConfig.RETRY_BACKOFF_MS_CONFIG, retryBackOffMsConfig);
          config.put(ProducerConfig.RETRIES_CONFIG, retries);
          config.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true);
          config.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG, "prod-99");
  
          return new DefaultKafkaProducerFactory&lt;&gt;(config);
      }
  
      @Bean
      public KafkaTemplate&lt;String, String&gt; kafkaTemplate() {
          return new KafkaTemplate&lt;&gt;(producerFactory());
      }
  
      @Bean(name = "ktm")
      public KafkaTransactionManager kafkaTransactionManager() {
          KafkaTransactionManager ktm = new KafkaTransactionManager(producerFactory());
          ktm.setTransactionSynchronization(AbstractPlatformTransactionManager.SYNCHRONIZATION_ON_ACTUAL_TRANSACTION);
          return ktm;
      }
  
  }
  </code></pre>
  
  <p>I am sending 10 messages like below as mentioned in the document. 9 messages should be sent and I message has size over 1MB which gets rejected by Kafka broker due to <code>RecordTooLargeException</code></p>
  
  <blockquote>
    <p><a href="https://docs.spring.io/spring-kafka/reference/html/#using-kafkatransactionmanager" rel="nofollow noreferrer">https://docs.spring.io/spring-kafka/reference/html/#using-kafkatransactionmanager</a></p>
  </blockquote>
  
  <pre class="lang-java prettyprint-override"><code>@Component
  @EnableTransactionManagement
  class Sender {
  
      @Autowired
      private KafkaTemplate&lt;String, String&gt; template;
  
      private static final Logger LOG = LoggerFactory.getLogger(Sender.class);
  
      @Transactional("ktm")
      public void sendThem(List&lt;String&gt; toSend) throws InterruptedException {
          List&lt;ListenableFuture&lt;SendResult&lt;String, String&gt;&gt;&gt; futures = new ArrayList&lt;&gt;();
          CountDownLatch latch = new CountDownLatch(toSend.size());
          ListenableFutureCallback&lt;SendResult&lt;String, String&gt;&gt; callback = new ListenableFutureCallback&lt;SendResult&lt;String, String&gt;&gt;() {
  
              @Override
              public void onSuccess(SendResult&lt;String, String&gt; result) {
                  LOG.info(" message sucess : " + result.getProducerRecord().value());
                  latch.countDown();
              }
  
              @Override
              public void onFailure(Throwable ex) {
                  LOG.error("Message Failed ");
                  latch.countDown();
              }
          };
  
          toSend.forEach(str -&gt; {
              ListenableFuture&lt;SendResult&lt;String, String&gt;&gt; future = template.send("t_101", str);
              future.addCallback(callback);
          });
  
          if (latch.await(12, TimeUnit.MINUTES)) {
              LOG.info("All sent ok");
          } else {
              for (int i = 0; i &lt; toSend.size(); i++) {
                  if (!futures.get(i).isDone()) {
                      LOG.error("No send result for " + toSend.get(i));
                  }
              }
          }
  </code></pre>
  
  <p>But when I see the topic t_hello_world 9 messages are there. My expectation was to see 0 messages as my producer is transactional.
  How can I achieve it?</p>
  
  <p>I am getting the following logs </p>
  
  <pre><code>2020-04-30 18:04:36.036 ERROR 18688 --- [   scheduling-1] o.s.k.core.DefaultKafkaProducerFactory   : commitTransaction failed: CloseSafeProducer [delegate=org.apache.kafka.clients.producer.KafkaProducer@1eb5a312, txId=prod-990]
  
  org.apache.kafka.common.KafkaException: Cannot execute transactional method because we are in an error state
      at org.apache.kafka.clients.producer.internals.TransactionManager.maybeFailWithError(TransactionManager.java:923) ~[kafka-clients-2.4.1.jar:na]
  at org.apache.kafka.clients.producer.internals.TransactionManager.lambda$beginCommit$2(TransactionManager.java:297) ~[kafka-clients-2.4.1.jar:na]
  at org.apache.kafka.clients.producer.internals.TransactionManager.handleCachedTransactionRequestResult(TransactionManager.java:1013) ~[kafka-clients-2.4.1.jar:na]
  at org.apache.kafka.clients.producer.internals.TransactionManager.beginCommit(TransactionManager.java:296) ~[kafka-clients-2.4.1.jar:na]
  at org.apache.kafka.clients.producer.KafkaProducer.commitTransaction(KafkaProducer.java:713) ~[kafka-clients-2.4.1.jar:na]
  at org.springframework.kafka.core.DefaultKafkaProducerFactory$CloseSafeProducer.commitTransaction(DefaultKafkaProducerFactory.java
  
  
  
  Caused by: org.apache.kafka.common.errors.RecordTooLargeException: The request included a message larger than the max message size the server will accept.
  
  2020-04-30 18:04:36.037  WARN 18688 --- [   scheduling-1] o.s.k.core.DefaultKafkaProducerFactory   : Error during transactional operation; producer removed from cache; possible cause: broker restarted during transaction: CloseSafeProducer [delegate=org.apache.kafka.clients.producer.KafkaProducer@1eb5a312, txId=prod-990]
  2020-04-30 18:04:36.038  INFO 18688 --- [   scheduling-1] o.a.k.clients.producer.KafkaProducer     : [Producer clientId=producer-prod-990, transactionalId=prod-990] Closing the Kafka producer with timeoutMillis = 5000 **ms.
  2020-04-30 18:04:36.038  INFO 18688 --- [oducer-prod-990] o.a.k.clients.producer.internals.Sender  : [Producer clientId=producer-prod-990, transactionalId=prod-990] Aborting incomplete transaction due to shutdown**
  </code></pre>