Hi all,

I would like to propose merging the Kafka Streams runner from its feature
branch into master. It has been developed this summer as a Google Summer of
Code project under the tracking issue [1]
<https://github.com/apache/beam/issues/18479>, mentored by Jan Lukavský.
The pull request is at [2] <https://github.com/apache/beam/pull/39785>.

What it is. A portable runner that translates a Beam pipeline into a Kafka
Streams topology and executes user code over the Fn API. What makes it
different from the other runners is that Kafka Streams is a library, not a
cluster: there is no job manager and no resource manager to operate. A
pipeline is an ordinary JVM process that reads from and writes to Kafka,
and you scale it by starting more copies of that process. State, fault
tolerance and exactly-once come from Kafka itself, through consumer groups,
changelog topics and transactions.

Current state. It is a skeleton. It runs a real subset of the model:
bounded and unbounded reads, stateless ParDo including multiple outputs,
GroupByKey and Combine, global, fixed and sliding windows with the default
trigger and allowed lateness, Flatten, Redistribute, metrics, and
exactly-once via Kafka transactions. That subset is covered by 105 unit
tests, 59 of Beam's own @ValidatesRunner tests, and integration tests
against a real broker. Beam's Python portable suite also runs against it,
so it is exercised from a non-Java SDK as well.

It also has real gaps, each tracked: side inputs (#39628
<https://github.com/apache/beam/issues/39628>), stateful ParDo and user
timers (#39629 <https://github.com/apache/beam/issues/39629>), merging
windows and custom WindowFns (#39630
<https://github.com/apache/beam/issues/39630>), splittable DoFn (#39631
<https://github.com/apache/beam/issues/39631>), TestStream (#39632
<https://github.com/apache/beam/issues/39632>), and reading a source in
parallel (#39626 <https://github.com/apache/beam/issues/39626>).

And it has at least one known bug rather than a missing feature: bundles
are not closed after a bounded time (#39633
<https://github.com/apache/beam/issues/39633>). The maxBundleTimeMs option
is accepted and has no effect, because closing a bundle from a wall-clock
punctuator duplicated output against a real broker and the cause is not yet
understood. There may be others we have not found.

Why I propose merging now. The motivation was to build a skeleton that can
be developed further by several contributors, and master is where that can
happen: the runner can be built, run and worked on by anyone interested,
and the gaps above are well-defined pieces of work someone could pick up.

To make that safe, the runner is not part of the standard build. Its
subprojects are only included when -Pwith-kafka-streams-runner is passed:

    ./gradlew -Pwith-kafka-streams-runner :runners:kafka-streams:build

Without the flag they are not in the build at all, so nothing reaches users
who have not asked for it, and no release artifact contains it. It is built
in the Java precommit, so it cannot rot unnoticed. If the runner becomes
stable enough the flag comes off and it is built like any other runner; if
it does not, it can be dropped again without affecting anyone, because no
release ever shipped it.

What is the potential of the runner. Two things, one operational and one
about recovery.

The operational one is that there is nothing to operate. If you already run
Kafka, a Beam pipeline becomes an ordinary application you deploy like any
other — no job manager, no resource manager, no second distributed system
to size, upgrade and keep alive. Scaling up or down is starting or stopping
a process, and the consumer group redistributes the work.

The recovery one is that Kafka Streams reassigns partitions and restores
state from a changelog, where a checkpoint-based engine restarts a job from
its last checkpoint. On a Mac, with one broker and two instances, killing
the instance holding the source read with kill -9 and timing until the
other took the work over:

    session.timeout.ms = 6000    ->  8.6, 8.7, 9.1, 10.0 s
    session.timeout.ms = 45000   ->  55.7, 55.8 s  (Kafka's default)

Handover is dominated by how long the consumer group takes to notice, which
is session.timeout.ms and is configurable; the recovery work itself is the
remainder. These are laptop numbers meant to show the shape of the thing,
not a benchmark against other runners.

This lazy consensus request will be open for at least 72 hours. If there
are no objections by then the consensus will pass. Any comments or
objections are welcome, here or on the pull request.

Thanks,
Junaid Shaukat
https://github.com/junaiddshaukat

[1] https://github.com/apache/beam/issues/18479
[2] https://github.com/apache/beam/pull/39785

Reply via email to