Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
// ProducerAppendInfo.java – appendEndTxnMarker()
private void checkProducerEpoch(short producerEpoch, long offset) {
             
	if (producerEpoch < updatedEntry.producerEpoch()) {
        String message = "Epoch of producer " + producerId + " at offset " + offset + " in " + topicPartition +
        " is " + producerEpoch + ", " + "which is smaller than the last seen epoch " + updatedEntry.producerEpoch();

        if (origin == AppendOrigin.REPLICATION)
        {
        	log.warn(message);
        } else {
        	// Starting from 2.7, we replaced ProducerFenced error with InvalidProducerEpoch in the
            // producer send response callback to differentiate from the former fatal exception,
            // letting client abort the ongoing transaction and retry.
            throw new InvalidProducerEpochException(...message);
        }
    } 
}

This check accepts markers when producerEpoch >= currentProducerEpoch. Under legacy transaction versions (TV0 and TV1) this was the expected behavior: EndTxn markers were written with the same epoch as the transactional records, so equality matched the intended case. However, it also created a correctness gap. Because the coordinator did not bump the epoch at EndTxn time, leaders could not distinguish between a valid marker and a late or duplicate one. If a duplicate marker arrived after a new transaction had already begun with the same epoch, the leader would treat it as valid and could mistakenly commit or abort records from the newer transaction. This threatens our EOS guarantees.

...