This is an automated email from the ASF dual-hosted git repository.
bbejeck pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/kafka.git
The following commit(s) were added to refs/heads/trunk by this push:
new d3b9f8a3bea KAFKA-20756: Parameterize for transactional state stores
(#22726)
d3b9f8a3bea is described below
commit d3b9f8a3beaa67455048aec0387eef5845acdcba
Author: Bill Bejeck <[email protected]>
AuthorDate: Thu Jul 2 13:02:59 2026 -0400
KAFKA-20756: Parameterize for transactional state stores (#22726)
Expand coverage for KIP-892 by parameterizing integration tests
Reviewers: TengYao Chi <[email protected]>
---
.../ConsistencyVectorIntegrationTest.java | 19 ++++--
.../streams/integration/EosIntegrationTest.java | 48 ++++++++++++--
.../KStreamAggregationIntegrationTest.java | 50 ++++++++++++--
.../KTableKTableForeignKeyJoinIntegrationTest.java | 76 +++++++++++++++++++---
.../integration/RestoreIntegrationTest.java | 26 +++++++-
.../SlidingWindowedKStreamIntegrationTest.java | 44 +++++++++----
.../integration/StandbyTaskEOSIntegrationTest.java | 39 ++++++-----
.../StreamStreamJoinIntegrationTest.java | 24 +++++--
.../SuppressionDurabilityIntegrationTest.java | 22 +++++--
.../VersionedKeyValueStoreIntegrationTest.java | 25 +++++--
10 files changed, 301 insertions(+), 72 deletions(-)
diff --git
a/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/ConsistencyVectorIntegrationTest.java
b/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/ConsistencyVectorIntegrationTest.java
index 7b1c02e5d22..c3c2045fb96 100644
---
a/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/ConsistencyVectorIntegrationTest.java
+++
b/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/ConsistencyVectorIntegrationTest.java
@@ -43,9 +43,10 @@ import org.apache.kafka.test.TestUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
-import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.Timeout;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
import java.io.IOException;
import java.time.Duration;
@@ -93,8 +94,10 @@ public class ConsistencyVectorIntegrationTest {
cluster.stop();
}
- @Test
- public void shouldHaveSamePositionBoundActiveAndStandBy(final TestInfo
testInfo) throws Exception {
+ @ParameterizedTest
+ @ValueSource(booleans = {false, true})
+ public void shouldHaveSamePositionBoundActiveAndStandBy(final boolean
transactionalStateStores,
+ final TestInfo
testInfo) throws Exception {
final Semaphore semaphore = new Semaphore(0);
final StreamsBuilder builder = new StreamsBuilder();
@@ -107,8 +110,8 @@ public class ConsistencyVectorIntegrationTest {
.peek((k, v) -> semaphore.release());
final String safeTestName = safeUniqueTestName(testInfo);
- final KafkaStreams kafkaStreams1 = createKafkaStreams(builder,
streamsConfiguration(safeTestName));
- final KafkaStreams kafkaStreams2 = createKafkaStreams(builder,
streamsConfiguration(safeTestName));
+ final KafkaStreams kafkaStreams1 = createKafkaStreams(builder,
streamsConfiguration(safeTestName, transactionalStateStores));
+ final KafkaStreams kafkaStreams2 = createKafkaStreams(builder,
streamsConfiguration(safeTestName, transactionalStateStores));
final List<KafkaStreams> kafkaStreamsList =
Arrays.asList(kafkaStreams1, kafkaStreams2);
try {
@@ -200,7 +203,7 @@ public class ConsistencyVectorIntegrationTest {
);
}
- private Properties streamsConfiguration(final String safeTestName) {
+ private Properties streamsConfiguration(final String safeTestName, final
boolean transactionalStateStores) {
final Properties config = new Properties();
config.put(StreamsConfig.APPLICATION_ID_CONFIG, "app-" + safeTestName);
config.put(StreamsConfig.APPLICATION_SERVER_CONFIG, "localhost:" +
(++port));
@@ -214,6 +217,10 @@ public class ConsistencyVectorIntegrationTest {
config.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, 1000);
config.put(StreamsConfig.COMMIT_INTERVAL_MS_CONFIG, 100L);
config.put(InternalConfig.IQ_CONSISTENCY_OFFSET_VECTOR_ENABLED, true);
+ if (transactionalStateStores) {
+ config.put(StreamsConfig.TRANSACTIONAL_STATE_STORES_CONFIG, true);
+ config.put(StreamsConfig.PROCESSING_GUARANTEE_CONFIG,
StreamsConfig.EXACTLY_ONCE_V2);
+ }
return config;
}
}
diff --git
a/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/EosIntegrationTest.java
b/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/EosIntegrationTest.java
index a6cb68f5283..f39a937bd8e 100644
---
a/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/EosIntegrationTest.java
+++
b/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/EosIntegrationTest.java
@@ -179,6 +179,21 @@ public class EosIntegrationTest {
);
}
+ // Adds a sparse "transactional state stores" (KIP-892) dimension on top
of the group-protocol/
+ // processing-threads matrix. To avoid exploding the full matrix by 2, the
transactional=true case
+ // is only exercised for a single, representative combination (classic
protocol, processing threads
+ // disabled) while preserving all existing (transactional=false) coverage.
Transactional state stores
+ // are an exactly-once-only feature; this test already runs under
EXACTLY_ONCE_V2.
+ private static java.util.stream.Stream<Arguments>
groupProtocolProcessingThreadsAndTransactionalParameters() {
+ return java.util.stream.Stream.of(
+ Arguments.of("classic", true, false),
+ Arguments.of("classic", false, false),
+ Arguments.of("streams", true, false),
+ Arguments.of("streams", false, false),
+ Arguments.of("classic", false, true)
+ );
+ }
+
@BeforeEach
public void createTopics() throws Exception {
applicationId = "appId-" + TEST_NUMBER.getAndIncrement();
@@ -498,8 +513,10 @@ public class EosIntegrationTest {
}
@ParameterizedTest
- @MethodSource("groupProtocolAndProcessingThreadsParameters")
- public void shouldNotViolateEosIfOneTaskFailsWithState(final String
groupProtocol, final boolean processingThreadsEnabled) throws Exception {
+ @MethodSource("groupProtocolProcessingThreadsAndTransactionalParameters")
+ public void shouldNotViolateEosIfOneTaskFailsWithState(final String
groupProtocol,
+ final boolean
processingThreadsEnabled,
+ final boolean
transactionalStateStores) throws Exception {
// this test updates a store with 10 + 5 + 5 records per partition
(running with 2 partitions)
// the app is supposed to emit all 40 update records into the output
topic
@@ -515,7 +532,7 @@ public class EosIntegrationTest {
// We need more processing time under "with state" situation, so
increasing the max.poll.interval.ms
// to avoid unexpected rebalance during test, which will cause
unexpected fail over triggered
- try (final KafkaStreams streams = getKafkaStreams("dummy", true,
"appDir", 2, groupProtocol, processingThreadsEnabled)) {
+ try (final KafkaStreams streams = getKafkaStreams("dummy", true,
"appDir", 2, groupProtocol, processingThreadsEnabled,
transactionalStateStores)) {
startApplicationAndWaitUntilRunning(streams);
final List<KeyValue<Long, Long>> committedDataBeforeFailure =
prepareData(0L, 10L, 0L, 1L);
@@ -772,12 +789,14 @@ public class EosIntegrationTest {
}
@ParameterizedTest
- @MethodSource("groupProtocolAndProcessingThreadsParameters")
- public void shouldWriteLatestOffsetsToCheckpointOnShutdown(final String
groupProtocol, final boolean processingThreadsEnabled) throws Exception {
+ @MethodSource("groupProtocolProcessingThreadsAndTransactionalParameters")
+ public void shouldWriteLatestOffsetsToCheckpointOnShutdown(final String
groupProtocol,
+ final boolean
processingThreadsEnabled,
+ final boolean
transactionalStateStores) throws Exception {
final List<KeyValue<Long, Long>> writtenData = prepareData(0L, 10, 0L,
1L);
final List<KeyValue<Long, Long>> expectedResult =
computeExpectedResult(writtenData);
- try (final KafkaStreams streams = getKafkaStreams("streams", true,
"appDir", 1, groupProtocol, processingThreadsEnabled)) {
+ try (final KafkaStreams streams = getKafkaStreams("streams", true,
"appDir", 1, groupProtocol, processingThreadsEnabled,
transactionalStateStores)) {
writeInputData(writtenData);
startApplicationAndWaitUntilRunning(streams);
@@ -1117,13 +1136,23 @@ public class EosIntegrationTest {
return data;
}
- // the threads should no longer fail one thread one at a time
private KafkaStreams getKafkaStreams(final String dummyHostName,
final boolean withState,
final String appDir,
final int numberOfStreamsThreads,
final String groupProtocol,
final boolean
processingThreadsEnabled) {
+ return getKafkaStreams(dummyHostName, withState, appDir,
numberOfStreamsThreads, groupProtocol, processingThreadsEnabled, false);
+ }
+
+ // the threads should no longer fail one thread one at a time
+ private KafkaStreams getKafkaStreams(final String dummyHostName,
+ final boolean withState,
+ final String appDir,
+ final int numberOfStreamsThreads,
+ final String groupProtocol,
+ final boolean
processingThreadsEnabled,
+ final boolean
transactionalStateStores) {
commitRequested = new AtomicInteger(0);
errorInjected = new AtomicBoolean(false);
stallInjected = new AtomicBoolean(false);
@@ -1231,6 +1260,11 @@ public class EosIntegrationTest {
properties.put(StreamsConfig.APPLICATION_SERVER_CONFIG, dummyHostName
+ ":2142");
properties.put(InternalConfig.PROCESSING_THREADS_ENABLED,
processingThreadsEnabled);
properties.put(StreamsConfig.GROUP_PROTOCOL_CONFIG, groupProtocol);
+ if (transactionalStateStores) {
+ // This test already runs under EXACTLY_ONCE_V2 (see
PROCESSING_GUARANTEE_CONFIG above);
+ // transactional state stores are an exactly-once-only feature.
+ properties.put(StreamsConfig.TRANSACTIONAL_STATE_STORES_CONFIG,
true);
+ }
final Properties config = StreamsTestUtils.getStreamsConfig(
applicationId,
diff --git
a/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/KStreamAggregationIntegrationTest.java
b/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/KStreamAggregationIntegrationTest.java
index 48e9ce88b9a..6b05e23364d 100644
---
a/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/KStreamAggregationIntegrationTest.java
+++
b/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/KStreamAggregationIntegrationTest.java
@@ -74,6 +74,8 @@ import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import java.io.ByteArrayOutputStream;
@@ -92,6 +94,7 @@ import java.util.Properties;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
import static java.time.Duration.ofMillis;
import static java.time.Duration.ofMinutes;
@@ -170,9 +173,37 @@ public class KStreamAggregationIntegrationTest {
IntegrationTestUtils.purgeLocalStreamsState(streamsConfiguration);
}
+ /**
+ * Provides (withHeaders, transactionalStateStores) pairs so
representative aggregation tests keep their
+ * existing withHeaders={false,true} coverage (with non-transactional
stores) and additionally run once
+ * with transactional state stores enabled. Transactional state stores
(KIP-892) are an exactly-once-only
+ * feature, so the transactional=true case always runs under
EXACTLY_ONCE_V2 (see
+ * {@link #maybeEnableTransactionalStateStores(boolean)}).
+ */
+ private static Stream<Arguments> headersAndTransactional() {
+ return Stream.of(
+ Arguments.of(false, false),
+ Arguments.of(true, false),
+ Arguments.of(false, true)
+ );
+ }
+
+ /**
+ * When {@code transactionalStateStores} is true, enable transactional
state stores under EXACTLY_ONCE_V2,
+ * since transactional state stores (KIP-892) are only supported with
exactly-once processing.
+ */
+ private void maybeEnableTransactionalStateStores(final boolean
transactionalStateStores) {
+ if (transactionalStateStores) {
+
streamsConfiguration.put(StreamsConfig.PROCESSING_GUARANTEE_CONFIG,
StreamsConfig.EXACTLY_ONCE_V2);
+
streamsConfiguration.put(StreamsConfig.TRANSACTIONAL_STATE_STORES_CONFIG, true);
+ }
+ }
+
@ParameterizedTest
- @ValueSource(booleans = {false, true})
- public void shouldReduce(final boolean withHeaders, final TestInfo
testInfo) throws Exception {
+ @MethodSource("headersAndTransactional")
+ public void shouldReduce(final boolean withHeaders,
+ final boolean transactionalStateStores,
+ final TestInfo testInfo) throws Exception {
produceMessages(mockTime.milliseconds());
groupedStream
.reduce(reducer, Materialized.as("reduce-by-key"))
@@ -180,6 +211,7 @@ public class KStreamAggregationIntegrationTest {
.to(outputTopic, Produced.with(Serdes.String(), Serdes.String()));
StreamsTestUtils.maybeSetDslStoreFormatHeaders(streamsConfiguration,
withHeaders);
+ maybeEnableTransactionalStateStores(transactionalStateStores);
startStreams();
@@ -224,8 +256,10 @@ public class KStreamAggregationIntegrationTest {
}
@ParameterizedTest
- @ValueSource(booleans = {false, true})
- public void shouldReduceWindowed(final boolean withHeaders, final TestInfo
testInfo) throws Exception {
+ @MethodSource("headersAndTransactional")
+ public void shouldReduceWindowed(final boolean withHeaders,
+ final boolean transactionalStateStores,
+ final TestInfo testInfo) throws Exception
{
final long firstBatchTimestamp = mockTime.milliseconds();
mockTime.sleep(1000);
produceMessages(firstBatchTimestamp);
@@ -241,6 +275,7 @@ public class KStreamAggregationIntegrationTest {
.to(outputTopic, Produced.with(windowedSerde, Serdes.String()));
StreamsTestUtils.maybeSetDslStoreFormatHeaders(streamsConfiguration,
withHeaders);
+ maybeEnableTransactionalStateStores(transactionalStateStores);
startStreams();
@@ -856,8 +891,9 @@ public class KStreamAggregationIntegrationTest {
}
@ParameterizedTest
- @ValueSource(booleans = {false, true})
- public void shouldReduceSessionWindows(final boolean withHeaders) throws
Exception {
+ @MethodSource("headersAndTransactional")
+ public void shouldReduceSessionWindows(final boolean withHeaders,
+ final boolean
transactionalStateStores) throws Exception {
final long sessionGap = 1000L; // something to do with time
final Properties producerConfig = TestUtils.producerConfig(
@@ -890,6 +926,8 @@ public class KStreamAggregationIntegrationTest {
latch.countDown();
});
+ maybeEnableTransactionalStateStores(transactionalStateStores);
+
startStreams();
latch.await(30, TimeUnit.SECONDS);
diff --git
a/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/KTableKTableForeignKeyJoinIntegrationTest.java
b/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/KTableKTableForeignKeyJoinIntegrationTest.java
index c6daa0420c1..f29292892d7 100644
---
a/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/KTableKTableForeignKeyJoinIntegrationTest.java
+++
b/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/KTableKTableForeignKeyJoinIntegrationTest.java
@@ -90,10 +90,20 @@ public class KTableKTableForeignKeyJoinIntegrationTest {
}
private static Properties getStreamsProperties(final String optimization,
final boolean withHeaders) {
+ return getStreamsProperties(optimization, withHeaders, false);
+ }
+
+ private static Properties getStreamsProperties(final String optimization,
final boolean withHeaders, final boolean transactional) {
final Properties props = mkProperties(mkMap(
mkEntry(StreamsConfig.STATE_DIR_CONFIG,
TestUtils.tempDirectory().getPath()),
mkEntry(StreamsConfig.TOPOLOGY_OPTIMIZATION_CONFIG,
optimization)
));
+ // Transactional state stores (KIP-892) are only supported under
exactly-once-v2, so whenever the
+ // transactional dimension is enabled we also switch the processing
guarantee to exactly-once-v2.
+ if (transactional) {
+ props.put(StreamsConfig.PROCESSING_GUARANTEE_CONFIG,
StreamsConfig.EXACTLY_ONCE_V2);
+ props.put(StreamsConfig.TRANSACTIONAL_STATE_STORES_CONFIG, true);
+ }
StreamsTestUtils.maybeSetDslStoreFormatHeaders(props, withHeaders);
return props;
}
@@ -145,6 +155,51 @@ public class KTableKTableForeignKeyJoinIntegrationTest {
return versionedData().stream().map(Arguments::of);
}
+ // Extends the standard testCases() with a transactional dimension (last
argument). All existing cases keep
+ // transactional=false (preserving current coverage), and we add a small,
representative set of
+ // transactional=true cases (KIP-892 transactional state stores, which
imply exactly-once-v2). To avoid
+ // doubling the whole matrix, transactional=true is only added for a
single materialization/config
+ // combination: materialized (so the queryable store is exercised),
non-optimized, non-rejoin, non-versioned,
+ // and without DSL store-format headers, for both inner and left joins.
+ private static Stream<Arguments> transactionalTestCases() {
+ final Stream<Arguments> nonTransactional = testCases()
+ .map(arguments -> extend(arguments.get(), false));
+ final Stream<Arguments> transactional = Stream.of(true, false)
+ .map(leftJoin -> Arguments.of(
+ leftJoin, // leftJoin
+ StreamsConfig.NO_OPTIMIZATION, // optimization
+ true, // materialized
+ false, // rejoin
+ false, // leftVersioned
+ false, // rightVersioned
+ false, // withHeaders
+ true // transactional
+ ));
+ return Stream.concat(nonTransactional, transactional);
+ }
+
+ // Same as transactionalTestCases() but without the leftJoin argument
(mirrors testCasesWithoutLeftJoinArg()).
+ private static Stream<Arguments>
transactionalTestCasesWithoutLeftJoinArg() {
+ final Stream<Arguments> nonTransactional =
testCasesWithoutLeftJoinArg()
+ .map(arguments -> extend(arguments.get(), false));
+ final Stream<Arguments> transactional = Stream.of(Arguments.of(
+ StreamsConfig.NO_OPTIMIZATION, // optimization
+ true, // materialized
+ false, // rejoin
+ false, // leftVersioned
+ false, // rightVersioned
+ false, // withHeaders
+ true // transactional
+ ));
+ return Stream.concat(nonTransactional, transactional);
+ }
+
+ private static Arguments extend(final Object[] args, final Object extra) {
+ final Object[] extended = Arrays.copyOf(args, args.length + 1);
+ extended[args.length] = extra;
+ return Arguments.of(extended);
+ }
+
protected static Collection<Object[]> buildParameters(final List<?>...
argOptions) {
List<Object[]> result = new LinkedList<>();
result.add(new Object[0]);
@@ -170,15 +225,16 @@ public class KTableKTableForeignKeyJoinIntegrationTest {
}
@ParameterizedTest
- @MethodSource("testCases")
+ @MethodSource("transactionalTestCases")
public void doJoinFromLeftThenDeleteLeftEntity(final boolean leftJoin,
final String optimization,
final boolean materialized,
final boolean rejoin,
final boolean leftVersioned,
final boolean
rightVersioned,
- final boolean withHeaders) {
- final Properties streamsConfig = getStreamsProperties(optimization,
withHeaders);
+ final boolean withHeaders,
+ final boolean
transactional) {
+ final Properties streamsConfig = getStreamsProperties(optimization,
withHeaders, transactional);
final Topology topology = getTopology(streamsConfig, materialized ?
"store" : null, leftJoin, rejoin, leftVersioned, rightVersioned);
try (final TopologyTestDriver driver = new
TopologyTestDriver(topology, streamsConfig)) {
final TestInputTopic<String, String> right =
driver.createInputTopic(RIGHT_TABLE, new StringSerializer(), new
StringSerializer());
@@ -370,15 +426,16 @@ public class KTableKTableForeignKeyJoinIntegrationTest {
}
@ParameterizedTest
- @MethodSource("testCases")
+ @MethodSource("transactionalTestCases")
public void doJoinFromRightThenDeleteRightEntity(final boolean leftJoin,
final String optimization,
final boolean
materialized,
final boolean rejoin,
final boolean
leftVersioned,
final boolean
rightVersioned,
- final boolean
withHeaders) {
- final Properties streamsConfig = getStreamsProperties(optimization,
withHeaders);
+ final boolean withHeaders,
+ final boolean
transactional) {
+ final Properties streamsConfig = getStreamsProperties(optimization,
withHeaders, transactional);
final Topology topology = getTopology(streamsConfig, materialized ?
"store" : null, leftJoin, rejoin, leftVersioned, rightVersioned);
try (final TopologyTestDriver driver = new
TopologyTestDriver(topology, streamsConfig)) {
final TestInputTopic<String, String> right =
driver.createInputTopic(RIGHT_TABLE, new StringSerializer(), new
StringSerializer());
@@ -814,13 +871,14 @@ public class KTableKTableForeignKeyJoinIntegrationTest {
}
@ParameterizedTest
- @MethodSource("testCasesWithoutLeftJoinArg")
+ @MethodSource("transactionalTestCasesWithoutLeftJoinArg")
public void shouldEmitRecordWhenOldAndNewFkDiffer(final String
optimization,
final boolean
materialized,
final boolean rejoin,
final boolean
leftVersioned,
final boolean
rightVersioned,
- final boolean
withHeaders) {
+ final boolean
withHeaders,
+ final boolean
transactional) {
final Function<String, String> foreignKeyExtractor = value -> {
final String split = value.split("\\|")[1];
if (split.equals("returnNull")) {
@@ -831,7 +889,7 @@ public class KTableKTableForeignKeyJoinIntegrationTest {
return split;
}
};
- final Properties streamsConfig = getStreamsProperties(optimization,
withHeaders);
+ final Properties streamsConfig = getStreamsProperties(optimization,
withHeaders, transactional);
final Topology topology = getTopology(streamsConfig, materialized ?
"store" : null, true, rejoin, leftVersioned, rightVersioned,
foreignKeyExtractor);
try (final TopologyTestDriver driver = new
TopologyTestDriver(topology, streamsConfig)) {
final TestInputTopic<String, String> left =
driver.createInputTopic(LEFT_TABLE, new StringSerializer(), new
StringSerializer());
diff --git
a/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/RestoreIntegrationTest.java
b/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/RestoreIntegrationTest.java
index 01caca64676..9c9bdda9f7b 100644
---
a/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/RestoreIntegrationTest.java
+++
b/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/RestoreIntegrationTest.java
@@ -187,6 +187,15 @@ public class RestoreIntegrationTest {
return streamsConfiguration;
}
+ // Enables transactional state stores (KIP-892) when requested.
Transactional stores are only supported
+ // under exactly-once, so this also sets the processing guarantee to
EXACTLY_ONCE_V2.
+ private static void maybeSetTransactionalStateStores(final Properties
props, final boolean transactional) {
+ if (transactional) {
+ props.put(StreamsConfig.TRANSACTIONAL_STATE_STORES_CONFIG, true);
+ props.put(StreamsConfig.PROCESSING_GUARANTEE_CONFIG,
StreamsConfig.EXACTLY_ONCE_V2);
+ }
+ }
+
@AfterEach
public void shutdown() throws Exception {
if (kafkaStreams != null) {
@@ -397,9 +406,21 @@ public class RestoreIntegrationTest {
assertThat(numReceived.get(), equalTo(offsetLimitDelta * 2));
}
+ // Adds a transactional dimension on top of (useNewProtocol, withHeaders).
When transactional is true the
+ // state stores are transactional (KIP-892), which requires
EXACTLY_ONCE_V2 processing guarantee. This
+ // exercises the transactional-store lifecycle over the changelog restore
path.
@ParameterizedTest
- @CsvSource({"false, false", "false, true", "true, false", "true, true"})
- public void shouldRestoreStateFromChangelogTopic(final boolean
useNewProtocol, final boolean withHeaders) throws Exception {
+ @CsvSource({
+ "false, false, false",
+ "false, true, false",
+ "true, false, false",
+ "true, true, false",
+ "false, false, true",
+ "true, false, true"
+ })
+ public void shouldRestoreStateFromChangelogTopic(final boolean
useNewProtocol,
+ final boolean withHeaders,
+ final boolean
transactional) throws Exception {
final String changelog = appId + "-store-changelog";
CLUSTER.createTopic(changelog, 2, 1);
@@ -412,6 +433,7 @@ public class RestoreIntegrationTest {
props.put(StreamsConfig.GROUP_PROTOCOL_CONFIG,
GroupProtocol.STREAMS.name());
}
StreamsTestUtils.maybeSetDslStoreFormatHeaders(props, withHeaders);
+ maybeSetTransactionalStateStores(props, transactional);
// restoring from 1000 to 5000, and then process from 5000 to 10000 on
each of the two partitions
final int offsetCheckpointed = 1000;
diff --git
a/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/SlidingWindowedKStreamIntegrationTest.java
b/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/SlidingWindowedKStreamIntegrationTest.java
index 562a80d0f37..f91be43c217 100644
---
a/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/SlidingWindowedKStreamIntegrationTest.java
+++
b/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/SlidingWindowedKStreamIntegrationTest.java
@@ -135,20 +135,29 @@ public class SlidingWindowedKStreamIntegrationTest {
public static Stream<Arguments> data() {
return Stream.of(
- Arguments.of(StrategyType.ON_WINDOW_UPDATE, true, false),
- Arguments.of(StrategyType.ON_WINDOW_UPDATE, true, true),
- Arguments.of(StrategyType.ON_WINDOW_UPDATE, false, false),
- Arguments.of(StrategyType.ON_WINDOW_UPDATE, false, true),
- Arguments.of(StrategyType.ON_WINDOW_CLOSE, true, false),
- Arguments.of(StrategyType.ON_WINDOW_CLOSE, true, true),
- Arguments.of(StrategyType.ON_WINDOW_CLOSE, false, false),
- Arguments.of(StrategyType.ON_WINDOW_CLOSE, false, true)
+ // (strategyType, withCache, withHeaders, transactional)
+ // Existing non-transactional coverage (at-least-once) is
preserved unchanged.
+ Arguments.of(StrategyType.ON_WINDOW_UPDATE, true, false, false),
+ Arguments.of(StrategyType.ON_WINDOW_UPDATE, true, true, false),
+ Arguments.of(StrategyType.ON_WINDOW_UPDATE, false, false, false),
+ Arguments.of(StrategyType.ON_WINDOW_UPDATE, false, true, false),
+ Arguments.of(StrategyType.ON_WINDOW_CLOSE, true, false, false),
+ Arguments.of(StrategyType.ON_WINDOW_CLOSE, true, true, false),
+ Arguments.of(StrategyType.ON_WINDOW_CLOSE, false, false, false),
+ Arguments.of(StrategyType.ON_WINDOW_CLOSE, false, true, false),
+ // Sparse transactional (KIP-892) coverage:
enable.transactional.statestores=true always
+ // implies exactly_once_v2. One representative case per emit
strategy, cache disabled and no
+ // store-format headers, exercising the transactional
sliding-window aggregation store path.
+ Arguments.of(StrategyType.ON_WINDOW_UPDATE, false, false, true),
+ Arguments.of(StrategyType.ON_WINDOW_CLOSE, false, false, true)
);
}
@ParameterizedTest
@MethodSource("data")
- public void shouldAggregateWindowedWithNoGrace(final StrategyType
strategyType, final boolean withCache, final boolean withHeaders) throws
Exception {
+ public void shouldAggregateWindowedWithNoGrace(final StrategyType
strategyType, final boolean withCache, final boolean withHeaders, final boolean
transactional) throws Exception {
+ maybeSetTransactionalStateStores(transactional);
+
produceMessages(
streamOneInput,
new KeyValueTimestamp<>("A", "1", 0), // Create [0, 10](0+1)
@@ -216,7 +225,9 @@ public class SlidingWindowedKStreamIntegrationTest {
@ParameterizedTest
@MethodSource("data")
- public void shouldAggregateWindowedWithGrace(final StrategyType
strategyType, final boolean withCache, final boolean withHeaders) throws
Exception {
+ public void shouldAggregateWindowedWithGrace(final StrategyType
strategyType, final boolean withCache, final boolean withHeaders, final boolean
transactional) throws Exception {
+ maybeSetTransactionalStateStores(transactional);
+
produceMessages(
streamOneInput,
new KeyValueTimestamp<>("A", "1", 0), // Create [0, 10](0+1)
@@ -293,7 +304,9 @@ public class SlidingWindowedKStreamIntegrationTest {
@ParameterizedTest
@MethodSource("data")
- public void shouldRestoreAfterJoinRestart(final StrategyType strategyType,
final boolean withCache, final boolean withHeaders) throws Exception {
+ public void shouldRestoreAfterJoinRestart(final StrategyType strategyType,
final boolean withCache, final boolean withHeaders, final boolean
transactional) throws Exception {
+ maybeSetTransactionalStateStores(transactional);
+
produceMessages(
streamOneInput,
new KeyValueTimestamp<>("A", "L1", 0),
@@ -434,6 +447,15 @@ public class SlidingWindowedKStreamIntegrationTest {
);
}
+ private void maybeSetTransactionalStateStores(final boolean transactional)
{
+ if (transactional) {
+ // KIP-892 transactional state stores are only supported under
exactly-once semantics, so
+ // enabling them requires exactly_once_v2 as the processing
guarantee.
+
streamsConfiguration.put(StreamsConfig.TRANSACTIONAL_STATE_STORES_CONFIG, true);
+
streamsConfiguration.put(StreamsConfig.PROCESSING_GUARANTEE_CONFIG,
StreamsConfig.EXACTLY_ONCE_V2);
+ }
+ }
+
private Materialized getMaterialized(final boolean withCache) {
if (withCache) {
return Materialized.with(null, new
StringSerde()).withCachingEnabled();
diff --git
a/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/StandbyTaskEOSIntegrationTest.java
b/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/StandbyTaskEOSIntegrationTest.java
index 722d3ab234d..9b4c4f9b8e8 100644
---
a/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/StandbyTaskEOSIntegrationTest.java
+++
b/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/StandbyTaskEOSIntegrationTest.java
@@ -46,9 +46,10 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
-import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.Timeout;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
import java.io.File;
import java.io.IOException;
@@ -124,8 +125,9 @@ public class StandbyTaskEOSIntegrationTest {
}
}
- @Test
- public void shouldSurviveWithOneTaskAsStandby() throws Exception {
+ @ParameterizedTest
+ @ValueSource(booleans = {false, true})
+ public void shouldSurviveWithOneTaskAsStandby(final boolean
transactionalStateStores) throws Exception {
IntegrationTestUtils.produceKeyValuesSynchronouslyWithTimestamp(
inputTopic,
Collections.singletonList(
@@ -144,8 +146,8 @@ public class StandbyTaskEOSIntegrationTest {
final CountDownLatch instanceLatch = new CountDownLatch(1);
- streamInstanceOne = buildStreamWithDirtyStateDir(stateDirPath + "/" +
appId + "-1/", instanceLatch);
- streamInstanceTwo = buildStreamWithDirtyStateDir(stateDirPath + "/" +
appId + "-2/", instanceLatch);
+ streamInstanceOne = buildStreamWithDirtyStateDir(stateDirPath + "/" +
appId + "-1/", instanceLatch, transactionalStateStores);
+ streamInstanceTwo = buildStreamWithDirtyStateDir(stateDirPath + "/" +
appId + "-2/", instanceLatch, transactionalStateStores);
startApplicationAndWaitUntilRunning(asList(streamInstanceOne,
streamInstanceTwo), Duration.ofSeconds(60));
@@ -160,12 +162,13 @@ public class StandbyTaskEOSIntegrationTest {
}
private KafkaStreams buildStreamWithDirtyStateDir(final String
stateDirPath,
- final CountDownLatch
recordProcessLatch) throws Exception {
+ final CountDownLatch
recordProcessLatch,
+ final boolean
transactionalStateStores) throws Exception {
final StreamsBuilder builder = new StreamsBuilder();
final TaskId taskId = new TaskId(0, 0);
- final Properties props = props(stateDirPath);
+ final Properties props = props(stateDirPath, transactionalStateStores);
final StateDirectory stateDirectory = new StateDirectory(
new StreamsConfig(props), new MockTime(), true, false);
@@ -186,8 +189,9 @@ public class StandbyTaskEOSIntegrationTest {
return new KafkaStreams(builder.build(), props);
}
- @Test
- public void shouldWipeOutStandbyStateDirectoryIfCheckpointIsMissing()
throws Exception {
+ @ParameterizedTest
+ @ValueSource(booleans = {false, true})
+ public void shouldWipeOutStandbyStateDirectoryIfCheckpointIsMissing(final
boolean transactionalStateStores) throws Exception {
final long time = System.currentTimeMillis();
final String base = TestUtils.tempDirectory(appId).getPath();
@@ -205,8 +209,8 @@ public class StandbyTaskEOSIntegrationTest {
10L + time
);
- streamInstanceOne = buildWithDeduplicationTopology(base + "-1");
- streamInstanceTwo = buildWithDeduplicationTopology(base + "-2");
+ streamInstanceOne = buildWithDeduplicationTopology(base + "-1",
transactionalStateStores);
+ streamInstanceTwo = buildWithDeduplicationTopology(base + "-2",
transactionalStateStores);
// start first instance and wait for processing
startApplicationAndWaitUntilRunning(streamInstanceOne);
@@ -273,7 +277,7 @@ public class StandbyTaskEOSIntegrationTest {
2
);
- streamInstanceOneRecovery = buildWithDeduplicationTopology(base +
"-1");
+ streamInstanceOneRecovery = buildWithDeduplicationTopology(base +
"-1", transactionalStateStores);
// "restart" first client and wait for standby recovery
// (could actually also be active, but it does not matter as long as
we enable "state stores"
@@ -321,7 +325,8 @@ public class StandbyTaskEOSIntegrationTest {
);
}
- private KafkaStreams buildWithDeduplicationTopology(final String
stateDirPath) {
+ private KafkaStreams buildWithDeduplicationTopology(final String
stateDirPath,
+ final boolean
transactionalStateStores) {
final StreamsBuilder builder = new StreamsBuilder();
builder.addStateStore(Stores.keyValueStoreBuilder(
@@ -372,11 +377,11 @@ public class StandbyTaskEOSIntegrationTest {
)
.to(outputTopic);
- return new KafkaStreams(builder.build(), props(stateDirPath));
+ return new KafkaStreams(builder.build(), props(stateDirPath,
transactionalStateStores));
}
- private Properties props(final String stateDirPath) {
+ private Properties props(final String stateDirPath, final boolean
transactionalStateStores) {
final Properties streamsConfiguration = new Properties();
streamsConfiguration.put(StreamsConfig.APPLICATION_ID_CONFIG, appId);
streamsConfiguration.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG,
CLUSTER.bootstrapServers());
@@ -384,6 +389,10 @@ public class StandbyTaskEOSIntegrationTest {
streamsConfiguration.put(StreamsConfig.STATE_DIR_CONFIG, stateDirPath);
streamsConfiguration.put(StreamsConfig.NUM_STANDBY_REPLICAS_CONFIG, 1);
streamsConfiguration.put(StreamsConfig.PROCESSING_GUARANTEE_CONFIG,
StreamsConfig.EXACTLY_ONCE_V2);
+ if (transactionalStateStores) {
+ // Transactional state stores are only supported under
exactly-once, which is already enabled above.
+
streamsConfiguration.put(StreamsConfig.TRANSACTIONAL_STATE_STORES_CONFIG, true);
+ }
streamsConfiguration.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG,
Serdes.IntegerSerde.class);
streamsConfiguration.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG,
Serdes.IntegerSerde.class);
streamsConfiguration.put(StreamsConfig.COMMIT_INTERVAL_MS_CONFIG,
1000L);
diff --git
a/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/StreamStreamJoinIntegrationTest.java
b/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/StreamStreamJoinIntegrationTest.java
index f6fbe1c75f8..adbd0f8b278 100644
---
a/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/StreamStreamJoinIntegrationTest.java
+++
b/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/StreamStreamJoinIntegrationTest.java
@@ -91,13 +91,14 @@ public class StreamStreamJoinIntegrationTest extends
AbstractJoinIntegrationTest
}
@ParameterizedTest
- @CsvSource({"true, false", "true, true", "false, false", "false, true"})
- public void testInner(final boolean cacheEnabled, final boolean
withHeaders) {
+ @CsvSource({"true, false, false", "true, true, false", "false, false,
false", "false, true, false", "false, false, true"})
+ public void testInner(final boolean cacheEnabled, final boolean
withHeaders, final boolean transactional) {
final StreamsBuilder builder = new StreamsBuilder();
final KStream<Long, String> leftStream =
builder.stream(INPUT_TOPIC_LEFT);
final KStream<Long, String> rightStream =
builder.stream(INPUT_TOPIC_RIGHT);
final Properties streamsConfig = setupConfigsAndUtils(cacheEnabled);
streamsConfig.put(StreamsConfig.APPLICATION_ID_CONFIG, APP_ID +
"-inner");
+ maybeEnableTransactionalStateStores(streamsConfig, transactional);
StreamsTestUtils.maybeSetDslStoreFormatHeaders(streamsConfig,
withHeaders);
@@ -201,13 +202,14 @@ public class StreamStreamJoinIntegrationTest extends
AbstractJoinIntegrationTest
}
@ParameterizedTest
- @CsvSource({"true, false", "true, true", "false, false", "false, true"})
- public void testLeft(final boolean cacheEnabled, final boolean
withHeaders) {
+ @CsvSource({"true, false, false", "true, true, false", "false, false,
false", "false, true, false", "false, false, true"})
+ public void testLeft(final boolean cacheEnabled, final boolean
withHeaders, final boolean transactional) {
final StreamsBuilder builder = new StreamsBuilder();
final KStream<Long, String> leftStream =
builder.stream(INPUT_TOPIC_LEFT);
final KStream<Long, String> rightStream =
builder.stream(INPUT_TOPIC_RIGHT);
final Properties streamsConfig = setupConfigsAndUtils(cacheEnabled);
streamsConfig.put(StreamsConfig.APPLICATION_ID_CONFIG, APP_ID +
"-left");
+ maybeEnableTransactionalStateStores(streamsConfig, transactional);
StreamsTestUtils.maybeSetDslStoreFormatHeaders(streamsConfig,
withHeaders);
@@ -313,13 +315,14 @@ public class StreamStreamJoinIntegrationTest extends
AbstractJoinIntegrationTest
}
@ParameterizedTest
- @CsvSource({"true, false", "true, true", "false, false", "false, true"})
- public void testOuter(final boolean cacheEnabled, final boolean
withHeaders) {
+ @CsvSource({"true, false, false", "true, true, false", "false, false,
false", "false, true, false", "false, false, true"})
+ public void testOuter(final boolean cacheEnabled, final boolean
withHeaders, final boolean transactional) {
final StreamsBuilder builder = new StreamsBuilder();
final KStream<Long, String> leftStream =
builder.stream(INPUT_TOPIC_LEFT);
final KStream<Long, String> rightStream =
builder.stream(INPUT_TOPIC_RIGHT);
final Properties streamsConfig = setupConfigsAndUtils(cacheEnabled);
streamsConfig.put(StreamsConfig.APPLICATION_ID_CONFIG, APP_ID +
"-outer");
+ maybeEnableTransactionalStateStores(streamsConfig, transactional);
StreamsTestUtils.maybeSetDslStoreFormatHeaders(streamsConfig,
withHeaders);
@@ -531,4 +534,13 @@ public class StreamStreamJoinIntegrationTest extends
AbstractJoinIntegrationTest
runTestWithDriver(inputWithoutOutOfOrderData, expectedResult,
streamsConfig, builder.build(streamsConfig));
}
+
+ // Transactional state stores (KIP-892) are only supported under
exactly-once, so whenever
+ // enable.transactional.statestores=true we must also set
processing.guarantee=exactly_once_v2.
+ private static void maybeEnableTransactionalStateStores(final Properties
streamsConfig, final boolean transactional) {
+ if (transactional) {
+ streamsConfig.put(StreamsConfig.TRANSACTIONAL_STATE_STORES_CONFIG,
true);
+ streamsConfig.put(StreamsConfig.PROCESSING_GUARANTEE_CONFIG,
StreamsConfig.EXACTLY_ONCE_V2);
+ }
+ }
}
diff --git
a/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/SuppressionDurabilityIntegrationTest.java
b/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/SuppressionDurabilityIntegrationTest.java
index 21acb226fc3..1c6b275f747 100644
---
a/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/SuppressionDurabilityIntegrationTest.java
+++
b/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/SuppressionDurabilityIntegrationTest.java
@@ -51,7 +51,7 @@ import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.ValueSource;
+import org.junit.jupiter.params.provider.CsvSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -103,9 +103,17 @@ public class SuppressionDurabilityIntegrationTest {
private static final LongDeserializer LONG_DESERIALIZER = new
LongDeserializer();
private static final long COMMIT_INTERVAL = 100L;
- @ParameterizedTest
- @ValueSource(booleans = {false, true})
- public void shouldRecoverBufferAfterShutdown(final boolean withHeaders,
final TestInfo testInfo) {
+ @ParameterizedTest(name = "{displayName} withHeaders={0},
transactional={1}")
+ @CsvSource({
+ // withHeaders, transactional
+ // transactional=false keeps the existing (at-least-once) coverage
over the DSL store-format header dimension.
+ "false, false",
+ "true, false",
+ // transactional=true always implies exactly-once-v2; a single sparse
invocation exercises the
+ // suppress-buffer restore path over transactional (KIP-892) state
stores.
+ "false, true"
+ })
+ public void shouldRecoverBufferAfterShutdown(final boolean withHeaders,
final boolean transactional, final TestInfo testInfo) {
final String testId = safeUniqueTestName(testInfo);
final String appId = "appId_" + testId;
final String input = "input" + testId;
@@ -154,6 +162,12 @@ public class SuppressionDurabilityIntegrationTest {
streamsConfig.put(StreamsConfig.COMMIT_INTERVAL_MS_CONFIG,
COMMIT_INTERVAL);
StreamsTestUtils.maybeSetDslStoreFormatHeaders(streamsConfig,
withHeaders);
+ if (transactional) {
+ // Transactional state stores (KIP-892) require exactly-once-v2.
+ streamsConfig.put(StreamsConfig.TRANSACTIONAL_STATE_STORES_CONFIG,
true);
+ streamsConfig.put(StreamsConfig.PROCESSING_GUARANTEE_CONFIG,
StreamsConfig.EXACTLY_ONCE_V2);
+ }
+
KafkaStreams driver = getStartedStreams(streamsConfig, builder, true);
try {
// start by putting some stuff in the buffer
diff --git
a/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/VersionedKeyValueStoreIntegrationTest.java
b/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/VersionedKeyValueStoreIntegrationTest.java
index 740b5b1af5c..0da0fcb8077 100644
---
a/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/VersionedKeyValueStoreIntegrationTest.java
+++
b/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/VersionedKeyValueStoreIntegrationTest.java
@@ -63,6 +63,8 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
import java.io.IOException;
import java.time.Duration;
@@ -125,8 +127,9 @@ public class VersionedKeyValueStoreIntegrationTest {
}
}
- @Test
- public void shouldPutGetAndDelete() throws Exception {
+ @ParameterizedTest
+ @ValueSource(booleans = {false, true})
+ public void shouldPutGetAndDelete(final boolean transactionalStateStores)
throws Exception {
// build topology and start app
final StreamsBuilder streamsBuilder = new StreamsBuilder();
@@ -142,7 +145,7 @@ public class VersionedKeyValueStoreIntegrationTest {
.process(() -> new VersionedStoreContentCheckerProcessor(true),
STORE_NAME)
.to(outputStream, Produced.with(Serdes.Integer(),
Serdes.Integer()));
- final Properties props = props();
+ final Properties props = props(transactionalStateStores);
kafkaStreams = new KafkaStreams(streamsBuilder.build(), props);
kafkaStreams.start();
@@ -208,8 +211,9 @@ public class VersionedKeyValueStoreIntegrationTest {
assertThat(changelogTopicConfig.getProperty("min.compaction.lag.ms"),
equalTo(Long.toString(HISTORY_RETENTION + 24 * 60 * 60 * 1000L)));
}
- @Test
- public void shouldRestore() throws Exception {
+ @ParameterizedTest
+ @ValueSource(booleans = {false, true})
+ public void shouldRestore(final boolean transactionalStateStores) throws
Exception {
// build topology and start app
StreamsBuilder streamsBuilder = new StreamsBuilder();
@@ -225,7 +229,7 @@ public class VersionedKeyValueStoreIntegrationTest {
.process(() -> new VersionedStoreContentCheckerProcessor(true),
STORE_NAME)
.to(outputStream, Produced.with(Serdes.Integer(),
Serdes.Integer()));
- final Properties props = props();
+ final Properties props = props(transactionalStateStores);
kafkaStreams = new KafkaStreams(streamsBuilder.build(), props);
kafkaStreams.start();
@@ -428,6 +432,10 @@ public class VersionedKeyValueStoreIntegrationTest {
}
private Properties props() {
+ return props(false);
+ }
+
+ private Properties props(final boolean transactionalStateStores) {
final String safeTestName = safeUniqueTestName(testInfo);
final Properties streamsConfiguration = new Properties();
streamsConfiguration.put(StreamsConfig.APPLICATION_ID_CONFIG, "app-" +
safeTestName);
@@ -435,6 +443,11 @@ public class VersionedKeyValueStoreIntegrationTest {
streamsConfiguration.put(StreamsConfig.STATE_DIR_CONFIG,
TestUtils.tempDirectory().getPath());
streamsConfiguration.put(StreamsConfig.COMMIT_INTERVAL_MS_CONFIG,
1000L);
streamsConfiguration.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,
"earliest");
+ if (transactionalStateStores) {
+ // Transactional state stores are only supported under
exactly-once.
+
streamsConfiguration.put(StreamsConfig.TRANSACTIONAL_STATE_STORES_CONFIG, true);
+
streamsConfiguration.put(StreamsConfig.PROCESSING_GUARANTEE_CONFIG,
StreamsConfig.EXACTLY_ONCE_V2);
+ }
return streamsConfiguration;
}