-
Notifications
You must be signed in to change notification settings - Fork 4.6k
[GSoC 2026] Kafka Streams runner: user documentation, marked experimental #39627
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
junaiddshaukat
wants to merge
2
commits into
apache:feat/18479-kafka-streams-runner-skeleton
Choose a base branch
from
junaiddshaukat:feat/ks-usage-guide
base: feat/18479-kafka-streams-runner-skeleton
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
169 changes: 169 additions & 0 deletions
169
website/www/site/content/en/documentation/runners/kafkastreams.md
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,169 @@ | ||
| --- | ||
| type: runners | ||
| title: "Kafka Streams Runner" | ||
| --- | ||
| <!-- | ||
| Licensed 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. | ||
| --> | ||
|
|
||
| # Kafka Streams Runner | ||
|
|
||
| The Kafka Streams Runner executes Beam pipelines on [Kafka | ||
| Streams](https://kafka.apache.org/documentation/streams/), by translating a pipeline into a Kafka | ||
| Streams topology. | ||
|
|
||
| What distinguishes it from the other runners is that Kafka Streams is a library rather than a | ||
| cluster. There is no job manager and no resource manager to operate: an application is an ordinary | ||
| JVM process that reads from and writes to Kafka, and scaling it means starting more copies of that | ||
| process. Fault tolerance, state, and exactly-once processing come from Kafka itself — from consumer | ||
| groups, changelog topics, and transactions. | ||
|
|
||
| That makes it worth considering if you already run Kafka and want Beam's programming model without | ||
| introducing a second distributed system to operate. | ||
|
|
||
| ## The runner is experimental | ||
|
|
||
| **The Kafka Streams Runner is experimental.** It executes a meaningful subset of the Beam model | ||
| correctly, and the parts it does support are covered by Beam's own `@ValidatesRunner` suite, but | ||
| several capabilities that are core to the model are not implemented yet. Read [what is not | ||
| supported](#what-is-not-supported-yet) before choosing it for anything real. | ||
|
|
||
| It is also aimed squarely at streaming. A pipeline over bounded data will run, but there are more | ||
| efficient choices for batch work; this runner exists for pipelines that do not end. | ||
|
|
||
| ## Running a pipeline | ||
|
|
||
| The runner is portable: it executes user code over the Fn API, in an SDK harness, so a pipeline goes | ||
| to a job server rather than being run directly. From Java you do not have to start one yourself. | ||
|
|
||
| ### From Java | ||
|
|
||
| Select `KafkaStreamsRunner` and point it at your Kafka cluster: | ||
|
|
||
| ``` | ||
| --runner=KafkaStreamsRunner \ | ||
| --bootstrapServers=localhost:9092 \ | ||
| --applicationId=my-beam-pipeline | ||
| ``` | ||
|
|
||
| With no `jobEndpoint` set, the runner starts a job server of its own on a dynamic port, submits to | ||
| it, and shuts it down when the pipeline finishes. Setting `--jobEndpoint` instead submits to a job | ||
| server you are already running. | ||
|
|
||
| ### From another SDK, or against a shared job server | ||
|
|
||
| Start the job server, which listens on `localhost:8099` by default: | ||
|
|
||
| ``` | ||
| ./gradlew :runners:kafka-streams:runJobServer | ||
| ``` | ||
|
|
||
| Then submit to it with the portable runner: | ||
|
|
||
| ``` | ||
| --runner=PortableRunner \ | ||
| --job_endpoint=localhost:8099 \ | ||
| --bootstrapServers=localhost:9092 \ | ||
| --applicationId=my-beam-pipeline | ||
| ``` | ||
|
|
||
| `applicationId` has no default and must be set. It becomes the Kafka Streams `application.id`, which | ||
| is the identity of the consumer group and of the runner's internal topics, so two different | ||
| pipelines sharing one would interfere with each other. | ||
|
|
||
| ## Pipeline options | ||
|
|
||
| | Option | Default | Description | | ||
| | --- | --- | --- | | ||
| | `bootstrapServers` | `localhost:9092` | Kafka brokers the application connects to. | | ||
| | `applicationId` | *(required)* | Kafka Streams `application.id`. Must be unique per pipeline. | | ||
| | `internalParallelism` | `1` | Partitions for the internal topics the runner creates, which is the parallelism the shuffled parts of a pipeline can reach. | | ||
| | `topicReplicationFactor` | `1` | Replication factor for those topics. | | ||
| | `maxBundleSize` | `1000` | Elements per bundle, and elements taken per poll of an unbounded source. | | ||
| | `maxBundleTimeMs` | `1000` | Intended cap on how long a bundle may stay open. **Not applied yet** — see below. | | ||
| | `readCheckpointNumBundles` | `10` | Polls of an unbounded source between stores of its checkpoint mark. Larger values replay more after a restart. | | ||
| | `stateDir` | temp directory | Where Kafka Streams keeps local state. | | ||
|
|
||
| ### Topics the runner creates | ||
|
|
||
| The runner shuffles through topics it names itself and creates before starting: a bootstrap topic | ||
| per `Impulse` and per source, and a repartition topic per `GroupByKey`. They carry a `__beam_` | ||
| prefix. Bootstrap topics always have one partition; repartition topics get `internalParallelism`, | ||
| which is what sets how many instances the parts of the pipeline behind a shuffle run across. | ||
|
|
||
| Topics the pipeline itself reads or writes are never created implicitly. | ||
|
|
||
| ## What is supported | ||
|
|
||
| * **Reading** — bounded and unbounded sources, through the primitive `Read`. | ||
| * **ParDo** — stateless, including multiple outputs. | ||
| * **GroupByKey**, and `Combine` through its GroupByKey expansion. | ||
| * **Windowing** — global, fixed and sliding windows, with the default trigger, allowed lateness, and | ||
| timestamp combiners. Windowing and triggering run through Beam's own `ReduceFnRunner`, backed by | ||
| Kafka Streams state and timers. | ||
| * **Flatten**, **Redistribute**. | ||
| * **Metrics** — user counters and distributions reported by the SDK harness surface as | ||
| `MetricResults`. | ||
| * **Exactly-once processing**, via Kafka transactions (`exactly_once_v2`). | ||
|
|
||
| Because the runner is portable and reads the language-neutral pipeline proto, a pipeline built in | ||
| any Beam SDK should translate, provided it stays inside the subset above. Only the Java SDK has been | ||
| exercised so far. | ||
|
|
||
| ## What is not supported yet | ||
|
je-ik marked this conversation as resolved.
|
||
|
|
||
| These are core parts of the Beam model that the runner does not implement. Each is a real gap rather | ||
| than a decision, and each is tracked: | ||
|
|
||
| * **Side inputs** ([#39628](https://github.com/apache/beam/issues/39628)). | ||
| * **Stateful `ParDo` and user timers** ([#39629](https://github.com/apache/beam/issues/39629)) — | ||
| including timer families, looping timers | ||
| and processing-time timers. | ||
| * **Merging windows**, so session windows do not work, and **custom `WindowFn`s** | ||
| ([#39630](https://github.com/apache/beam/issues/39630)). The standard windows travel as URNs the | ||
| runner interprets directly; one the | ||
| user wrote themselves would have to run in the SDK harness, which is not wired up. | ||
| * **Splittable `DoFn`**, bounded or unbounded | ||
| ([#39631](https://github.com/apache/beam/issues/39631)). | ||
| * **`TestStream`** ([#39632](https://github.com/apache/beam/issues/39632)). | ||
| * **Reading a source in parallel** | ||
| ([#39626](https://github.com/apache/beam/issues/39626)). A source is split into exactly one part | ||
| and read by a single reader. A source that insists on splitting further is rejected at | ||
| translation rather than having its extra splits silently dropped. | ||
| * **A time bound on bundles** ([#39633](https://github.com/apache/beam/issues/39633)). | ||
| `maxBundleTimeMs` is accepted but has no effect: | ||
| closing a bundle from a wall-clock punctuator duplicated output against a real broker, and the | ||
| cause is not yet understood. Bundles are bounded by element count and closed on watermarks. | ||
| * **`finalizeCheckpoint`** ([#39634](https://github.com/apache/beam/issues/39634)) is not called on | ||
| an unbounded source's checkpoint mark, | ||
| so sources that rely on finalization to acknowledge data will not see it. | ||
| * **Committed metrics** ([#39635](https://github.com/apache/beam/issues/39635)) — only attempted | ||
| values are reported. | ||
|
|
||
| ## How it works | ||
|
|
||
| A Beam pipeline arrives as a proto and is translated into a Kafka Streams `Topology`. Fused stages | ||
| of user code become processors that execute that code in an SDK harness over the Fn API; a | ||
| `GroupByKey` becomes a repartition topic plus a stateful processor; and the elements flowing between | ||
| them carry either data or a watermark report. | ||
|
|
||
| Watermarks are the part with no direct Kafka Streams equivalent. Kafka Streams tracks stream-time, | ||
| which only advances when data arrives, whereas Beam needs a watermark that can advance on an idle | ||
| stream and that reflects every upstream instance. The runner therefore propagates its own watermark | ||
| reports alongside the data: a transform aggregates the reports of everything upstream of it, holds | ||
| until every partition of every upstream transform has reported, and only then lets its own watermark | ||
| advance. | ||
|
|
||
| For the full design, see the [design | ||
| document](https://docs.google.com/document/d/1BBMURhSG4SxPcvvnKMTrmnKCr_jhXL6R4TBDBW7zsy8/edit) and | ||
| the tracking issue, [#18479](https://github.com/apache/beam/issues/18479). | ||
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably should provide a "wrappers" as flink does that will run their own jobserver automatically.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, and it turned out we already have one — I'd just documented the wrong path. KafkaStreamsRunner.run() starts a KafkaStreamsJobServerDriver on a dynamic port when jobEndpoint is empty, and stops it when the pipeline finishes, so from Java there is nothing to start by hand.
The page now leads with --runner=KafkaStreamsRunner for that, and keeps the manual job server as what you would use from another SDK or against a shared deployment. My original text only showed the manual route, which made the runner look more awkward to use than it is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Flink adds a similar wrapper for python (and maybe go as well?), you can check it for inspiration.