[ 
https://issues.apache.org/jira/browse/KAFKA-20357?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18110081#comment-18110081
 ] 

sanghyeok An commented on KAFKA-20357:
--------------------------------------

[~jolshan] Hi!
Sorry to bother you.
I've addressed it based on your comments.

When you get a chance, could you take a look this PR?

https://github.com/apache/kafka/pull/22096

> lastProducerEpoch fields never persisted to transaction log
> -----------------------------------------------------------
>
>                 Key: KAFKA-20357
>                 URL: https://issues.apache.org/jira/browse/KAFKA-20357
>             Project: Kafka
>          Issue Type: Bug
>    Affects Versions: 4.0.0
>            Reporter: sanghyeok An
>            Assignee: sanghyeok An
>            Priority: Major
>              Labels: transaction
>
> This issue is a follow-up to KAFKA-20310.
>  
> During epoch rotation, if the coordinator fails after writing the updated 
> transaction metadata but before the client receives the InitProducerId 
> response, a retry from the client may be incorrectly rejected with 
> PRODUCER_FENCED.
> This appears to be caused not only by previousProducerId not being persisted 
> in the transaction log, but also by lastProducerEpoch not being restored 
> correctly after failover. As a result, the retry is initially accepted by 
> TransactionCoordinator.isValidProducerId, but later fails in 
> TransactionMetadata.prepareIncrementProducerEpoch because the recovered 
> metadata no longer contains the previous epoch information needed to 
> recognize the retry correctly.
>  
> Scenario 2: Client retry after coordinator failover during epoch rotation
>  # The client has producerId = 100 and producerEpoch = 32766, so epoch 
> exhaustion is imminent.
>  # The client sends InitProducerId, and the coordinator performs epoch 
> rotation in memory:
>  ** prevProducerId = 100
>  ** producerId = 101
>  ** producerEpoch = 0
>  ** lastProducerEpoch = 32766
>  # The updated transaction metadata is written to the log, but 
> previousProducerId is not persisted in TransactionLog.valueToBytes().
> Proposed related fix area:
> [https://github.com/apache/kafka/blob/5729bb614fbd3397a7894d3831fb46ddc555a27b/core/src/main/scala/kafka/coordinator/transaction/TransactionLog.scala]
>  # The coordinator fails before the client receives the InitProducerId 
> response.
>  # After failover and recovery, the coordinator reconstructs in-memory state 
> as:
>  ** producerId = 101
>  ** producerEpoch = 0
>  ** prevProducerId = 100
>  ** lastProducerEpoch = -1
>  # The client retries InitProducerId with expectedProducerIdAndEpoch = (100, 
> 32766).
>  # In TransactionCoordinator.isValidProducerId, the retry is accepted because:
>  ** producerIdAndEpoch.producerId == txnMetadata.prevProducerId
>  ** the client epoch is exhausted
> Relevant code:
> [https://github.com/apache/kafka/blob/5729bb614fbd3397a7894d3831fb46ddc555a27b/core/src/main/scala/kafka/coordinator/transaction/TransactionCoordinator.scala#L240-L243]
>  # The coordinator then proceeds into the InitProducerId handling path and 
> calls txnMetadata.prepareIncrementProducerEpoch(...), expecting to continue 
> the retry flow.
>  ** Relevant code:
> [https://github.com/apache/kafka/blob/5729bb614fbd3397a7894d3831fb46ddc555a27b/core/src/main/scala/kafka/coordinator/transaction/TransactionCoordinator.scala#L262-L268]
>  # However, this path does not treat the request as a valid retry of the 
> previous epoch rotation, because lastProducerEpoch was not restored correctly 
> after failover.
>  # Inside TransactionMetadata.prepareIncrementProducerEpoch(...), the retry 
> validation eventually fails.
>  ** Relevant code:
> [https://github.com/apache/kafka/blob/5729bb614fbd3397a7894d3831fb46ddc555a27b/transaction-coordinator/src/main/java/org/apache/kafka/coordinator/transaction/TransactionMetadata.java#L163]
>  # More specifically:
>  ** the exhausted-epoch retry path is not taken because the current in-memory 
> producerEpoch is 0, while the original exhausted epoch was 32766
> Relevant code:
> [https://github.com/apache/kafka/blob/5729bb614fbd3397a7894d3831fb46ddc555a27b/transaction-coordinator/src/main/java/org/apache/kafka/coordinator/transaction/TransactionMetadata.java#L168]
>  ** the retry is not recognized as a duplicate/retry of the previous rotation 
> because lastProducerEpoch is -1 instead of 32766
> Relevant code:
> [https://github.com/apache/kafka/blob/5729bb614fbd3397a7894d3831fb46ddc555a27b/transaction-coordinator/src/main/java/org/apache/kafka/coordinator/transaction/TransactionMetadata.java#L174]
>  ** the method eventually throws PRODUCER_FENCED
> Relevant code:
> [https://github.com/apache/kafka/blob/5729bb614fbd3397a7894d3831fb46ddc555a27b/transaction-coordinator/src/main/java/org/apache/kafka/coordinator/transaction/TransactionMetadata.java#L182-L186]
>  # As a result, the coordinator returns PRODUCER_FENCED even though this is a 
> legitimate retry after failover during epoch rotation.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to