Skip to content

Commit f8ae1b0

Browse files
Add tests for DYNAMIC sentinel fallback paths
Cover the two regression cases surfaced in review: when num.stream.threads is set to DYNAMIC but the app has no resolver wired up, and when the resolver-supplier throws. In both cases the framework must replace the literal sentinel with FALLBACK_NUM_STREAM_THREADS so Kafka Streams gets a parseable integer.
1 parent 736f37d commit f8ae1b0

1 file changed

Lines changed: 53 additions & 0 deletions

File tree

  • kafka-streams-framework/src/test/java/org/hypertrace/core/kafkastreams/framework

kafka-streams-framework/src/test/java/org/hypertrace/core/kafkastreams/framework/SampleAppTest.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,25 @@
44
import static org.apache.kafka.streams.StreamsConfig.DEFAULT_DESERIALIZATION_EXCEPTION_HANDLER_CLASS_CONFIG;
55
import static org.apache.kafka.streams.StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG;
66
import static org.apache.kafka.streams.StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG;
7+
import static org.apache.kafka.streams.StreamsConfig.NUM_STREAM_THREADS_CONFIG;
78
import static org.apache.kafka.streams.StreamsConfig.ROCKSDB_CONFIG_SETTER_CLASS_CONFIG;
89
import static org.apache.kafka.streams.StreamsConfig.producerPrefix;
910
import static org.hamcrest.CoreMatchers.equalTo;
1011
import static org.hamcrest.CoreMatchers.is;
1112
import static org.hamcrest.MatcherAssert.assertThat;
1213

14+
import com.typesafe.config.Config;
1315
import io.confluent.kafka.streams.serdes.avro.SpecificAvroSerde;
1416
import java.util.Map;
17+
import java.util.Optional;
1518
import java.util.Properties;
1619
import org.apache.kafka.common.serialization.Serdes;
1720
import org.apache.kafka.streams.TestInputTopic;
1821
import org.apache.kafka.streams.TestOutputTopic;
1922
import org.apache.kafka.streams.TopologyTestDriver;
2023
import org.apache.kafka.streams.errors.LogAndContinueExceptionHandler;
2124
import org.hypertrace.core.kafkastreams.framework.rocksdb.BoundedMemoryConfigSetter;
25+
import org.hypertrace.core.kafkastreams.framework.threading.StreamThreadsCountResolver;
2226
import org.hypertrace.core.serviceframework.config.ConfigClientFactory;
2327
import org.junit.jupiter.api.AfterEach;
2428
import org.junit.jupiter.api.BeforeEach;
@@ -84,4 +88,53 @@ public void baseStreamsConfigTest() {
8488
is(LogAndContinueExceptionHandler.class));
8589
assertThat(baseStreamsConfig.get(producerPrefix(ACKS_CONFIG)), is("all"));
8690
}
91+
92+
// Verifies the fix for "DYNAMIC sentinel could leak to Kafka Streams config when no resolver is
93+
// wired up" — without resolver override, the framework must replace DYNAMIC with the integer
94+
// fallback so Kafka Streams receives a parseable value.
95+
@Test
96+
public void dynamicWithoutResolverFallsBackToEight() {
97+
SampleApp dynamicApp =
98+
new SampleApp(ConfigClientFactory.getClient()) {
99+
@Override
100+
public Map<String, Object> getStreamsConfig(Config jobConfig) {
101+
Map<String, Object> properties = super.getStreamsConfig(jobConfig);
102+
properties.put(NUM_STREAM_THREADS_CONFIG, StreamThreadsCountResolver.DYNAMIC_SENTINEL);
103+
return properties;
104+
}
105+
};
106+
107+
dynamicApp.doInit();
108+
109+
assertThat(
110+
dynamicApp.streamsConfig.get(NUM_STREAM_THREADS_CONFIG),
111+
is(StreamThreadsCountResolver.FALLBACK_NUM_STREAM_THREADS));
112+
}
113+
114+
// Verifies the fix for "exception while obtaining the resolver should not leak DYNAMIC" — when
115+
// getStreamThreadsCountResolver() throws, the framework must catch and fall back to the integer
116+
// default rather than letting the literal sentinel reach Kafka Streams.
117+
@Test
118+
public void dynamicWithThrowingResolverFallsBackToEight() {
119+
SampleApp dynamicApp =
120+
new SampleApp(ConfigClientFactory.getClient()) {
121+
@Override
122+
public Map<String, Object> getStreamsConfig(Config jobConfig) {
123+
Map<String, Object> properties = super.getStreamsConfig(jobConfig);
124+
properties.put(NUM_STREAM_THREADS_CONFIG, StreamThreadsCountResolver.DYNAMIC_SENTINEL);
125+
return properties;
126+
}
127+
128+
@Override
129+
protected Optional<StreamThreadsCountResolver> getStreamThreadsCountResolver() {
130+
throw new RuntimeException("simulated wiring failure");
131+
}
132+
};
133+
134+
dynamicApp.doInit();
135+
136+
assertThat(
137+
dynamicApp.streamsConfig.get(NUM_STREAM_THREADS_CONFIG),
138+
is(StreamThreadsCountResolver.FALLBACK_NUM_STREAM_THREADS));
139+
}
87140
}

0 commit comments

Comments
 (0)