slachiewicz commented on code in PR #314:
URL: 
https://github.com/apache/flink-connector-kafka/pull/314#discussion_r4054035209


##########
flink-connector-kafka/src/main/java/org/apache/flink/connector/kafka/sink/internal/TransactionAbortStrategyContextImpl.java:
##########
@@ -111,7 +136,50 @@ public Set<String> getPrefixesToAbort() {
 
     @Override
     public Set<String> getPrecommittedTransactionalIds() {
-        return precommittedTransactionIds;
+        return Collections.unmodifiableSet(precommittedTransactions.keySet());
+    }
+
+    @Override
+    public boolean isPrecommittedTransactionSuperseded(String transactionalId) 
{

Review Comment:
   Yes. `TransactionAbortStrategyImplTest` drives the strategy through a fake 
context, so this method itself runs only in `ExactlyOnceKafkaWriterITCase` and 
`KafkaSinkRecoveryITCase`, because the decision needs 
`Admin.describeTransactions`. Those cover the newer-epoch case; the "state has 
no epoch" branch is covered through `KafkaWriterStateSerializerTest` reading v2 
bytes as unknown. If you want the full matrix (id unknown to the broker, equal 
epoch, different producer id) without a broker, I can add a unit test with a 
stub `Admin`.



##########
flink-connector-kafka/src/main/java/org/apache/flink/connector/kafka/sink/internal/TransactionAbortStrategyContextImpl.java:
##########
@@ -111,7 +136,50 @@ public Set<String> getPrefixesToAbort() {
 
     @Override
     public Set<String> getPrecommittedTransactionalIds() {
-        return precommittedTransactionIds;
+        return Collections.unmodifiableSet(precommittedTransactions.keySet());
+    }
+
+    @Override
+    public boolean isPrecommittedTransactionSuperseded(String transactionalId) 
{
+        CheckpointTransaction transaction = 
precommittedTransactions.get(transactionalId);
+        if (transaction == null || !transaction.hasKnownEpoch()) {
+            // state written before v3 did not record the epoch; keep the old 
behavior
+            return false;
+        }
+        TransactionDescription description = 
describePrecommitted().get(transactionalId);
+        if (description == null) {
+            // the broker does not know the id any more; nothing to abort
+            return false;
+        }
+        boolean superseded =
+                description.producerId() != transaction.getProducerId()
+                        || description.producerEpoch() > 
transaction.getEpoch();
+        if (superseded) {
+            LOG.info(
+                    "Recovered transaction {} was opened with producer id {} 
and epoch {}, but the broker now holds producer id {} and epoch {} in state {}",
+                    transactionalId,
+                    transaction.getProducerId(),
+                    transaction.getEpoch(),
+                    description.producerId(),
+                    description.producerEpoch(),
+                    description.state());
+        }
+        return superseded;
+    }
+
+    @Override
+    public void abandonPrecommittedTransaction(String transactionalId) {
+        precommittedTransactions.remove(transactionalId);
+        precommittedTransactionAbandoner.accept(transactionalId);
+    }
+
+    private Map<String, TransactionDescription> describePrecommitted() {
+        if (precommittedDescriptions == null) {
+            precommittedDescriptions =
+                    AdminUtils.describeTransactions(

Review Comment:
   Once per writer initialization, so once per subtask start or restart: 
`abortLingeringTransactions` creates the context in `initialize()`, the first 
superseded check issues one batched `describeTransactions` for all precommitted 
ids, and the result is cached for the lifetime of the context, which ends with 
that call. Nothing on the checkpoint or record path.



##########
flink-connector-kafka/src/main/java/org/apache/flink/connector/kafka/sink/KafkaWriterStateSerializer.java:
##########
@@ -31,11 +32,17 @@
 
 import static org.apache.flink.connector.kafka.sink.KafkaWriterState.UNKNOWN;
 
-/** A serializer used to serialize {@link KafkaWriterState}. */
+/**
+ * A serializer used to serialize {@link KafkaWriterState}.
+ *
+ * <p>Version 3 adds the producer id and epoch of every precommitted 
transaction, so that a recovery

Review Comment:
   Forward-only, like the 1 to 2 bump: a connector without this change throws 
`IOException("Unknown version: 3")` on restore, so a rollback needs a savepoint 
taken by the older version. Backports to v5.0 and v4.0 keep the version 
consistent across the supported lines.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to