|
16 | 16 | import static org.apache.kafka.clients.CommonClientConfigs.CLIENT_ID_CONFIG;
|
17 | 17 | import static org.hamcrest.CoreMatchers.instanceOf;
|
18 | 18 | import static org.hamcrest.MatcherAssert.assertThat;
|
| 19 | +import static org.mockito.ArgumentMatchers.any; |
| 20 | +import static org.mockito.Mockito.spy; |
| 21 | +import static org.mockito.Mockito.times; |
| 22 | +import static org.mockito.Mockito.verify; |
19 | 23 | import static org.testng.Assert.assertEquals;
|
20 | 24 | import static org.testng.Assert.assertFalse;
|
21 | 25 | import static org.testng.Assert.assertNotNull;
|
|
30 | 34 | import io.streamnative.pulsar.handlers.kop.coordinator.transaction.TransactionState;
|
31 | 35 | import io.streamnative.pulsar.handlers.kop.coordinator.transaction.TransactionStateManager;
|
32 | 36 | import io.streamnative.pulsar.handlers.kop.scala.Either;
|
| 37 | +import io.streamnative.pulsar.handlers.kop.storage.CompletedTxn; |
33 | 38 | import io.streamnative.pulsar.handlers.kop.storage.PartitionLog;
|
| 39 | +import io.streamnative.pulsar.handlers.kop.storage.ProducerStateManager; |
34 | 40 | import io.streamnative.pulsar.handlers.kop.storage.ProducerStateManagerSnapshot;
|
| 41 | +import io.streamnative.pulsar.handlers.kop.storage.ProducerStateManagerSnapshotBuffer; |
35 | 42 | import io.streamnative.pulsar.handlers.kop.storage.TxnMetadata;
|
| 43 | + |
| 44 | +import java.lang.reflect.InvocationTargetException; |
| 45 | +import java.lang.reflect.Method; |
36 | 46 | import java.time.Duration;
|
37 | 47 | import java.time.temporal.ChronoUnit;
|
38 | 48 | import java.util.ArrayList;
|
|
51 | 61 | import java.util.concurrent.atomic.AtomicInteger;
|
52 | 62 | import java.util.function.BiConsumer;
|
53 | 63 | import java.util.function.Function;
|
| 64 | + |
| 65 | +import io.streamnative.pulsar.handlers.kop.utils.ReflectionUtils; |
54 | 66 | import lombok.Cleanup;
|
55 | 67 | import lombok.extern.slf4j.Slf4j;
|
56 | 68 | import org.apache.commons.lang3.RandomStringUtils;
|
|
81 | 93 | import org.apache.kafka.common.serialization.StringSerializer;
|
82 | 94 | import org.apache.pulsar.common.naming.TopicName;
|
83 | 95 | import org.awaitility.Awaitility;
|
| 96 | +import org.mockito.Mockito; |
84 | 97 | import org.testng.Assert;
|
85 | 98 | import org.testng.annotations.AfterClass;
|
86 | 99 | import org.testng.annotations.AfterMethod;
|
@@ -1036,8 +1049,42 @@ public void testPurgeAbortedTx(boolean takeSnapshotBeforeRecovery) throws Except
|
1036 | 1049 |
|
1037 | 1050 | }
|
1038 | 1051 |
|
| 1052 | + @Test(timeOut = 10_000) |
| 1053 | + public void testAbortedPurgeIntervalConfiguration() throws Exception { |
| 1054 | + Class<ProducerStateManager> clazz = ProducerStateManager.class; |
| 1055 | + Method maybePurgeMethod = clazz.getDeclaredMethod("maybePurgeAbortedTx"); |
| 1056 | + maybePurgeMethod.setAccessible(true); |
| 1057 | + Method updateAbortedTxnsPurgeOffsetMethod = |
| 1058 | + clazz.getDeclaredMethod("updateAbortedTxnsPurgeOffset", long.class); |
| 1059 | + updateAbortedTxnsPurgeOffsetMethod.setAccessible(true); |
| 1060 | + |
| 1061 | + ProducerStateManager producerStateManager = buildProducerStateManager( |
| 1062 | + updateAbortedTxnsPurgeOffsetMethod, Integer.MAX_VALUE); |
| 1063 | + for (int i = 0; i < 10; i++) { |
| 1064 | + // the purge interval is Integer.MAX_VALUE, the purge operation should not be triggered |
| 1065 | + assertEquals(maybePurgeMethod.invoke(producerStateManager), 0L); |
| 1066 | + Thread.sleep(500); |
| 1067 | + } |
1039 | 1068 |
|
| 1069 | + producerStateManager = buildProducerStateManager(updateAbortedTxnsPurgeOffsetMethod, 1); |
| 1070 | + assertEquals(maybePurgeMethod.invoke(producerStateManager), 0L); |
| 1071 | + Thread.sleep(1500); |
| 1072 | + assertEquals(maybePurgeMethod.invoke(producerStateManager), 1L); |
| 1073 | + } |
1040 | 1074 |
|
| 1075 | + private ProducerStateManager buildProducerStateManager(Method updateAbortedTxnsPurgeOffsetMethod, |
| 1076 | + int purgeAbortedTxnIntervalSec) throws Exception { |
| 1077 | + ProducerStateManager producerStateManager = new ProducerStateManager( |
| 1078 | + "aborted-txn-index-purge-interval-test-" + RandomStringUtils.randomAlphanumeric(5), |
| 1079 | + UUID.randomUUID().toString(), |
| 1080 | + Mockito.mock(ProducerStateManagerSnapshotBuffer.class), |
| 1081 | + 1000 * 30, |
| 1082 | + purgeAbortedTxnIntervalSec); |
| 1083 | + producerStateManager.updateMapEndOffset(100L); |
| 1084 | + updateAbortedTxnsPurgeOffsetMethod.invoke(producerStateManager, 10L); |
| 1085 | + producerStateManager.updateTxnIndex(new CompletedTxn(1L, 5L, 6L, true), 6L); |
| 1086 | + return producerStateManager; |
| 1087 | + } |
1041 | 1088 |
|
1042 | 1089 | @Test(timeOut = 60000)
|
1043 | 1090 | public void testRecoverFromInvalidSnapshotAfterTrim() throws Exception {
|
|
0 commit comments