|
34 | 34 | import org.apache.kafka.common.errors.ProducerFencedException;
|
35 | 35 | import org.apache.kafka.common.serialization.StringDeserializer;
|
36 | 36 | import org.apache.kafka.common.serialization.StringSerializer;
|
| 37 | +import org.assertj.core.api.AbstractThrowableAssert; |
37 | 38 | import org.junit.jupiter.api.AfterEach;
|
38 | 39 | import org.junit.jupiter.api.Test;
|
39 | 40 | import org.junit.jupiter.api.extension.ExtendWith;
|
40 | 41 | import org.junit.jupiter.api.extension.RegisterExtension;
|
41 | 42 | import org.junit.jupiter.params.ParameterizedTest;
|
| 43 | +import org.junit.jupiter.params.provider.CsvSource; |
42 | 44 | import org.junit.jupiter.params.provider.MethodSource;
|
43 | 45 | import org.testcontainers.containers.KafkaContainer;
|
44 | 46 | import org.testcontainers.junit.jupiter.Container;
|
|
54 | 56 | import static org.apache.flink.connector.kafka.testutils.KafkaUtil.checkProducerLeak;
|
55 | 57 | import static org.apache.flink.connector.kafka.testutils.KafkaUtil.createKafkaContainer;
|
56 | 58 | import static org.assertj.core.api.Assertions.assertThat;
|
| 59 | +import static org.assertj.core.api.Assertions.assertThatCode; |
57 | 60 | import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
58 | 61 |
|
59 | 62 | @Testcontainers
|
@@ -174,6 +177,51 @@ void testResetInnerTransactionIfFinalizingTransactionFailed(
|
174 | 177 | }
|
175 | 178 | }
|
176 | 179 |
|
| 180 | + @ParameterizedTest |
| 181 | + @CsvSource({"true,true", "true,false", "false,true", "false,false"}) |
| 182 | + void testDoubleCommitAndAbort(boolean firstCommit, boolean secondCommit) { |
| 183 | + final String topic = "test-double-commit-transaction-" + firstCommit + secondCommit; |
| 184 | + final String transactionIdPrefix = "testDoubleCommitTransaction-"; |
| 185 | + final String transactionalId = transactionIdPrefix + "id"; |
| 186 | + |
| 187 | + KafkaCommittable committable; |
| 188 | + try (FlinkKafkaInternalProducer<String, String> producer = |
| 189 | + new FlinkKafkaInternalProducer<>(getProperties(), transactionalId)) { |
| 190 | + producer.initTransactions(); |
| 191 | + producer.beginTransaction(); |
| 192 | + producer.send(new ProducerRecord<>(topic, "test-value")); |
| 193 | + producer.flush(); |
| 194 | + committable = KafkaCommittable.of(producer); |
| 195 | + if (firstCommit) { |
| 196 | + producer.commitTransaction(); |
| 197 | + } else { |
| 198 | + producer.abortTransaction(); |
| 199 | + } |
| 200 | + } |
| 201 | + |
| 202 | + try (FlinkKafkaInternalProducer<String, String> resumedProducer = |
| 203 | + new FlinkKafkaInternalProducer<>(getProperties(), transactionalId)) { |
| 204 | + resumedProducer.resumeTransaction(committable.getProducerId(), committable.getEpoch()); |
| 205 | + AbstractThrowableAssert<?, ? extends Throwable> secondOp = |
| 206 | + assertThatCode( |
| 207 | + () -> { |
| 208 | + if (secondCommit) { |
| 209 | + resumedProducer.commitTransaction(); |
| 210 | + } else { |
| 211 | + resumedProducer.abortTransaction(); |
| 212 | + } |
| 213 | + }); |
| 214 | + if (firstCommit == secondCommit) { |
| 215 | + secondOp.doesNotThrowAnyException(); |
| 216 | + } else { |
| 217 | + secondOp.isInstanceOf(InvalidTxnStateException.class); |
| 218 | + } |
| 219 | + } |
| 220 | + |
| 221 | + assertNumTransactions(1, transactionIdPrefix); |
| 222 | + assertThat(readRecords(topic).count()).isEqualTo(firstCommit ? 1 : 0); |
| 223 | + } |
| 224 | + |
177 | 225 | private static Properties getProperties() {
|
178 | 226 | Properties properties = new Properties();
|
179 | 227 | properties.put(
|
|
0 commit comments