Status

Current state: "Under Discussion"

Discussion thread: here 

JIRA:

Please keep the discussion on the mailing list rather than commenting on the wiki (wiki discussions get unwieldy fast).

Motivation

In transactions, having a poison record or sending the record to a topic with issues ends up going to an error state, which leads to dropping the whole batch. This logic does not allow the user to do advanced error handling and ignore some errors. More specifically, the custom production exception handler of Kafka Streams can not influence the control to skip the error by dropping the bad record and committing the transaction with the rest of the batch records.  

Proposed Changes

We aim at enabling users to ignore the `send(ProducerRecord)` errors by defining a new `commitTransaction` method. The new method clears the errors produced by `send(ProducerRecord)` and transits the transaction back from the error state.  In order to keep it safe, the change is applied to transactions with no unflushed record. Therefore, the user must explicitly call `flush()` before calling the newly defined  `commitTransaction`. 

Public Interfaces

If the user passes the `CommitOption` with the value `CLEAR_SEND_ERRORS` to the method, it commits the transaction even in the presence of `send()` errors.


     /**
     * This method should only be called if there are no pending writes, i.e., only after calling {@link #flush()}.
     * If there are any errors in sending messages to topics, these errors can be cleared by passing {@link CommitOption#CLEAR_SEND_ERRORS},
     * allowing the transaction to be committed even in case of data loss.
     * <p>
     * If this method is used while there are pending sends, the send errors cannot be cleared.
      
     * @param commitOption The method option
     *
     * @throws IllegalStateException if no transactional.id has been configured or no transaction has been started
     * @throws ProducerFencedException fatal error indicating another producer with the same transactional.id is active
     * @throws org.apache.kafka.common.errors.UnsupportedVersionException fatal error indicating the broker
     *         does not support transactions (i.e. if its version is lower than 0.11.0.0)
     * @throws org.apache.kafka.common.errors.AuthorizationException fatal error indicating that the configured
     *         transactional.id is not authorized. See the exception for more details
     * @throws org.apache.kafka.common.errors.InvalidProducerEpochException if the producer has attempted to produce with an old epoch
     *         to the partition leader. See the exception for more details
     * @throws KafkaException if the producer has encountered a previous fatal or abortable error, or for any
     *         other unexpected error
     * @throws TimeoutException if the time taken for committing the transaction has surpassed <code>max.block.ms</code>.
     * @throws InterruptException if the thread is interrupted while blocked
     */


     public void commitTransaction(CommitOption option) throws ProducerFencedException {}

     public enum CommitOption {
        /**
         * Commits the ongoing transaction, flushing any unsent records before actually committing
         * the transaction. If any of the records sent in this transaction hit unrecoverable errors,
         * the transaction will not be committed.
         */
        NONE,
        /**
         * Commits the ongoing transaction, first clearing any errors from records already sent in
         * this transaction. If there are any unsent records flushed by this operation which hit 
         * unrecoverable errors, these errors will not be cleared and the transaction will not be 
         * committed.
         * <p>
         * To ensure there are no unsent records, you must call {@link #flush()} before
         * committing the transaction.
         */
        CLEAR_SEND_ERRORS
    }


We define the method in the interface as well. The `default` implementation helps with backward compatibility.

     /**
     * See {@link KafkaProducer#commitTransaction(KafkaProducer.CommitOption)}
     */
    default void commitTransaction(KafkaProducer.CommitOption option) throws ProducerFencedException {}


Compatibility, Deprecation, and Migration Plan

Since the default behaviour is preserved, the change has no impact on existing users.

Test Plan

Unit tests for `KafkaProducer` to show that the new feature works with different `send()` errors and exceptions such as RecordTooLargeException.

Rejected Alternatives