I wonder could I get some help understanding transactions in Kafka and in particular how I use transaction.id. Here\'s the context:
The blog article you mentioned has all the information you're looking for, although it's rather dense.
From the Why Transactions? section in aforementioned article.
Using vanilla Kafka producers and consumers configured for at-least-once delivery semantics, a stream processing application could lose exactly once processing semantics in the following ways:
The
producer.send()could result in duplicate writes of message B due to internal retries. This is addressed by the idempotent producer and is not the focus of the rest of this post.We may reprocess the input message A, resulting in duplicate B messages being written to the output, violating the exactly once processing semantics. Reprocessing may happen if the stream processing application crashes after writing B but before marking A as consumed. Thus when it resumes, it will consume A again and write B again, causing a duplicate.
Finally, in distributed environments, applications will crash or—worse!—temporarily lose connectivity to the rest of the system. Typically, new instances are automatically started to replace the ones which were deemed lost. Through this process, we may have multiple instances processing the same input topics and writing to the same output topics, causing duplicate outputs and violating the exactly once processing semantics. We call this the problem of “zombie instances.” [emphasis added]
From the Transactional Semantics section in same article.
Zombie fencing
We solve the problem of zombie instances by requiring that each transactional producer be assigned a unique identifier called the
transactional.id. This is used to identify the same producer instance across process restarts. [emphasis added]The API requires that the first operation of a transactional producer should be to explicitly register its
transactional.idwith the Kafka cluster. When it does so, the Kafka broker checks for open transactions with the giventransactional.idand completes them. It also increments an epoch associated with thetransactional.id. The epoch is an internal piece of metadata stored for everytransactional.id.Once the epoch is bumped, any producers with same
transactional.idand an older epoch are considered zombies and are fenced off, ie. future transactional writes from those producers are rejected. [emphasis added]
And from the Data flow section in the same article.
A: the producer and transaction coordinator interaction
When executing transactions, the producer makes requests to the transaction coordinator at the following points:
The
initTransactionsAPI registers atransactional.idwith the coordinator. At this point, the coordinator closes any pending transactions with thattransactional.idand bumps the epoch to fence out zombies. This happens only once per producer session. [emphasis added]When the producer is about to send data to a partition for the first time in a transaction, the partition is registered with the coordinator first.
When the application calls
commitTransactionorabortTransaction, a request is sent to the coordinator to begin the two phase commit protocol.
Hope this helps!