[GSoC 2026] Kafka Streams runner: Python wrapper that starts its own job server - #39680
Conversation
…job server The runner starting its own job server only helped Java, so a Python user still had to run one by hand. This adds the wrapper Flink and Spark provide, so a Python pipeline can select the runner and nothing else. runners/kafka-streams/job-server packages the runner into one shaded jar, so the job server can be launched without a Beam source tree, in the same shape as the Flink and Spark job server modules. It carries an SLF4J binding: the Spark module excludes one because Spark supplies its own, and copying that left the jar starting up and then logging nothing, which is unhelpful for a process a user runs in the foreground to watch their pipeline. On the Python side KafkaStreamsRunner extends PortableRunner, resolves the jar through JavaJarJobServer, and defaults the environment to LOOPBACK so a local run needs no Docker. KafkaStreamsRunnerOptions adds bootstrap_servers, application_id and kafka_streams_job_server_jar. Beam's Python is not built in my environment, so each link of the chain was checked separately rather than assumed: the jar starts all three services on the arguments the Python runner passes, those arguments are all accepted by JobServerDriver.ServerConfiguration, and against an installed Beam with these files grafted in the runner resolves, the options parse, the environment defaults to LOOPBACK and java_arguments produces those flags. A Python pipeline running end to end is the part still unverified.
|
Checks are failing. Will not request review until checks are succeeding. If you'd like to override that behavior, comment |
je-ik
left a comment
There was a problem hiding this comment.
I think we should also add a test (similar to flink and spark), otherwise this LGMT!
Covers what the Python wrapper is responsible for: that job servers are cached across runner instances and keyed on the options that actually change their configuration, that the jar is resolved from the job server module, and that the driver is launched with arguments it accepts. Follows spark_java_job_server_test.py.
Added kafka_streams_java_job_server_test.py, following spark_java_job_server_test.py. It covers the job server cache (and that it's keyed only on the two options groups that change the server's configuration), the jar resolution, and the arguments the driver is launched with. I checked the tests actually fail when the code is wrong rather than just passing — dropping the runner options from the cache key breaks the cache test, and pointing the gradle target elsewhere breaks the jar test. One thing I want to check with you: flink and spark also have the runner-level suite (spark_runner_test.py / flink_runner_test.py) that extends PortableRunnerTest. That needs a gradle task, a workflow, and a real broker in CI, so I didn't put it in this PR. Should I file it as a follow-up issue, or do you want it here before merging? |
Summary
Part of #18479. Follows the review point on #39627: the runner starting its own job server only helped Java, and a Python user still had to run one by hand. This adds the wrapper Flink and Spark provide, so a Python pipeline can select the runner and nothing else.
The job server jar
A portable runner needs its job server to be launchable without a Beam source tree, so
runners/kafka-streams/job-serverpackages the runner and its dependencies into one shaded jar, inthe same shape as the Flink and Spark job server modules.
KafkaStreamsRunneron the Python sidebuilds it if it has to, starts it, and stops it with the pipeline.
The module carries an SLF4J binding. Without one the jar starts and then logs nothing at all, which
is unhelpful for a process a user runs in the foreground to watch their pipeline — the Spark module
excludes the binding because Spark supplies its own, and copying that left us silent.
The Python runner
KafkaStreamsRunnerextendsPortableRunner, resolves the jar throughJavaJarJobServer, anddefaults the environment to
LOOPBACKso a local run needs no Docker.KafkaStreamsRunnerOptionsadds
--bootstrap_servers,--application_idand--kafka_streams_job_server_jar, the last ofwhich takes a prebuilt jar and skips the build.
Testing
Beam's Python is not built in my environment, so rather than assert this works I checked each link
of the chain against something real:
expansion — on the ports it is given.
--artifacts-dir,--job-port,--artifact-port,--expansion-port) are all accepted byJobServerDriver.ServerConfiguration, and our driveroverrides none of them. The jar was run with exactly that argument set.
--runner=KafkaStreamsRunnerresolves,the options parse,
default_environmentsetsLOOPBACK, andjava_argumentsproduces the flagsabove.
:runners:kafka-streams:job-server:shadowJar, the target the runner asks Beam to build, exists.What that leaves untested is a Python pipeline actually running through to completion, which needs a
built Beam Python SDK. If CI does not cover that path I would rather it were checked before this is
relied on.
The runner documentation gains a Python section, which I will add to #39627 since the page is still
open for review there.