[GSoC 2026] Kafka Streams runner: run against a real broker - #39546
Open
junaiddshaukat wants to merge 1 commit into
Open
[GSoC 2026] Kafka Streams runner: run against a real broker#39546junaiddshaukat wants to merge 1 commit into
junaiddshaukat wants to merge 1 commit into
Conversation
Adds what the runner needs to execute on an actual Kafka cluster, and an integration test that runs a pipeline through the production KafkaStreamsPipelineRunner against a Kafka container. Everything until now ran through TopologyTestDriver, which fakes the topics and never builds a KafkaStreams application, so the production path had not been executed. KafkaStreamsTopicManager creates the topics the runner names for itself - a bootstrap topic per Impulse and primitive Read, and a repartition topic per GroupByKey - with an AdminClient before the application starts. Kafka Streams treats them as user topics because they are declared with explicit names, so it does not create them and refuses to start when a source topic is missing; the broker's auto-creation is not a substitute, since it is often disabled and gives the topic the broker's default partition count. Only topics carrying one of the runner's prefixes are created, so a topic the user named is never created implicitly. Adds topicPartitions and topicReplicationFactor options. Fixes two bugs the test driver could not catch. Validation demanded jobEndpoint, which KafkaStreamsPipelineOptions inherits as required from PortablePipelineOptions but which is a client-side option: this code runs on the job server for an already-submitted pipeline, so it rejected every valid job. Only the options the runner reads are checked now, as Flink's equivalent PortablePipelineRunner does. Separately, the result object registers a KafkaStreams state listener in its constructor but was built after start(), and Kafka Streams only accepts a listener in the CREATED state, so every real startup threw and the listener behind waitUntilFinish and cancel was never installed; the result is now built before the application starts. The integration test needs Docker, so the default test task excludes it and a brokerIntegrationTest task runs it. Also adds PortableWindowingStrategyTest, checking that the windowing the runner reconstructs comes from the standard beam:window_fn URNs every SDK emits rather than anything Java-specific.
Contributor
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
Contributor
|
Assigning reviewers: R: @chamikaramj added as fallback since no labels match configuration Note: If you would like to opt out of this review, comment Available commands:
The PR bot will only process comments in the main thread (not review comments). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Part of #18479.
Makes the runner able to run against a real Kafka cluster, and adds the integration test that proves it. Until now everything ran through
TopologyTestDriver, which fakes the topics and never builds aKafkaStreamsapplication — so the production path had never actually been executed. Running it found two bugs that the test driver cannot structurally catch.This is also the groundwork for two things that were going to need it anyway: running the ValidatesRunner suite from another SDK through the job server, and the failure-recovery and scaling benchmark.
Creating the runner's topics
The runner shuffles through topics it names itself — a bootstrap topic per Impulse and per primitive Read, and a repartition topic per GroupByKey. Kafka Streams creates the internal topics it manages, but these are declared with explicit names through
addSourceandaddSink, so to Kafka Streams they are ordinary user topics: it does not create them, and refuses to start withMissingSourceTopicExceptionwhen a source topic is missing. Leaving it to the broker'sauto.create.topics.enableis not a fix either — it is off on many clusters, and a topic auto-created on first fetch gets the broker's default partition count instead of the pipeline's.KafkaStreamsTopicManagercreates them with anAdminClientbefore the application starts. It only creates topics carrying one of the runner's own prefixes; anything else in the topology is a topic the user named, and creating those implicitly would hide a misconfiguration behind an empty topic. A topic that appears between the check and the create — another instance of the same job starting concurrently — is treated as success.Two new options come with it:
topicPartitions(the parallelism the shuffled parts of a pipeline can reach) andtopicReplicationFactor.Two bugs the test driver could not catch
Options validation rejected every valid job.
runvalidated the wholeKafkaStreamsPipelineOptionsinterface, which inherits@Required jobEndpointfromPortablePipelineOptions:jobEndpointis a client-side option — the address a client uses to reach the job server. This code runs on the job server, executing a pipeline that has already been submitted, so it does not apply. Flink's equivalentPortablePipelineRunnerdoes not validate here either. Now only the options this runner actually reads are checked, which keeps the clear error that motivated the original call.The state listener was registered after the application had started.
The result object registers a
KafkaStreamsstate listener in its constructor, but it was constructed afterstart()had been called, and Kafka Streams only accepts a listener while the application is still inCREATED. This threw on every real startup, and the listener is what backswaitUntilFinish()andcancel(). The result is now built before the application starts, and the ordering requirement is documented on its constructor.Testing
KafkaStreamsRunnerBrokerITrunsImpulse -> ParDo -> GroupByKey -> ParDothrough the productionKafkaStreamsPipelineRunneragainst a Kafka container, and polls the pipeline's metrics until the group counter reaches the expected value. That covers the runner creating its own topics, records round-tripping through a real repartition topic, exactly-once processing, the state stores' changelog, and the application lifecycle.It uses testcontainers rather than an embedded broker because
library.java.testcontainers_kafkais already a declared Beam dependency and is what KafkaIO's own tests use.It needs Docker, so it is not part of the default build — the default
testtask excludes it and abrokerIntegrationTesttask runs it:Also adds
PortableWindowingStrategyTest, which checks that the windowing the runner reconstructs comes from the language-neutral windowing strategy in the proto — the standardbeam:window_fn:*URNs every SDK emits — rather than from anything Java-specific. That was a question raised on the windowing PR about whether the runner would work for a pipeline submitted from another SDK.