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 b30cd5f6a9d KAFKA-20760: Break out PositionRestartIntegrationTest into
sub-class for transactional stores (#22823)
b30cd5f6a9d is described below
commit b30cd5f6a9dbda39f0dc59643f215326ad81d8ad
Author: Bill Bejeck <[email protected]>
AuthorDate: Thu Jul 16 14:49:51 2026 -0400
KAFKA-20760: Break out PositionRestartIntegrationTest into sub-class for
transactional stores (#22823)
The transactional (KIP-892) Position-across-restart coverage previously
added to PositionRestartIntegrationTest expanded the parameter matrix
across the full cache x log x store x kind cross-product, roughly
doubling the case count and runtime and making the test prone to
timeouts.
This splits the concern:
- PositionRestartIntegrationTest returns to its original
non-transactional matrix,
with small hooks (an overridable `data()` source and a
`transactional()` flag)
so the machinery can be reused
- TransactionalPositionRestartIntegrationTest extends it and supplies a
compact
covering set for the transactional path: every persistent and
in-memory store
family (transactional state stores are supported for both), log
enabled
throughout, rotating cache and kind so both values are exercised
without a full
cross-product, all under exactly-once-v2.
Reviewers: Nick Telford <[email protected]>, Matthias J. Sax
<[email protected]>
---
.../PositionRestartIntegrationTest.java | 48 ++++++++------
...ransactionalPositionRestartIntegrationTest.java | 74 ++++++++++++++++++++++
2 files changed, 103 insertions(+), 19 deletions(-)
diff --git
a/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/PositionRestartIntegrationTest.java
b/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/PositionRestartIntegrationTest.java
index 612f9633256..66d65f27423 100644
---
a/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/PositionRestartIntegrationTest.java
+++
b/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/PositionRestartIntegrationTest.java
@@ -69,6 +69,7 @@ import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
@@ -101,6 +102,9 @@ import static org.hamcrest.Matchers.is;
@Tag("integration")
@Timeout(600)
+// PER_CLASS lets the parameter source `data()` be a non-static, overridable
method so subclasses
+// (e.g. the transactional variant) can supply a different matrix while
reusing all the machinery below.
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class PositionRestartIntegrationTest {
private static final Logger LOG =
LoggerFactory.getLogger(PositionRestartIntegrationTest.class);
private static final long SEED = new Random().nextLong();
@@ -252,19 +256,22 @@ public class PositionRestartIntegrationTest {
}
}
- public static Stream<Arguments> data() {
+ /** Whether the store-under-test is configured transactional (KIP-892).
Overridden by the transactional variant. */
+ protected boolean transactional() {
+ return false;
+ }
+
+ protected Stream<Arguments> data() {
LOG.info("Generating test cases according to random seed: {}", SEED);
final List<Arguments> values = new ArrayList<>();
for (final boolean cacheEnabled : Arrays.asList(true, false)) {
for (final boolean logEnabled : Arrays.asList(true, false)) {
- for (final boolean transactional : Arrays.asList(true, false))
{
- for (final StoresToTest toTest : StoresToTest.values()) {
- // We don't need to test if non-persistent stores
without logging
- // survive restarts, since those are by definition not
durable.
- if (logEnabled ||
toTest.supplier().get().persistent()) {
- for (final String kind : Arrays.asList("DSL",
"PAPI")) {
- values.add(Arguments.of(cacheEnabled,
logEnabled, transactional, toTest, kind));
- }
+ for (final StoresToTest toTest : StoresToTest.values()) {
+ // We don't need to test if non-persistent stores without
logging
+ // survive restarts, since those are by definition not
durable.
+ if (logEnabled || toTest.supplier().get().persistent()) {
+ for (final String kind : Arrays.asList("DSL", "PAPI"))
{
+ values.add(Arguments.of(cacheEnabled, logEnabled,
toTest, kind));
}
}
}
@@ -365,8 +372,8 @@ public class PositionRestartIntegrationTest {
@ParameterizedTest
@MethodSource("data")
- public void verifyStore(final boolean cache, final boolean log, final
boolean transactional, final StoresToTest storeToTest, final String kind) {
- final Properties streamsConfig = streamsConfiguration(cache, log,
transactional, storeToTest.name(), kind);
+ public void verifyStore(final boolean cache, final boolean log, final
StoresToTest storeToTest, final String kind) {
+ final Properties streamsConfig = streamsConfiguration(cache, log,
storeToTest.name(), kind);
final StreamsBuilder streamsBuilder = getStreamBuilder(cache, log,
storeToTest, kind);
kafkaStreams = IntegrationTestUtils.getStartedStreams(streamsConfig,
streamsBuilder, true);
@@ -650,14 +657,13 @@ public class PositionRestartIntegrationTest {
.process(processorSupplier, sessionStoreStoreBuilder.name());
}
- private static Properties streamsConfiguration(final boolean cache,
- final boolean log,
- final boolean transactional,
- final String supplier,
- final String kind) {
+ protected Properties streamsConfiguration(final boolean cache,
+ final boolean log,
+ final String supplier,
+ final String kind) {
final String safeTestName =
- PositionRestartIntegrationTest.class.getName() + "-" + cache + "-"
+ log + "-" + transactional + "-"
- + supplier + "-" + kind;
+ getClass().getName() + "-" + cache + "-" + log + "-"
+ + supplier + "-" + kind + "-" + transactional();
final Properties config = new Properties();
config.put(StreamsConfig.TOPOLOGY_OPTIMIZATION_CONFIG,
StreamsConfig.OPTIMIZE);
config.put(StreamsConfig.APPLICATION_ID_CONFIG, "app-" + safeTestName);
@@ -671,7 +677,11 @@ public class PositionRestartIntegrationTest {
config.put(StreamsConfig.COMMIT_INTERVAL_MS_CONFIG, 100L);
config.put(StreamsConfig.NUM_STREAM_THREADS_CONFIG, 1);
config.put(InternalConfig.IQ_CONSISTENCY_OFFSET_VECTOR_ENABLED, true);
- config.put(StreamsConfig.TRANSACTIONAL_STATE_STORES_CONFIG,
transactional);
+ if (transactional()) {
+ // Transactional state stores (KIP-892) are only meaningful under
exactly-once.
+ config.put(StreamsConfig.TRANSACTIONAL_STATE_STORES_CONFIG, true);
+ config.put(StreamsConfig.PROCESSING_GUARANTEE_CONFIG,
StreamsConfig.EXACTLY_ONCE_V2);
+ }
return config;
}
}
\ No newline at end of file
diff --git
a/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/TransactionalPositionRestartIntegrationTest.java
b/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/TransactionalPositionRestartIntegrationTest.java
new file mode 100644
index 00000000000..716eccefb0b
--- /dev/null
+++
b/streams/integration-tests/src/test/java/org/apache/kafka/streams/integration/TransactionalPositionRestartIntegrationTest.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.kafka.streams.integration;
+
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.Timeout;
+import org.junit.jupiter.params.provider.Arguments;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+/**
+ * Exercises the transactional (KIP-892) Position-across-restart path, reusing
all of
+ * {@link PositionRestartIntegrationTest}'s machinery (topology building,
produce, restart, IQ position
+ * verification) by overriding two hooks: {@link #transactional()} (turns on
+ * {@code enable.transactional.statestores} + {@code exactly_once_v2}) and
{@link #data()} (supplies a
+ * transactional-only matrix).
+ *
+ * <p>Keeping this separate from the parent keeps the (large)
non-transactional matrix and the transactional
+ * matrix in independent test targets, each with its own time budget, rather
than multiplying one big
+ * cross-product.
+ */
+@Tag("integration")
+@Timeout(600)
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class TransactionalPositionRestartIntegrationTest extends
PositionRestartIntegrationTest {
+
+ @Override
+ protected boolean transactional() {
+ return true;
+ }
+
+ @Override
+ protected Stream<Arguments> data() {
+ // Every persistent and in-memory store family, crossed with caching
on/off. log=true throughout so
+ // in-memory stores survive the restart via changelog restore; kind is
rotated (not crossed) since it
+ // is store-level-independent for the Position API.
+ final List<StoresToTest> stores = List.of(
+ StoresToTest.ROCKS_KV,
+ StoresToTest.TIME_ROCKS_KV,
+ StoresToTest.ROCKS_WINDOW,
+ StoresToTest.TIME_ROCKS_WINDOW,
+ StoresToTest.ROCKS_SESSION,
+ StoresToTest.IN_MEMORY_KV,
+ StoresToTest.IN_MEMORY_WINDOW,
+ StoresToTest.IN_MEMORY_SESSION
+ );
+ final List<Arguments> values = new ArrayList<>();
+ int i = 0;
+ for (final StoresToTest store : stores) {
+ for (final boolean cache : List.of(false, true)) {
+ final String kind = (i++ % 2 == 0) ? "DSL" : "PAPI";
+ values.add(Arguments.of(cache, true, store, kind));
+ }
+ }
+ return values.stream();
+ }
+}